今天我们的目标是学习进程间的几个关系:它们分别是进程组/作业/会话。
首先我们谈论第一个话题----进程组。我们先来了解一下它的概念,什么是进程组呢?显而,进程组就是一个或多个进程组成的集合。每个进程除了有一个进程ID之外,还属于一个进程组。
进程组(Process Group)
进程组就是一个或多个进程组成的集合。每个进程除了有一个进程ID之外,还属于一个进程组。通常,它们与同一个作业相关联,可以接受来自同一终端的各种信号。每个进程组有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程标识是,其进程组ID等于其进程ID。组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中一个进程存在,则该组就存在,这与其组长进程是否终止无关。
我们看一个例子:
其中 “&”表示将进程组放在后台运行
进程: 4088 4089 4090
组长: 4088 进程组中的第一个进程
kill -9 杀掉组长,进程组还在
ps选项:
a:不仅列出当前用户的进程,也列出所有其他用户的进程
x:表示不仅列有控制终端的进程,也列出所有无终端的进程
j:表示列出与作业控制相关的信息
作业(Jobs)
shell分前后台来控制的,不是进程,而是作业或者进程组,一个前台作业可以有多个进程组成,一个后台作业可以由多个进程组成,shell可以运行一个前台作业和多个后台作业,这成为作业控制。
作业在后台的时候不允许从前台读取数据,一旦试图读取数据,则该进程被终止。
作业和进程组的区别:
如果作业中的某个进程创建了子进程,那么这个子进程是不属于作业的,一旦作业运行结束,shell就把自己提到前台,如果前台作业还存在,那么它将自动变成后台进程组,在前台新起的作业,shell是无法运行的,因为它被提到了后台,如果前台进程退出,shell就又提到了前台,所以可以继续接收用户的输入。
现在我们来看一个例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
if(fork() == 0){
while(1){
printf("i am child:%d\n",getpid());
sleep(1);
}
}else{
int i = 5;
while(i>0){
printf("i am father:%d,i will dead:%d\n",getppid(),i);
i--;
sleep(1);
}
}
return 0;
}
我们发现,程序运行了以后,在前台创建了一个作业,包含了父进程和子进程,5s之内shell无法接受任何命令,说明此时的前台作业不是shell。但是我们发现当父进程退出后,子进程还在运行,但是此时我们输入命令,shell可以处理,说明变成了前台作业。换句话说,我们刚建的作业退出了,但是子进程还在,那么他就会被动提到后台。我们可以发现,子进程所属的组还在,但是父进程已经退出了。
会话(Session)
会话是指一个或者多个进程组集合,一个会话可以有一个控制终端,这通常是登录到其上的终端设备或者是伪终端设备,建立与控制会话的首进程,被称之为控制进程(会话首进程),一个会话中的几个进程组可被分为一个前台进程和任意多个后台进程,所以会话中包括控制进程,一个前台进程,和任意多个后台进程,和新打开的一个终端。
我们发现SID是会话ID:4142三个进程都属于同一个进程组,同一个会话,它们的父进程ID也相同,现在我们来查看一个他们的父进程是谁?
我们可以看得出来父进程就是bash,也就是会话首进程,而且三个进程的父进程都是bash。
作业控制
Session与进程组“shell可以同时运行一个前台进程和任意多个后台进程”,其实这是不全面的,现在我们来研究一下复杂的情况。事实上,Shell分前台和后台开控制的不是进程而是作业(Jobs)或者进程组(Process Group),一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程古组成,Shell可以同时运行一个前台作业和任意多个后台作业,这就成为作业控制。现在我们来看个例子:
Ctrl-C杀掉的不是进程,而是整个作业。
现在我们来看一下作业控制有关的信号
作业控制有关的信号
我们通过实验来理解与作业控制有关的信号
在cat指令的后面加上“&”,表示让指令在后台运行,但是cat指令需要从中断读取数据,此时内核发送SIGTTIN信号给cat,该信号的默认处理动作是使进程停止。
指令:jobs,可以查看当前有哪些作业,fg命令可以将某个命令提到前台来进行,如果该作业的进程组在后台进行,则提到前台运行,如果作业处于停止,则发送SIGCONT使其继续运行,如果输入:ctrl+z则向所有前台进程发送SIGTSTP信号,该信号默认使得所有前台进程停止,cat继续以后台作业的形式存在。
bg命令可以让某个停止的作业在后台继续运行,也需要给改该作业的每个进程组发SIGCONT信号。cat进程继续运行,又要读中断输入,然而它在后台不能读端输入,所以又收到SIGTTIN信号而停止。
以上过程是:先把cat放在后台运行,此时cat被stop,然后向cat发送SIGTERM(15)信号,默认进程终止,但是此时cat已经被stop了,所以此时不会立即的处理信号,得等到我们把cat运行起来的时候,再去执行这个信号,此时我们使用fg 1命令,代表将第一个后台作业提到前台来。
kill 命令给一个停止的进程发SIGTERM(15)信号,这个信号不会立马被处理,而要等进程准备继续运行时处理,默认动作是终止进程。但是如果给一个停止的进程发SIGKILL信号就不同了。