Bootstrap

作业,进程组,终端

1:进程组:
每一个进程除了有一个进程ID,还属于进程组.进程组是一个或者多个进程的集合.通常,它们与同一作业相关联,可以接收来自同一终端的各种信号.每个进程组有一个唯一的进程组ID,每个进程组都可以有一个组长进程.组长进程的标志是,其进程组ID等于其进程ID.
组长进程可以创建一个进程组,创建该组中的进程,然后终止.只要在某个进程组中一个进程存在,则该进程组就存在,这与组长进程是否终止无关.

函数getpgrp返回调用者的进程组ID

#include<unistd.h>
pid_t getpgrp(void);

2:作业
shell分前后台来看控制的不是进程而是作业(Job)或者进程组(Process Group).一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,shell可以运行一个前台作业和任意多个后台作业,这称为作业控制
作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业.一旦作业运行结束,shell就把自己提到前台,如果原来的前台进程还存在,它自动变为后台进程组

3:会话
会话(Session)是一个或多个进程组的集合
一个会话可以有一个控制终端.这通常是登录到其上的终端设备或伪终端设备.建立与控制连接的会话首进程被称为控制进程.一个会话中的几个进程组课被分成一个前台进程组以及一个或多个后台进程组.所以一个会话中,应该包括控制进程,一个前台进程进程组和任意进程组.

我们来编写一个前台进程:
int mian()
{
while(1);
return 0;
}
a)前台进程:
这里写图片描述
b)后台进程
当给前台进程加上&,表明该前台进程转成后台进程.
&表示后台进程
这里写图片描述

4:作业控制
1:Session与进程组
前面我们知道”shell可以同时运行一个前台进程和任意多个后台进程”是不全面的,事实上,shell前台作业可以由多个进程组成,一个后台作业也可以,由多个进程组成,shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)
1 $proc1 | proc2

1 proc3|proc4 proc5
2:进程调用setsid函数建立一个新会话
其中proc1和proc2属于同一个后台进程组,proc3,proc4和 proc5属于同一个前台进程组,shell本身属于一个单独的进程组,=.这些进程组的控制终端相同它们同属于一个会话,当用户在控制终端输入特殊的控制键(Ctrl+C产生SIGINT,Ctrl+C\产生SIGQUIT,Ctrl+Z,产生SIGTSTP),内核发送相应的信号给前台进程组中的所有进程
这里写图片描述

我们从session和进程组的角度重新来看登录和执行命令的过程
这里写图片描述
表明这个作业由ps和cat两个进程组成,在前台运行,从PPID可以知道这两个进程的父进程是bash,从PGRP列可以看出,bash在id为4152的进程组中,而bash的id也是4152,所以bash是进程组的Leader,而两个子进程在id为4152的进程组中,ps是这个进程组的Leader.从Session空可以看出三个进程都在同一个Session中,bash是Session Leader.从TPGID可以看出,前台进程组的id是4166,,就是两个子进程所在的进程组.

这里写图片描述
ps和cat属于同一个作业,bash不等作业结束打印提示信息[1] 4174,然后给出新的提示符接受新的命令,[1]是作业的编号,如果同时运行多个作业可以用这个编号区分.4174是cat进程的id.

3:与作业控制有关的信号
这里写图片描述
将cat放到后台运行,由于cat需要读标准输入,而该后台进程是不能读终端输入的,因此内核法SIGTTIN信号给进程,该信号的默认处理动作是使进程停止.
这里写图片描述
jobs命令可以查看当前有哪些作业,fg命令可以将某个作业提至前台运行,如果该作业的进程正在后台运行则提至前台运行,如果该作业是停止状态,则给进程组的每个进程发SIGCONT信号使它继续运行.参数%1表示将第一个作业提至前台运行,cat提到前台后挂起等待输入,Ctrl+Z则向所有前台进程发SIGTSTP信号,该信号的默认动作是使进程停止,cat继续以后台的形式存在.
这里写图片描述

bg命令可以让某个停止的作业在后台继续运行,也需要给该作业的进程组的每个进程发SIGCONT信号,cat进程继续运行,cat进程继续运行,又要读终端输入,然而它在后台不能读终端输入,所以又收到SIGTTIN而停止
这里写图片描述
用kill命令给一个停止的进程发SIGTERM信号,这个信号并不会立即处理,而要等待进程准备继续运行之前处理,默认动作是终止进程

5: 终端:
在UNIX系统中,用户通过终端登录系统后得到一个shelll进程,这个终端成为shell进程的控制终端,控制终端保存在pcb中的信息,而我们知道fork()会复制pcb中的信息,因此由shell进程启动的其他进程的控制终端也是这个终端.

默认情况下,每个进程的标准输入,标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或保准错误输出写也就是输出到显示器上.

每个进程都可以通过有个特殊的设备文件/dev/tty访问它的控制终端.事实上每个终端设备读对应一个不同的设备文件,./dev/tty提供了一个通用的接口,一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访问.ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不是任意文件.
查看终端对应的设备:

#include<stdio.h>
#include<unistd.h>

int main()
{
printf("fd:  %d->%s\n",0,ttyname(0));
printf("fd:  %d->%s\n",1,ttyname(1));
printf("fd:  %d->%s\n",2,ttyname(2));

return 0;
}

这里写图片描述

;