守护进程
守护进程的概念:长时间在后台执行,不与用户交互,但是能提供某种服务的进程。一般守护进程的命名以 "d"来结尾,说明这是个守护进程,例如 :httpd要实现守护进程,首先需要了解下面4个概念:1. 会话: 可以认为在Linux中每打开一个终端就与系统建立一个会话2. 会话首进程: 在会话建立的第一个进程,一般来说是终端中的...
守护进程的概念:
长时间在后台执行,不与用户交互,但是能提供某种服务的进程。
一般守护进程的命名以 "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);
}
更多推荐
所有评论(0)