34.Linux/Unix 系统编程手册(下) -- 进程组,会话和作业控制

1.进程组	
	进程组是一个或者多个共享同一进程组标识符(PGID)的进程组成。进程组ID是一个数字,
  其类型和进程ID(pid_t)一样。一个进程组拥有一个进程组首进程,该进程是创建进程组的进程,
  其进程ID为该进程组的ID,新进程会继承其父进程所属的进程组ID.
    进程组拥有一个生命周期,其开始时间为首进程创建时间,结束时间为最后一个成员进程退出时间。
  一个进程可能因为终止而退出进程组,也可能因为加入了另外一个进程组而退出。进程组的首进程无需
  是最后一个离开进程组的成员。

2.会话
	会话是一组进程组的集合。进程的会话成员关系是由其会话标识符(SID)确定的,会话标识符与进程组ID一样,
  是一个类型为 pid_t 的数字。会话首进程是创建该新会话的进程,其进程ID会成为会话ID.新进程会继承其父进程
  的会话ID.
    一个会话中所有的进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时建立。一个终端最多可能
  会成为一个会话的控制终端。
    在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其他进程组会成为后台进程组。只有前台进程组中的进程
  才能从控制终端中读取输入。当用户在控制终端中输入其中一个信号生成终端字符之后,该信号会被发送到前台进程组中的所有
  成员。这些字符包括生成 SIGINT 的中断字符(control + c),生成 SIGQUIT 的退出字符(control + \), 生成 SIGSTP
  的挂起字符(control + z)。
    当到控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程。成为控制进程的主要标志是当断开与终端之间的连接时
  内核会向该进程发送一个 SIGHUP 信号。

    会话和进程组的主要用途是用于 shell 作业控制。
    在除任务控制之外的其他场景中也可能用到进程组,因为进程组拥有两个有用的属性:
    	1.在特定的进程组中父进程能够等待任意子进程
    	2.信号能够被发送给进程组中的所有成员

    一个进程在其子进程已经执行 exec() 之后,就无法修改子进程的进程组ID。

    setsid();
    如果调用进程不是进程组的首进程,那么将创建一个新的会话。
    setsid()会按照下列步骤创建一个新的会话:
    	1.调用进程成为新会话的首进程和该会话中新进程组的首进程。调用进程的进程组ID和会话ID会被设置为该进程的进程ID
    	2.调用进程没有控制终端。所有之前到控制终端的连接都会被断开。
    如果调用进程是一个进程组首进程,那么setsid()调用将失败.

3.控制终端和控制进程
	一个会话中所有的进程可能都会拥有一个控制终端。会话别创建出来的时候是没有控制终端的,当会话首进程首次打开一个还没有
  成为某个会话的控制终端的终端时会建立控制终端,除非在调用 open() 时指定 O_NOCTTY标记。一个终端至多只能称为一个会话的
  控制终端。
    控制终端会被由 fork() 创建的子进程继承并且在 exec() 调用中保持。
    当会话首进程打开了一个控制终端后它同时成为了该终端的控制进程。在发生终端断开后,内核会向该控制进程发送一个 SIGHUP 信号
  来通知这一事件的发生。
    如果一个进程拥有一个控制终端,那么打开特殊文件 /dev/tty 就能获得该终端的文件描述符。这对于一个程序在标准输入和标准输出被
  重定向之后需要确保资金确实在与控制终端进行通信是很有用的。

4.前台和后台进程组
	在一个会话中,在同一时刻只有一个进程能够成为前台进程,会话中的其他所有进程都是后台进程组。前台进程组是唯一能够自由的读取和写入
  控制终端的进程组。当在控制终端中输入其中一个信号生成终端字符之后,终端驱动器会将相应的信号发送给前台进程组的所有成员。

