文中用到的缩写
PID = 进程ID (由内核根据延迟重用算法生成)
PPID = 父进程ID(只能由内核修改)
PGID = 进程组ID(子进程、父进程都能修改)
SID = 会话ID(进程自身可以修改,但有限制,详见下文)
TPGID= 控制终端进程组ID(由控制终端修改,用于指示当前前台进程组)
子进程与父进程
由fork创建的新进程称为子进程,调用fork函数的进程为父进程。
1,子进程可以获得父进程数据空间、堆栈的副本
2,父、子进程并不共享这些存储空间部分,只共享正文段,即只共享代码段
3,在fork之后父进程先执行还是子进程先执行是不确定的
僵尸进程与孤儿进程
僵尸进程:先于父进程终止的子进程,但是父进程没有对其进行善后处理(获取终止子进程有关信息,释放它仍占有的资源),即wait/waitpid他。消灭僵尸进程的唯一方法是终止其父进程。
孤儿进程:该进程的父进程先于自身终止。又init进程(PPID=1)接管成为新的父进程。一个孤儿进程可以自成孤儿进程组。
会话
一个或多个进程组的集合。一个登陆shell发起的会话,一般由一个会话首进程、一个前台进程组、一个后台进程组组成。
如果调用setsid函数的进程不是一个进程组组长,则该函数会创建一个新会话,否则会返回出错:
1,该进程变成新会话首进程
2,该进程成为一个新进程组的组长进程
3,该进程没有控制终端
进程组
一个或多个进程的集合,进程组属于一个会话。fork()并不改变进程组ID。
进程组组长:
PID与PGID相等的进程。组长可以改变子进程的进程组ID,使其转移到另一进程组。
例如一个shell进程(下文均以bash为例),当使用管道线时,如echo "hello" | cat,bash以第一个命令的进程ID为该管道线内所有进程设置进程组ID。此时echo和cat的进程组ID都设置成echo的进程ID。
前台进程组:
该进程组中的进程能够向终端设备进行读、写操作的进程组。
登陆shell(例如bash)通过调用tcsetpgrp()函数设置前台进程组,该函数将终端设备的fd(文件描述符)与指定进程组关联。成为前台进程组的进程其TPGID=PGID,常常可以通过比较他们来判断前后台进程组。
后台进程组:
一个会话中,除前台进程组、会话首进程以外的所有进程组。该进程组中的进程能够向终端设备写,但是当试图读终端设备时,将会收到SIGTTIN信号,并停止。登录shell可以根据设置在终端上发出一条消息[1]通知用户有进程欲求读终端。
前台进程组ID只能有一个,而后台进程组同时可存在多个。后台进程组的PGID≠TPGID。
孤儿进程组:
定义1
该组中的每个成员的父进程要么是该组的一个成员,要么不是该组所属会话的成员
定义2
不是孤儿进程组的条件是,该组中有一个进程,其父进程属于同一会话的另一个组中。
总体关系
进程属于一个进程组,进程组属于一个会话,会话可能有也可能没有控制终端
守护进程
守护进程也称精灵进程,他们常常在系统自举时启动,仅在系统关闭时才终止。没有控制终端,只在后台运行。
常见守护进程:
inetd守护进程:监听系统网络接口
cron守护进程:在指定的时期和时间执行指定命令
syslogd守护进程:把系统消息计入日志
创建守护进程:
1,调用umask将文件模式创建屏蔽字设置为0,防止拒绝了文件的读写权限
2,调用fork,生成子进程,父进程终止,子进程继承父进程的进程组ID,但不是进程组的组长进程
3,调用setsid创建新的会话,该进程成为新会话的首进程
4,调用fork生成第二个子进程,父进程终止,使其不会是会话的首进程,保证该进程不会取得控制终端
5,将当前工作目录改为根目录