标签:linux内核
大体步骤如下
1. 调用fork()函数创建子进程后,让父进程立即exit(),这样产生的子进程变成孤儿进程,由init进程接管。
2. 调用setsid()函数,使得新创建的进程脱离控制终端,同时创建新的进程组,并成为该进程组的首进程。在linux系统中,所有的进程都属于各自的进程组,进程组是一个或多个进程的集合,一个进程组中至少有一个进程成员,否则就消亡了。每个进程组都有一个进程组ID,是由领头进程的进程号决定的,会话则是一个或多个进程组的集合,每个会话都有一个领头进程,会话和进程组是linux内核用于管理多用户情况下用户进程的方法,每个进程都属于一个进程组,而进程组又属于某个会话,当用户从终端登录系统时,系统会创建一个新的会话,在该终端上启动的进程都会被系统划归到会话的进程组中。会话中的进程通过该会话中的领头进程与一个终端相连,该终端是会话的控制终端,一个会话只能有一个控制终端,如果会话存在一个控制终端时,则它必然拥有一个前台进程组,属于该组的进程可以从从控制终端获得输入。由于守护进程没有控制终端,而使用fork()函数创建的子进程会继承父进程的控制终端,会话和进程组,因此,必须用setsid()创建新的会话,以脱离父进程的影响。Setsid函数将创建新的会话,并使得调用setsid函数的进程成为新会话的领头进程。调用setsid函数的进程是新创建会话中的唯一的进程组,进程组ID为调用进程的进程号。Setsid函数产生这一结果还有个条件,即调用进程不为一个进程的领头进程。由于在第一步调用fork的父进程退出,使得子进程不可能是进程组的领头进程。该会话的领头进程没有控制终端与其相连,至此,满足了守护进程没有控制终端的要求。
3. 更改当前工作目录
使用fork()函数创建的子进程会继承父进程的当前工作目录,当进程工作没有结束时,其工作目录是不能被卸载的.为了防止此问题,守护进程一般要chdir()了数将其工作目录更改到别的目录下(一般为/根目录,因为根目录是永远不会被卸载的,除非关机).
4. 关闭文件描述符,并重定向标准输入,输出和错误
子进程会继承父进程某些打开了的文件描述符,如果不使用这些文件描述符,则需要关闭它们.守护进程是运行在系统后台的,不应该在终端有任何的输出信息,可以使用dup()函数将其重定向到/dev/null空设备上.
5. 设置守护进程的文件权限创建掩码
守护进程会创建一些临时文件,出于性的考虑,往往不希望这些文件被别的用户查看,这时可以用umask()函数修改文件权限,创建掩码的取值.
上面说的是创建守护进程的大体步骤,解释其中的两点:1、setsid的作用
一、让进程摆脱原会话的控制
如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。
二、第一个子进程调用setsid()函数,调用以后第一个子进程才成为了新的进程组的进程组长(注意,调用setsid()函数的第一个子进程在调用这个函数之前不是进程组的进程组长)第一子进程成为新的会话组长和进程组长,进程组长有权利申请打开一个控制终端,第二次fork的意义就在此,关闭掉第一子进程,使第二子进程成为守护进程,并且因为没有进程组长, 所以守护进程不会被关闭。#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include <syslog.h> #include <signal.h> #include <sys/param.h> #include <fcntl.h> int init_daemon(void) { int pid; int i; int fd; /*忽略终端I/O信号,STOP信号*/ signal (SIGTTOU, SIG_IGN); signal (SIGTTIN, SIG_IGN); signal (SIGTSTP, SIG_IGN); signal (SIGHUP, SIG_IGN); printf(“ppid = %d\n”,getppid()); pid = fork(); fd = open(“/dev/tty”,O_RDONLY); printf(“!fd = %d\n”,fd); close(fd); if (pid > 0) { printf(“Parent process pid = %d\n”,getpid()); exit(0); //结束父进程,使得子进程成为后台进程 } else if (pid < 0) return -1; printf(“First Child process pid = %d\n”,getpid()); //当前进程为第一子进程 //建立一个新的进程组,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程脱离所有终端 printf(“pgid = %d\n”,getpgid(getpid())); //printf(“pid = %d\n”,getpid()); setsid(); printf(“pgid = %d\n”,getpgid(getpid())); //printf(“pid = %d\n”,getpid()); fd = open(“/dev/tty”,O_RDONLY); printf(“fd = %d\n”,fd); close(fd); //再次新建一个子进程,退出父进程(第一子进程),保证该进程不是进程组长,同时让该进程无法再打开一个新的终端 pid = fork(); fd = open(“/dev/tty”,O_RDONLY); printf(“#fd = %d\n”,fd); close(fd); if (pid > 0) { printf(“First Child process pid = %d\n”,getpid()); exit(0); } else if (pid < 0) return -1; //关闭所有从父进程继承的不再需要的文件描述符 for (i=0; i < NOFILE; close(i++)); //改变工作目录,使得进程不与任何文件系统联系 chdir(“/”); //将文件屏蔽字设置为0 umask(0); //忽略SIGCHLD信号 signal(SIGCHLD,SIG_IGN); return 0; } int main(int argc, char *argv[]) { time_t now; init_daemon(); syslog(LOG_USER|LOG_INFO,”测试守护进程!\n”); while(1) { sleep(8); time(&now); syslog(LOG_USER|LOG_INFO,”系统时间:\t%s\t\t\n”,ctime(&now)); } }
标签:linux内核
原文地址:http://blog.csdn.net/yusiguyuan/article/details/39048123