5.SIGHUP 信号
	当一个控制进程失去其终端连接之后,内核会向其发送一个 SIGHUP 信号来通知这一事实。
	SIGHUP 信号也可以用作他用。当一个进程组称为孤儿进程组时会生成 SIGHUP 信号;此外,手工发送 SIGHUP 信号通常用来触发 daemon 进程
  重新初始化自身或重新读取配置文件。
    在登录会话中,shell 通常是终端的控制进程。大多数 shell 程序在交互式运行时会为 SIGHUP 信号建立一个处理器。这个处理器会终止shell。
  但在终止之前会向由 shell 创建的各个进程组(包括前台,后台进程组)发送一个 SIGHUP 信号。

    nohup 命令可以用来使一个命令对 SIGHUP 信号免疫---即执行命令时将 SIGHUP信号的处置设置为 SIG_IGN。这样就防止了当终端被挂掉时命令
  被杀死的情况的发生。

    如果因为终端断开而引起的向控制进程发送的 SIGHUP 信号会导致控制进程终止,那么SIGHUP 信号会被发送给终端的前台进程组中的所有成员。

6.作业控制
	作业控制允许一个 shell 用户同时执行多个命令(作业),其中一个命令在前台运行,其余的命令在后台运行。作业可以被停止和恢复运行以及在
  前后台之间移动。
    sleep 60 &   // 放到后台运行
    [1] 18932   //作业号,进程ID
    %num // 来引用作业
    %% , %+ 指当前作业
    %- 上一个作业

    jobs // 列出所有后台作业
    fg %num //将后台作业移到前台
    control + Z // 暂停作业,发送的是 SIGTSTP 信号
    bg %num // 将作业放在后台进行,发送一个 SIGCONT 继续的信号
    kill -STOP %num // 停止后台的某个作业

    只有前台作业中的进程才能够从控制终端中读取输入。这个限制条件避免了多个作业竞争读取终端输入。如果后台作业尝试从终端读取输入,就会接受到
  一个 SIGTTIN 信号,默认处理动作是停止作业.

    SIGTTIN  // 后台作业尝试从终端读
    SIGTTOU //后台作业尝试写终端
    SIGTSTP // control + z, 暂停作业

7.孤儿进程组
	每个成员的父进程本身是组的一个成员或不是组会话的一个成员时,就变成了一个孤儿进程组。
	由于 shell 并没有创建子进程,因此它并不清楚子进程是否存在以及子进程与已经退出的父进程位于同一个进程组中。此外,init 进程只会检查被终止的  
  子进程并清理该僵尸进程,从而导致被停止的子进程可能会永远停留在系统中,因为没有进程知道要向其发送一个 SIGCONT 信号来恢复它的执行。
    即使孤儿进程组中的一个被停止的进程拥有一个仍然存活但位于不同会话的父进程,也无法保证父进程能够向这个停止的子进程发送 SIGCONT 信号。一个进程
  可以向同一会话中的其他任意进程发送 SIGCONT 信号,但如果子进程位于不同的会话中,发送信号的标准规则就开始起作用了,因此如果子进程是一个修改了自身的验证
  信息的特权进程,父进程可能就无法向子进程发送信号。
    为了防止上述情况发送,SUSv3 规定,如果一个进程组变成了孤儿进程组并且拥有很多已经停止的成员,那么系统会向进程组中的所有成员发送一个 SIGHUP 信号
  通知它们已经与会话断开连接了,之后再发送一个 SIGCONT 信号确保它们恢复执行。如果孤儿进程组不包含被停止的成员,那么久不会发送任何信号。

    会话和进程组是用来支持 shell 作业控制的。在作业控制中,shell是会话是首进程和运行该shell的终端的控制进程。shell 会为执行的每个作业创建一个独立的进程组,
  并且提供了将作业在3个状态之间转移的命令。这3个状态分别是,在前台运行,在后台运行,和在后台运行。

getpgrp(); //获得一个进程的进程组ID
setpgid(); //设置进程的进程组ID
getsid(); //返回pid指定的进程的会话id
setsid(); //新建一个会话
ctermid(); //返回表示控制终端的路径名
tcgetogro(); //获得一个终端的进程组
tcsetpgrp(); //修改一个终端的进程组

猜你喜欢

转载自blog.csdn.net/enlyhua/article/details/82932444
今日推荐