进程之间的关系不仅仅是简单的父子关系,为了描述Linux上复杂的进程关系,Linux引入了进程组和会话的概念,我们今天就来讲讲这两个概念。
下图表示了进程,进程组,会话的关系
Session
一个进程可以通过调用pid_t setsid(void),来创建一个新的session,关于这个函数的使用有几个注意事项:
--创建session的进程不能是一个进程组的leader,在编程中我们可以先fork一个子进程然后退出父进程,那么我们就保证了子进程肯定不是进程组组长,它可以调用setsid来创建一个新session啦;
--调用setsid的进程成为session 的 leader
--调用setsid的进程会成为一个新进程组的 leader
--调用setsid的进程失去了控制终端连接
进程组
再来看看进程组。顾名思义,进程组就是把多个进程放进一个组组里统一管理,例如当我们运行一下命令的时候:
ls | grep -i hello | more
这包括三个进程,这三个进程就组成了一个进程组,在编程的时候我们可以通过调用系统调用
int sepgrp(pid_t pid, pid_t pgid)来将pid指定的进程那加入到pgid指定的进程组中,如果pid为0,则表示当前进程,如果pgid为0,则表示我们把pid加入到一个新的进程组中,理所当然地,pid则成为这个进程组的leader,也就是说,如果一个进程的pid与组gid相同,则表示这个进程是当前进程组的leader。那么进程组leader拥有那些特征和特权呢?
--进程组leader的pid==gid
--leader可以创建另一个进程组,并创建其中的进程,然后退出
--父进程可以为自己或者子进程设置进程组ID,但是如果子进程调用了exec函数,则父进程也不能改变子进程的进程组啦
守护进程
有了上面的概念,我们接下来看看守护进程
在Linux上有大量的守护进程,他们的特点如下:
1)他们是以root权限运行的
2)没有控制终端
3)后台运行
4)父进程是init
5)进程组和session的leader,而且是其中唯一的进程
那么如何来写一个守护进程呢,有一些规则在里面
1)使用umask来把文件权限的掩码设置为0,这是因为,掩码是从父进程继承来的,在守护进程中创建文件的时候,得到的权限可能跟我们预期的不一样,把掩码设置为0,也就是说我们在守护进程中创建文件的时候指定的权限就是实际的权限,没有mask处理过
2)调用fork,然后使用exit退出父进程,像之前讲过的,这样就保证了子进程不是进程组leader,可以调用setsid来创建一个新的会话
3)调用setsid来创建一个新的会话,这将使得该进程成为新会话的leader,新进程组的组长
4)把工作目录切换到根目录
5)关闭不需要的文件描述符,因为这些文件描述符是从父进程继承来的,关闭他们
6)将标准输入输出错误从定向到/dev/null
7)看系统的守护进程,一般会在/var/run目录下面创建一个XXX.pid文件,里面存储了守护进程的pid,这个文件成为 lock file(因为他会使用文件锁),使用它来保证在系统中只有一个守护进程在运行
8)有的守护进程有配置文件,一般放在/etc/xxx.conf中,守护进程启动的时候会读取这个文件,不过只会读取一次,要想重新读取就要重启守护进程,不过实际上你也可以让守护进程接受一个特定的信号(SIGHUP)来重新读取配置文件
9)由于没有控制终端,守护进程的log信息一般使用syslog来完成,syslog会把个信息输出到/var/log/messages文件中
下面是最简单的实现了一个daemon,至于上述8条规则有哪些没有涉及到的,请你添加哦。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
//clear file creation mask
umask(0);
//get maximun number of file descriptor
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
printf("cannot get file limit\n");
//become a session leader to disconnect control terminal
if ((pid = fork()) < 0)
printf("fork failed\n");
else if (pid > 0)
exit(1);
setsid();
//change the current work directory to root
chdir("/");
//close all opened file descriptor
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);
//redirect fd 0, 1, 2 to /dev/null
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(fd0);
fd2 = dup(fd0);
//initialize the log file
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected fd\n");
exit(1);
}
}
int main(int argc, char *argv[])
{
daemonize("dameon");
while (1) {
sleep(1);
syslog(LOG_NOTICE, "running\n");
}
return 0;
}