1、引言
之前学习进程方面的东西,在处理孤儿进程时,我们有两种方法,一是父进程调用wait得到子进程的退出码,而是将孤儿进程挂载到守护进程(精灵进程)上。这是第一次接触到守护进程这个概念,按照自己的理解,它应该是一种后台进程,用来管理事务活动。在经过学习后,对守护进程又有了新的认识。
2、概念
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。
3、特点
- 守护进程没有控制终端,不能和用户进行交互;
- 其他进程一般都是在用户登录或加载程序时创建的,用户注销或者程序加载完之后进程也随之结束,而守护进程是后台进程,不受用户注销的影响,只受开机关机的影响。
4、守护进程与后台进程的区别?
- 守护进程是后台进程,后台进程不一定是守护进程;
- 守护进程运行是与终端无关的,是不能往终端上打消息的;
- 守护进程的会话组和当前目录,文件描述符都是独立的。后台运行只是终端进行了一次fork,让程序在后台执行;
5、创建守护进程的步骤:
(1)调用umask将文件模式创建屏蔽字设置为0
由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了很多的麻烦(比如父进程中的文件没有执行文件的权限,然而在子进程中希望执行相应的文件这个时候就会出问题)。因此在子进程中要把文件的权限掩码设置成为0,即在此时有最大的权限,这样可以大大增强该守护进程的灵活性。
(2)父进程fork出子进程,然后子进程调用setsid,父进程直接退出(保证了子进程是一个进程组的组长),
(3)调用setsid创建一个新的会话(调用成功会使调用进程成为新会话的首进程,并且成为一个进程组的组长进程,调用进程没有控制终端)
(4)将当前工作目录更改为根目录
使用fork()创建的子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。因此通常的做法是让“/”作为守护进程的当前目录。
(5)关闭不需要的文件描述符
fork()函数创建的子进程会从父进程那里继承一些已经打开了的文件。这些文件被打开的文件可能永远不会被守护进程读写,如果不进行关闭的话将会浪费系统的资源。
(6)守护进程退出
6、模拟实现守护进程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0);\
void creat_daemon(int nochdir, int noclose);
int main(void)
{
time_t t;
int fd;
creat_daemon(0,0);
while(1){
fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
if(fd == -1)
ERR_EXIT("open error");
t = time(0);
char *buf = asctime(localtime(&t));
write(fd,buf,strlen(buf));
close(fd);
sleep(60);
}
return 0;
}
void creat_daemon(int nochdir, int noclose)
{
pid_t pid;
pid = fork();
if( pid == -1)
ERR_EXIT("fork error");
if(pid > 0 )
exit(EXIT_SUCCESS);
if(setsid() == -1)
ERR_EXIT("SETSID ERROR");
if(nochdir == 0)
chdir("/");
if(noclose == 0){
int i;
for( i = 0; i < 3; ++i)
{
close(i);
open("/dev/null", O_RDWR);
dup(0);
dup(0);
}
umask(0);
return;
}