守护进程
进程组是一组进程的集合,进程组由进程组PID表示
每个进程除了进程id之外,还必须有一个进程组id,即必须属于某个进程组
每个进程组都有一个组长,其进程id就作为进程组id,它不受进程组长的退出影响。
一般来说,一个终端一般是进程组长。
会话期是由一个或多个进程组组成的集合
一个会话期开始于用户登录,结束于用户退出。在此期间用户运行的的所有进程都属于这个会话期。
创建守护进程基本步骤
1.创建子进程
在终端输入命令执行用户程序时,该进程成为父进程,它此时受控制终端即创建该进程的控制终端控制,通过它创建子进程
让子进程脱离该终端的控制:子进程执行守护进程需要执行的代码,然后让父进程退出,此时子进程的父进程变为了系统的Init进程。
当父进程先于子进程退出,子进程成为孤儿进程。
在linux系统中,每当系统发现一个孤儿进程后,就会自动由1号进程(init进程) 收养它。
每个进程在创建时都有其自身的进程组、会话期及所属的控制终端,当创建子进程时,子进程复制父进程的这些属性,因此父子进程属于同一个进程组、会话期及控制终端
2.让子进程脱离该终端的控制。当执行setsid()系统调用,使其脱离原终端控制。它创建一个新的会话并让调用它的进程成为该会话的组长,主要的3个作用
2.1 让进程摆脱原会话的控制
2.2 让进程摆脱原进程组的控制
2.3 让进程摆脱原控制终端的控制
setsid()系统调用
3.改变当前目录为根目录。
每个进程在执行时,都保存当前执行目录的信息。子进程也继承父进程的该信息
由于在进程运行过程中,当前目录所在的文件系统是不能卸载的,
这对以后的使用会造成诸多的麻烦(如用户要对不使用的目录进行卸载操作,由于该目录还在被守护进程使用,所以umount出错,导致该目录不能被卸载)
通常的做法是让系统根目录"/" 作为守护进程的的当前工作目录,会避免上述不必要的问题。当然,如果有特殊需要也可以把当前工作目录换成其它的路劲
4.修改文件权限掩码
文件掩码是指屏蔽掉文件权限中的对应位。比如,一个文件的权限掩码是050,其对应的二进制码为000101000 表示屏蔽文件用户组用户读和执行的权限
由于新建子进程继承父进程的文件权限掩码,这就给该子进程操作文件带来诸多麻烦,因此,把文件权限掩码设置成0,即取消子进程对文件操作的限制,
从而大大增强该守护进程的灵活性,即调用umask(0)
5.关闭文件描述符
同文件掩码一样,新建的子进程从父进程继承已经打开的文件描述符。这些被打开的文件描述符可能永远不会被守护进程读写,但它们同样耗费系统资源,
而且可能导致文件系统无法卸载。既然守护进程与控制终端失去了联系,因此与控制终端相关的文件描述符就不会被使用,如:文件描述符为 0,1,2(输入,输出和出错)
等文件描述符就不会被使用,失去存在的意义,要关闭它们,可使用close(fd)方法
示例
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <time.h> #define MAXFILE 65535 char *timestr(); int main(){ pid_t pc; char wtr[100]; int i,fd,len; char *buf ="hello everybody!"; len=strlen(buf); pc=fork(); if(pc<0){ printf("fork err "); exit(1); } if(pc>0) exit(0); setsid(); chdir("/"); umask(0); for(i=0;i<MAXFILE;i++){ while(1){ if(fd= open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600)<0){ perror("open"); exit(1); } sprintf(wtr, "%s-%s ",buf,timestr()); write(fd,wtr,strlen(wtr)+1); close(fd); sleep(2); } } } char *timestr(){ time_t timep; static char timestr[50]; struct tm *p; time(&timep); p = gmtime(&timep); sprintf(timestr,"%d:%d:%d", p->tm_hour, p->tm_min, p->tm_sec); return timestr; }
运行后
[root@centos1 c]# ./daemon hello everybody!-11:39:41 [root@centos1 c]# ^C [root@centos1 c]# tail -f /tmp/daemon.log hello everybody!-11:39:43 hello everybody!-11:39:45 hello everybody!-11:39:47 hello everybody!-11:39:49 hello everybody!-11:39:51 hello everybody!-11:39:53 hello everybody!-11:39:55
目标 |
创建新会话 |
头文件 |
#include <unistd.h>;#include <sys/types.h> |
函数原型 |
pid_t pid=setsid(void) |
返回值 |
出错,返回-1 成功,返回该进程组的pid |