标签: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