守护进程的概念:

        长时间在后台执行,不与用户交互,但是能提供某种服务的进程。

一般守护进程的命名以 "d"来结尾,说明这是个守护进程,例如 :   httpd

 

要实现守护进程,首先需要了解下面4个概念:

       1. 会话: 可以认为在Linux中每打开一个终端就与系统建立一个会话

       2. 会话首进程: 在会话建立的第一个进程,一般来说是终端中的  bash这个进程,通过ps命令可以看到

       3. 进程组: 在终端中运行一个进程,例如 sleep 30  和  sleep 3 它们俩分别为一个进程组,但是如果程序中有fork()则进程组 中就不只一个进程 

       4. 组长进程: 进程组中的组长,进程组id 等于组长进程的pid

上述4者的关系如下:

      

实现守护进程的编程流程:

1.  先fork(),然后父进程退出,这样做实现了以下几点:第一,如果该守护进程是作为一个简单的shell命令启动的,那么终端关闭,则该进程就会结束,这样不符合守护进程能在后台不断运行的特点;第二,子进程继承了父进程的进程组id,但具有了一个新的进程id, 这保证了子进程不是一个进程组的组长进程,这个是下面调用setsid()的必要前提条件。

2.  setsid()创建新会话,使调用进程(a)成为新会话的首进程, (b)成为一个新进程组的组长进程,(c)没有控制终端

3.  fork(),然后退出父进程,这一步是保证fork()得到的子进程失去 会话首进程 和 组长进程 的身份,但是属于可做可不做的操作

4.  chdir("/"),改变工作目录为根目录,因为从父进程继承过来的工作目录可能在一个装配文件系统下,因为守护进程通常在系统的再引导下是一直存在的,所以如果守护进程的当前工作在一个装配文件系统中,那么该文件系统就不能被拆卸,这与装配文件系统的意愿不符合。

5.  unask(0), 将文件模式创建屏蔽字设置为0,由继承得来的文件模式创建屏蔽字可能会拒绝设置某些权限。例如,若守护进程要创建一个组可读可写的文件,而继承来的文件模式创建屏蔽字可能屏蔽了这两种权限,于是守护进程所要求的组可读可写就不起作用

6.  close(),关闭文件描述符,这使守护进程不在持有从父进程继承来的文件描述符。可以通过getdtablesize()函数来获得全部描述符的个数。

 

如下,按照编程流程实现了一个守护进程,使其间隔3秒就往日志文件(/tmp/SHJCd.log)中写入当前的时间:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<time.h>

int main()
{
    int pid = fork(); //1 步
    if(pid != 0)
    {
	exit(0);
    }

    setsid(); //2 步

    pid = fork(); //3 步
    if(pid != 0)
    {
	exit(0);
    }

    chdir("/"); //4 步
    umask(0); //5 步

    int i=0;
    int maxfd = getdtablesize();
    for(i;i<maxfd;i++)
    {
		close(i);  //6 步
    }

    
    while(1) //每间隔3秒向 /tmp/SHJCd.log 文件中写入当前时间
    {
		FILE* fp = fopen("/tmp/SHJCd.log","a");
		if(fp == NULL)
			exit(0);

		time_t tv;
		time(&tv);
		fprintf(fp,"time is %s",asctime(localtime(&tv)));
		fclose(fp);
		sleep(3);
    }
    exit(0);
}

我们可以通过     cat  和   tail  -f  来查看日志文件,建议用 tail  -f  因为这个可以动态显示最新的日志文件。

 

守护进程 与  僵死进程:

       僵死进程的的特点是:先于父进程结束,而父进程没有wait()子进程的退出码。

       因为守护进程长时间运行的特点,如果守护进程在运行过程中 fork() 了自己的子进程,那么子进程就可能会成为僵死进程!僵死进程的出现对系统来说是不利的,因为它会耗费一定量的系统资源,数量多了以后就可能让系统崩溃。

处理办法:

在程序的前面加上 signal(SIGCHLD,SIG_IGN)  来将子进程的终止信号设置为忽略的处理方式,这样子进程退出就会交给内核处理,从而避免了僵死进程的产生。

代码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<time.h>

int main()
{
    int pid = fork(); //1 步

    signal(SIGCHLD,SIG_IGN);  //这一步很重要!!忽略方式响应子进程结束的信号

    if(pid != 0)
    {
	exit(0);
    }

    setsid(); //2 步

    pid = fork(); //3 步
    if(pid != 0)
    {
	exit(0);
    }

    chdir("/"); //4 步
    umask(0); //5 步

    int i=0;
    int maxfd = getdtablesize();
    for(i;i<maxfd;i++)
    {
		close(i);  //6 步
    }

    exit(0);
}

 

Logo

更多推荐