僵死进程:在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍然占用的资源)的进程成为 僵尸进程。
孤儿进程:如果父进程先退出,子进程还没有完成,那么此时子进程就会被托付给 init进程,此时该子进程的父进程就是 init进程(1号进程)。
守护进程:一般是指 Linux中的后台服务进程,它是一个生存周期较长的进程,如其父进程为init进程,通常独立于控制终端,即 不接受电脑用户的直接操作。守护进程是一个特殊的孤儿进程。
会话(session): 会话是一个或多个进程组的集合。通常一个会话开始于 用户登录,终止于用户退出,在此期间用户运行的所有进程都属于会话
进程组:每个进程除了有一个进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合。每个进程组有一个组长进程,组长进程的进程ID等于进程组ID。
NAME
setsid - creates a session and sets the process group ID
创建会话,并设置进程组的ID
SYNOPSIS
#include <unistd.h>
pid_t setsid(void);
如果调用setsid()的进程不是一个进程组的组长进程,则 创建一个会话。即父进程不能调用setsid()创建会话,只能由子进程调用。调用setsid()的进程会成为 会话组长,以及 会成为当前新的进程组的组长。并且脱离控制终端。
setsid()用于创建一个新的会话,并有以下三个作用:
1 让进程摆脱原会话的控制
2 让进程摆脱原进程组的控制
3 让进程摆脱原控制终端的控制
守护进程:
1 后台服务程序
2 脱离控制终端,用户无法通过终端与守护进程交互,TTY == ?
3 长期,周期性的执行某任务,即父进程为init进程,即1号进程
4 不受用户登录,注销的影响 即不受当前会话控制
那么,在创建守护进程时为什么要调用setsid函数呢?由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。由于在调用了fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。
一个进程一定会父进程,但是守护进程有点不一样,一般子进程调用之后,按照之前的逻辑,子进程运行的时候,父进程会一直等待子进程运行完成 并释放资源,即 wait()收尸,但是守护进程就没有必要 wait()了,因为它运行的时间会很长,父进程以及父进程的父进程等等就没必要一直等待了,所以直接托付给init进程就好。
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ps axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
父进程ID : PPID
进程ID : PID
组进程ID : PGID
会话ID : SID
实验:守护进程的创建
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <fcntl.h>
#define FILENAME "/tmp/out"
static int craetdeamon(void)
{
pid_t pid;
int fd;
pid = fork();
if(pid < 0)
{
perror("fork()");
return -1;
}
if(pid > 0) //父进程结束
{
printf("%d\n",getpid());
exit(0);
}
fd = open("/dev/null",O_RDWR);
if(fd < 0)
{
perror("open()");
return -1;
}
//重定向 0 1 2 文件描述符
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
if(fd > 2)
{
close(fd);
}
//创建守护进程
setsid();
chdir("/");
return 0;
}
int main(int argc,char* argv[])
{
FILE* fp;
int i;
if(craetdeamon())
{
exit(1);
}
fp = fopen(FILENAME,"w");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
for(i = 0; ;i++)
{
fprintf(fp,"%d\n",i);
fflush(fp);
sleep(1);
}
exit(0);
}
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ps axj
...
1662 3141 3141 3141 ? -1 Ss 1000 0:00 ./a.out
2959 3188 3188 2959 pts/2 3188 R+ 1000 0:00 ps axj
父进程是 1662 并不是init进程
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ps 1662
PID TTY STAT TIME COMMAND
1662 ? Ss 0:00 /sbin/upstart --user
mhr@ubuntu:~/Desktop/xitongbiancheng/test$
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ tail -f /tmp/out
1693
1694
1695
1696
1697
1698
1699
1700
1701
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ kill 3141
最后记得杀死 守护进程
发现此时守护进程的父进程 并不是init进程,后面得知 原来较新 ubuntu 使用 upstart进程 来替代的 init进程 收养孤儿进程。
之所以来重定向 0 1 2 三个文件描述符:
用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭或重定向。
使用fork创建的子进程会继承父进程的工作目录,如果守护进程踩在某个设备上运行,而当设备卸载时,会出现异常,device is busy。通常做法是让让 根目录(“/”)作为守护进程的当前工作目录。可以避免上述问题。
chdir("/");