进程组
每一个进程除了自己的进程ID,还有自己的进程组iD。进程组由一个或多个进程组成。通常它们与作业相关联,可以接受同一终端的各种信号。在进程组中进程ID与进程组ID相同的为进程组长
组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。
例如:
进程组长PID为2824 杀掉 进程组长进程组还在。
‘&’: 表示将进程组放在后台执行
ps选项 | |
---|---|
-a | 不仅列出当前用户的进程,还列出所有其它的用户的进程 |
-j | 列出与作业控制相关的信息 |
-x | 不仅列出于有控制终端的进程,也列出所有无控制终端的进程 |
作业
Shell分前后台控制的不是进程而是作业(job)或进程组(Process Group),一个前台作业可以由多个进程组组成,一个后台作业同样可以由多个进程组组成。
Shell可以运行一个作业和任意多后台作业。
作业与进程组的区别:
如果作业的某一个进程创建了子进程,子进程不属于作业,子进程属于进程组。
一旦作业运行结束,Shell就把自己提到前台(子进程还在,可是子进程不属于作业),如果原来前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。
例如:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
while(1)
{
printf("child(%d): hello\n", getpid());
sleep(1);
}
}
else
{
int i = 3;
while(i)
{
printf("father(%d): hi", getpid());
i--;
}
}
return 0;
}
shell前台运行一个作业,作业中的进程创建了子进程,而子进程不属于作业,当作业完成(父进程退出),shell提到前台,子进程还在打消息,并不影响shell的运行(正常使用ls命令, 使用
kill -9杀掉子进程恢复正常)。
会话
会话(session)是一个或多个进程组的集合。一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。
建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组,新打开一个终端:
其中SID就是会话ID。SID:2494 三个进程属于同一个进程组,进程组属于会话;
TTY是会话相关联的终端。
查看PPID:2521
看到是bash,也是我们的会话首进程。
每开一个终端,就打开了一次会话。
作业控制
一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)。
jobs :查看作业
fa 1(编号) : 将作业放在前台
ctrl c :杀掉整个作业
bg 2 : 将进程放在后台(需要先ctrl z先将作业暂停)。
作业控制有关信号
将cat放到后台运行,由于cat需要读标准输入(也就是终端输入),而后台进程是不能读终端输入的,因此内核发SIGTTIN信号给进程,该信号的默认处理动作是使进程停止。
cat提到前台后,打印出同样的一行,然后继续挂起等待输入。
如果输入Ctrl-Z则向所有前台进程发SIGTSTP信号,该信号的默认动作是使进程停止,cat继续以后台作业的形式存在。
bg命令可以让某个停止的作业在后台继续运行,也需要给该作业的进程组的每个进程发SIGCONT信号。cat进程继续运行,又要读终端输入,然而它在后台不能读终端输入,所以又收到SIGTTIN信号而停止。
用kill命令给一个停止的进程发SIGTERM(15)信号,这个信号并不会立刻处理,而要等进程准备继续运行之前处理,默认动作是终止进程。但如果给一个停止的进程发SIGKILL信号就不同了(会终止进程)。