精灵进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时起动,在系统
关闭时终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX系统有很多精灵进
程,它们执行日常事物活动。
关闭时终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX系统有很多精灵进
程,它们执行日常事物活动。
我们在编写程序时往往要用到精灵进程,下面介绍精灵进程的编程规则,并给出示例。
1:调用fork产生一个子进程,同时父进程退出。我们所有后续工作都在子进程中完成。这样做我们可以:如果我们是从命令行执行的该程序,这可以造成程序执行完毕的假象,shell会回去等待下一条命令;刚刚通过fork产生的新进程一定不会是一个进程组的组长,这为第2步的执行提供了前提保障。
这样做还会出现一种很有趣的现象:由于父进程已经先于子进程退出,会造成子进程没有父进程,变成一个孤儿进程(orphan)。每当系统发现一个孤儿进程,就会自动由1号进程收养它,这样,原先的子进程就会变成1号进程的子进程。
2:调用setsid系统调用。这是整个过程中最重要的一步。setsid的介绍见附录2,它的作用是创建一个新的会话(session),并自任该会话的组长(session leader)。如果调用进程是一个进程组的组长,调用就会失败,但这已经在第1步得到了保证。调用setsid有3个作用:
让进程摆脱原会话的控制;
让进程摆脱原进程组的控制;
让进程摆脱原控制终端的控制;
让进程摆脱原会话的控制;
让进程摆脱原进程组的控制;
让进程摆脱原控制终端的控制;
总之,就是让调用进程完全独立出来,脱离所有其他进程的控制。
3:把当前工作目录切换到根目录。如果我们是在一个临时加载的文件系统上执行这个进程的,比如:/mnt/floppy/,该进程的当前工作目录就会是/mnt/floppy/。在整个进程运行期间该文件系统都无法被卸下(umount),而无论我们是否在使用这个文件系统,这会给我们带来很多不便。解决的方法是使用chdir系统调用把当前工作目录变为根目录,应该不会有人想把根目录卸下吧。关于chdir的用法,参见附录1。
当然,在这一步里,如果有特殊的需要,我们也可以把当前工作目录换成其他的路径,比如/tmp。
当然,在这一步里,如果有特殊的需要,我们也可以把当前工作目录换成其他的路径,比如/tmp。
4:将文件权限掩码设为0。这需要调用系统调用umask,参见附录3。每个进程都会从父进程那里继承一个文件权限掩码,当创建新文件时,这个掩码被用于设定文件的默认访问权限,屏蔽掉某些权限,如一般用户的写权限。当另一个进程用exec调用我们编写的daemon程序时,由于我们不知道那个进程的文件权限掩码是什么,这样在我们创建新文件时,就会带来一些麻烦。所以,我们应该重新设置文件权限掩码,我们可以设成任何我们想要的值,但一般情况下,大家都把它设为0,这样,它就不会屏蔽用户的任何操作。
5:如果你的应用程序根本就不涉及创建新文件或是文件访问权限的设定,你也完全可以把文件权限掩码一脚踢开,跳过这一步。关闭所有不需要的文件。同文件权限掩码一样,我们的新进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不被我们的daemon进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。需要指出的是,文件描述符为0、1和2的三个文件(文件描述符的概念将在下一章介绍),也就是我们常说的输入、输出和报错这三个文件也需要被关闭。很可能不少读者会对此感到奇怪,难道我们不需要输入输出吗?但事实是,在上面的第2步后,我们的daemon进程已经与所属的控制终端失去了联系,我们从终端输入的字符不可能达到daemon进程,daemon进程用常规的方法(如printf)输出的字符也不可能在我们的终端上显示出来。所以这三个文件已经失去了存在的价值,也应该被关闭。
5:如果你的应用程序根本就不涉及创建新文件或是文件访问权限的设定,你也完全可以把文件权限掩码一脚踢开,跳过这一步。关闭所有不需要的文件。同文件权限掩码一样,我们的新进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不被我们的daemon进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。需要指出的是,文件描述符为0、1和2的三个文件(文件描述符的概念将在下一章介绍),也就是我们常说的输入、输出和报错这三个文件也需要被关闭。很可能不少读者会对此感到奇怪,难道我们不需要输入输出吗?但事实是,在上面的第2步后,我们的daemon进程已经与所属的控制终端失去了联系,我们从终端输入的字符不可能达到daemon进程,daemon进程用常规的方法(如printf)输出的字符也不可能在我们的终端上显示出来。所以这三个文件已经失去了存在的价值,也应该被关闭。
我们可以用ps -ajx命令来观察daemon进程的状态和一些参数。
下面第一个是 Unix高级编程 书中的例子,第二个是可以执行的。
/* 一个daemon程序 */
#include<unistd.h>
#include<sys/types.h>
#include <sys/stat.h>
#include<unistd.h>
#include<sys/types.h>
#include <sys/stat.h>
#define MAXFILE 65535
main()
{
pid_t pid;
int i, j = 0;
pid=fork();
if(pid<0)
{
printf("error in fork\n");
exit(1);
}else if(pid>0)
/* 父进程退出 */
exit(0);
/* 调用setsid */
setsid();
/* 切换当前目录 */
chdir("/");
/* 设置文件权限掩码 */
umask(0);
/* 关闭所有可能打开的不需要的文件 */
for(i=0;i<MAXFILE;i++)
close(i);
/*
*到现在为止,进程已经成为一个完全的daemon进程,
*你可以在这里添加任何你要daemon做的事情,如:
*/
for(j = 0; j < 5; j++)
sleep(10);
}
{
pid_t pid;
int i, j = 0;
pid=fork();
if(pid<0)
{
printf("error in fork\n");
exit(1);
}else if(pid>0)
/* 父进程退出 */
exit(0);
/* 调用setsid */
setsid();
/* 切换当前目录 */
chdir("/");
/* 设置文件权限掩码 */
umask(0);
/* 关闭所有可能打开的不需要的文件 */
for(i=0;i<MAXFILE;i++)
close(i);
/*
*到现在为止,进程已经成为一个完全的daemon进程,
*你可以在这里添加任何你要daemon做的事情,如:
*/
for(j = 0; j < 5; j++)
sleep(10);
}
--------------------------------------------------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "outhdr.h"
#include <sys/stat.h>
#include <fcntl.h>
#include "outhdr.h"
int
daemon_init(void)
{
pid_t pid;
daemon_init(void)
{
pid_t pid;
if ((pid = fork()) < 0)
return(-1);
else if (pid != 0)
exit(0); /* parent goes bye-bye */
return(-1);
else if (pid != 0)
exit(0); /* parent goes bye-bye */
/* child continues */
setsid(); /* become session leader */
setsid(); /* become session leader */
chdir("/"); /* change working directory */
umask(0);
return(0);
}
}