进程组

原著这里前面实际上还有两节,但是笔者感觉并不是特别重要,只是Unix各个实现的登录,所以就直接从进程组开始讲。

在使用man 2 intro的时候,Unix系统手册上已经写了很多关于系统的概念,其中就有进程组概念:

这是由一个称为进程组ID的非负整数标识的进程组组成的组。 这是组长的进程ID。

这种分组允许相关过程的信号传递(参见termios(4))和csh(1)的作业控制机制。

激活的进程都是进程组的医院,进程组ID由进程组顶部进程的pid作为标识,进程组主要用于相关进程之间的信号传递和作业控制。由于进程组ID实际上是一个pid,所以Unix系统给出了以下函数

pid_t getpgrp(void);
pid_t getpgid(pid_t pid);

getpgrp函数是返回当前进程的进程组ID,SUS标准还规定了getpgid用于返回指定pid的进程组ID,如果pid为0,则返回当前进程的进程组ID。

进程组实际上是一个虚概念,并不是物理划分的组,任何一个进程都可以认为是一个进程组,进程自身就带有一个属性用于标识自身的进程组,当进程组ID和自身进程ID相等时,就是一个只有一个进程的进程组,当然,进程也可以调用setpgid函数加入一个进程组或者创建一个新进程组。

int setpgid(pid_t pid, pid_t pgid);

setpgid函数将pid进程的进程组ID设置为pgid,如果pid为0,则使用调用者的进程ID,如果pgid为0,则由pid指定的进程ID作为进程组ID。


会话

会话是一系列进程组的集合,使用setsid函数来创建一个会话,并且当前的进程会变成新会话中的唯一进程组的唯一成员。既然前面提到了setsid函数,我们就来看看手册说明

setsid函数创建一个新的会话。 调用进程是新会话的会话负责人,是新进程组的进程组负责人,没有控制终端。 调用进程是会话或进程组中的唯一进程。

成功完成后,setsid函数会返回新进程组的进程组ID,它与调用进程的进程ID相同。

和前面会话的讲解基本差不多,就多了一点——没有控制终端。在说明页(man 2 intro)中,笔者并没有找到有关会话ID的概念,但是确实存在会话唯一标示符,可以类比前面的内容,大胆猜测一下,会话ID实际上就等于会话首进程的进程ID,实际上,在BSD系统历史上,也确实引入了这个概念


控制终端

控制终端是什么,手册页上是这么描述的,与会话关联的终端被称为该会话及其成员的控制终端。,除了控制终端以外,会话和进程组还有其他特点

  1. 会话有控制终端,只要是能实现终端功能的设备都行。

  2. 和控制终端建立链接的会话首进程是控制进程。

  3. 会话中的进程组可以分为前台进程组、后台进程组

  4. 存在控制终端则必定存在前台进程组

从上面可以看到几个新名词,控制进程、前台进程组和后台进程组,控制进程这里也直接拿手册页描述,与控制终端的会议领导者是一个控制过程。,前台后台进程组可能经常使用Unix系统的人比较熟悉,前台进程组在SSH断线后就会自动终止,而后台进程组则不会终止。


tcgetpgrp、tcsetpgrp和tcgetsid函数

pid_t tcgetpgrp(int fildes);
int tcsetpgrp(int fildes, pid_t pgid_id);
pid_t tcgetsid(int fildes);

tcgetpgrp函数看函数名好像是获得进程组ID,但是实际上是根据终端设备的文件描述符返回前台进程组ID,tcsetpgrp则是当进程有一个控制终端的时候,将前台进程组ID设置为pgid_id,这个值应当是同一会话的一个进程组ID,filedes则是该会话的控制终端。实际上非常好理解,进程组ID的控制应当是归属会话的启动进程的,也就是第一个进程,而更改前台进程组ID则应当限定为同一会话,将后台进程组更改为前台,这就是这两个函数的作用。

最后一个tcgetsid函数则是获得会话ID,实际上前面也讲过了,这个就是第一个进程组的第一个进程的进程ID。


作业控制

shell环境下的作业控制各位应当是很熟悉了,作业控制就是在一个终端下进行的多任务处理,所有的作业实际上都是shell的子进程,这很容易理解。任何情况下,终端都只有一个前台任务,其他都是后台任务,对于后台任务的处理,基本上都是使用&让其在后台运行,或者说是更加普遍的screen命令,当然,这个不在讨论范围内,在前面的讲解中,有一些关于终端产生信号的部分,其实有三个特殊字符用于产生信号

  1. Ctrl+C => SIGINT
  2. Ctrl+\ => SIGQUIT
  3. Ctrl+Z => SIGTSTP

其中前两个特殊字符已经讲过,最后一个Ctrl+Z就是挂起信号。


shell执行程序

在前面讲过,shell执行程序是通过exec函数族来进行的,也就是说,所有的程序都是shell的子进程,经过前面的讲解,应该知道进程至少有pid、ppid、pgid和sid四种属性,对于经典的sh终端来说,由于sh不支持作业控制,也就是说,只有一个前台进程组存在,在实际使用中经常使用到管道,管道实际上是一系列串行的任务集合,shell非常巧妙的逆序将其fork自身,然后反向执行,也就是说,由于第一个任务的结果是第二个任务的输入,所以实际上fork的过程是shell fork最后一个任务,逆序的向下派生的。笔者在这里可能介绍的不够全面,可以看原著学习。


孤儿进程组

在前面介绍过一个进程如果没有了父进程,则父进程变为init进程,这种叫做孤儿进程,那么也可以类比到进程组,是否存在孤儿进程组。这节也是可看可不看。


小结

实际上在写这章的时候笔者是很头疼的,因为基本没什么干货,都是些概念性的东西,很多都是man 2 intro上面看看就完事了,特别是前面的终端登录,实际上由于原著已经很老了,很多实现基本都变了,所以很难写出实践和感想,笔者建议就只看看原著就行了。

results matching ""

    No results matching ""