`

进程组、会话和控制终端

阅读更多
    每个进程除了有一个进程 ID 外,还属于一个进程组。进程组是进程的集合,它们通常是在同一作业中结合起来的,其中的各进程接收来自同一终端的各种信号。每个进程组有一个进程组 ID,并可存放在 pid_t 数据类型中。
    函数 getpgrp 返回调用进程的进程组 ID,函数 getpgid 可返回特定进程的进程组 ID。
#include <unistd.h>
pid_t getpgrp(void);         /* 返回值:调用进程的进程组 ID */
pid_t getpgid(pid_t pid);    /* 返回值:若成功,返回进程组 ID;否则,返回 -1 */

    当 pid 等于 0 时,getpgid(0) 等价于 getpgrp()。
    每个进程组有一个组长进程,其进程组 ID 等于其进程 ID。组长进程可以创建一个进程组、创建该组中的进程,然后终止。只要某个进程组中有一个进程存在,则该进程组就存在,而与其组长进程是否终止无关。从进程组创建到其中最后一个进程离开为止的时间区称为进程组的生命期。每个进程组中的最后一个进程可以终止,也可以转移到另一个进程组。
    进程调用 setpgid 可以加入一个现有的进程组或者创建一个新进程组(setsid 函数也可以创建一个新的进程组,见下)。
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);    /* 返回值:若成功,返回 0;否则,返回 -1 */

    setpgid 函数把 pid 进程的进程组 ID 设置为 pgid。如果这两个参数相等,则由 pid 指定的进程变成进程组组长。如果 pid 是 0,则使用调用者的进程 ID。而如果 pgid 是 0,则由 pid 指定的进程 ID 用作进程组 ID。
    一个进程只能为它自己或它的子进程设置进程组 ID,在它的子进程调用了 exec 后,它就不再更改该子进程的进程组 ID。在多数作业控制中,在 fork 之后调用此函数,使父进程设置其子进程的进程组 ID,并且也使子进程设置其自己的进程组 ID。尽管其中有一个是冗余的,但确能保证在父进程和子进程认为子进程已进入了该进程组之前,这确实已经发生了。否则,由于父进程和子进程运行的先后次序不确定,会因为子进程的组员身份取决于哪个进程首先执行而产生竞争条件。

    一个或多个进程组的集合就称为会话。如下图中的一个会话有 3 个进程组。

    通常是由 shell 的管道将几个进程编成一组的。例如上图中的安排可能是由下列形式的 shell 命令形成的:
        proc1 | proc2 & proc3 | proc4 | proc5
    进程调用 setsid 函数建立一个新会话。
#include <unistd.h>
pid_t setsid(void);            /* 返回值:若成功,返回进程组 ID;否则,返回 -1 */

    如果调用进程不是一个进程组的组长,则此函数创建一个新回话。具体会发生以下 3 件事:
    (1)该进程变成新会话的会话首进程(session leader,即创建该会话的进程)。
    (2)该进程成为一个新进程组的组长进程。
    (3)该进程没有控制终端(见下),即使在之前它有,这种联系也会被切断。
    如果调用进程已经是一个组长进程,则此函数返回出错。为了避免这种情况,通常先调用 fork,然后使其父进程终止,而子进程继续。因为子进程继承了父进程的进程组 ID,而其进程 ID 是新分配的,两者不可能相等,这就保证了子进程不是一个进程组的组长。
    getsid 函数可以返回会话首进程的进程组 ID。
#include <unistd.h>
pid_t getsid(pid_t pid);
                /* 返回值:若成功,返回会话首进程的进程组 ID;否则,返回 -1 */

    如果 pid 是 0,getsid 返回调用进程的会话首进程的进程组 ID。出于安全考虑,一些实现作了这种限制:如若 pid 不属于调用者所在的会话,那么调用进程就不能得到该会话首进程的进程组 ID。

    会话和进程组还有一些其他特性。
    * 一个会话可以有一个控制终端,通常是终端设备(在终端登录的情况下)或伪终端设备(在网络登录的情况下)。
    * 建立与控制终端连接的会话首进程被称为控制进程。
    * 一个会话中的几个进程组可被分成一个前台进程组以及多个后台进程组。
    * 如果一个会话有一个控制终端,则它有一个前台进程组,其他进程组为后台进程组。
    * 无论何时输入终端中断键(常为 Delete 或 Ctrl+C)或退出键(常为 Ctrl+\),都会将中断信号或退出信号发送至前台进程组的所有进程。
    * 如果终端接口检测到调制解调器或网络已经断开,则将挂断信号发送至控制进程(会话首进程)。
    这些特性可用下图表示。

    通常登录时会自动建立控制终端。POSIX.1 将如何分配控制终端的机制交给具体实现来选择。比如,当会话首进程打开第一个尚未与一个会话相关联的终端设备时,只要在调用 open 时没有指定 O_NOCTTY 标志,System V 派生的系统就将此作为控制终端分配给此会话;当回话首进程用 TIOCSCTTY 作为 request 参数(第三个参数是空指针)调用 ioctl 时,基于 BSD 的系统(但 Mac OS X 10.6.8 采用的是 System V 方式)为会话分配控制终端。为使此调用成功执行,此会话不能已经有一个控制终端(通常 ioctl 调用紧跟在 setsid 调用后,setsid 可保证此进程是一个没有控制终端的会话首进程)。
    有时不管标准输入、标准输出是否重定向,程序都要与控制终端交互作用。保证程序能与控制终端对话的方式是 open 文件 /dev/tty,此特殊文件在内核中是控制终端的同义词。典型的例子是用于读口令的 getpass 函数,它由程序 crypt 调用,并可用于管道中。例如:
        crypt < salaris | lpr
    将文件 salaries 解密,然后经由管道将输出送至打印缓冲服务程序。
    可以使用下面几个函数来通知内核哪一个进程组是前台进程组,以便设备驱动程序知道该把终端输入和终端产生的信号发往何处。
#include <unistd.h>
pid_t tcgetpgrp(int fd);    /* 返回值:若成功,返回前台进程组 ID;否则,返回 -1 */
int tcsetpgrp(int fd, pid_t pgrpid);   /* 返回值:若成功,返回 0;否则,返回 -1 */

#include <termios.h>
pid_t tcgetsid(int fd);
                    /* 返回值:若成功,返回会话首进程的进程组 ID;否则,返回 -1 */

    函数 tcgetpgrp 返回前台进程组 ID,它与在 fd 上打开的终端相关联。
    如果进程有一个控制终端,就可调用 tcsetpgrp 将前台进程组 ID 设置为同一会话中的一个进程组 ID pgrpid,fd 必须引用该会话的控制终端。tcgetpgrp 和 tcsetpgrp 通常都是由作业控制 shell 调用。
    如果给出控制终端的文件描述符,就可利用 tcgetsid 获得会话首进程的进程组 ID。

    下面这这张图显示了 FreeBSD 中使用的各种有关数据结构。

    每个会话都分配一个 session 结构(如每次调用 setsid 时),其中:
    * s_count 是该会话中的进程组数。当它减至 0 时,则可释放此结构。
    * s_leader 是指向会话首进程 proc 结构的指针。
    * s_ttyvp 是指向控制终端 vnode 结构的指针。
    * s_ttyp 是指向控制终端 tty 结构的指针。
    * s_sid 是会话 ID(非 Single UNIX Specification 的组成部分)。
    在调用 setsid 时,内核分配一个新的 session 结构。s_count 设置为 1,s_leader 设置为调用进程 proc 结构的指针,s_sid 设置为进程 ID。因为新回话没有控制终端,所以 s_ttyvp 和 s_ttyp 设置为空指针。
    而对于 tty 结构,每个终端设备和伪终端设备均在内核中分配这样一种结构,其中:
    * t_session 指向将此终端作为控制终端的 session 结构。终端在失去载波信号时使用此指针将挂起信号发送给会话首进程。
    * t_pgrp 指向前台进程组的 pgrp 结构。终端驱动程序用此字段将信号发送给前台进程组。
    * t_termios 是包含所有特殊字符(如中断 Ctrl+ C)和与该终端有关信息(如波特率、回显打开或关闭等)的结构。
    * t_winsize 是包含终端窗口当前大小的 winsize 型结构。当终端窗口大小改变时,信号 SIGWINCH 将被发送至前台进程组。
    pgrp 结构包含一个特定进程组的信息,其中:
    * pg_id 是进程组 ID。
    * pg_session 指向此进程组所属会话的 session 结构。
    * pg_members 是指向此进程组 proc 结构成员表的指针。
    proc 结构包含一个进程的所有信息,其中:
    * p_pid 包含进程 ID。
    * p_pptr 是指向父进程 proc 结构的指针。
    * p_pgrp 是指向本进程所属的进程组的 pgrp 结构的指针。
    * p_pglist 结构包含两个指针,分别指向进程组中的上一个和下一个进程。
    最后还有一个 vnode 结构,在打开控制终端设备时分配此结构。进程对 /dev/tty 的所有访问都通过该结构。
  • 大小: 4.9 KB
  • 大小: 9.3 KB
  • 大小: 18.7 KB
分享到:
评论

相关推荐

    进程组和会话期1

    在操作系统中,进程组和会话...进程组用于接收终端发送的信号,而会话则更高级地组织进程,尤其是在实现作业控制和终端交互时。理解这些概念对于编写需要进行复杂进程管理的程序,如守护进程或后台作业,是非常关键的。

    守护进程简介,个人使用

    守护进程必须与其运行前的环境隔离开来,这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等。这些环境通常是守护进程从执行它的父进程中继承下来的。 进程组是每运行一个程序或...

    进程同步.docx

    一个会话只能有一个控制终端,产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程。 二、信号处理 信号处理是进程同步的一种机制,用于处理异步事件。 1. 信号的概念 信号是软件中断,是异步...

    Linux-UNIX系统编程手册(下册)1

    34.4 控制终端和控制进程: 控制终端是与会话关联的终端设备,它为会话中的进程提供输入和输出接口。控制进程是指与控制终端直接交互的进程,通常是会话首进程。当用户在控制终端上按下组合键(如Ctrl+C)时,产生的...

    Linux实验二:Linux-基本命令-进程管理.doc

    会话是一组相关进程的集合,通常与一个控制终端相关联。进程组是共享同一会话的进程集合。一个进程可以有子进程,形成进程树结构。在终端上运行的进程可以分为前台进程和后台进程。前台进程接收用户的输入,而后台...

    Linux守护进程的应用研究.pdf

    2. **调用setsid函数**:setsid函数用于创建新的会话和进程组,使进程脱离控制终端,成为新会话的首进程。这一步至关重要,因为守护进程不应有控制终端。同时,了解进程组和会话的概念也很重要。进程组是一组进程,...

    LINUX进程控制编程

    5. **进程组和会话**:`setpgid()`设置进程组ID,`setsid()`创建新会话。 五、进程终止 进程可以通过正常退出(`exit()`)或异常终止(收到信号)结束。`_exit()`系统调用比`exit()`更直接地退出进程,不执行清理...

    编写Linux守护进程

    编写 Linux 守护进程需要了解多个概念,如子进程、进程组、会晤期、信号机制、文件、目录和控制终端等。 要编写一个 Linux 守护进程,需要遵循以下八条经验: 1. 屏蔽一些有关控制终端操作的信号,以防止守护进程...

    Linux守护进程的编写.pdf

    使用`setsid()`函数可以使进程成为一个新的会话组长和进程组长,从而脱离原有的登录会话和控制终端。 3. **禁止重新打开控制终端**:为防止守护进程重新获取控制终端,再次`fork()`一次,让子进程不再成为会话组长...

    linux技巧之使用screen管理你的远程会话.docx

    * 控制进程:会话期首进程,与控制终端连接的会话期首进程。 * 前台进程:当前与终端交互的进程。 问题: * 当使用 ssh 或 telent 远程登录到 Linux 服务器上,运行一些长时间的任务,必须等待任务完成才能离开该...

    Linux系统下守护进程编程方法

    脱离终端和会话过程** 守护进程需要从启动它的会话和进程组中分离出来,避免受到终端信号的干扰。这通常通过调用`setsid()`函数实现,但在调用前可能需要先通过`fork()`创建一个子进程,以满足`setsid()`的条件,...

    Linux守护进程的编程方法.pdf

    2. **脱离终端、会话和进程组**:调用`setsid()`函数使进程成为新的会话组长和进程组组长,与原有的终端、会话和进程组脱离。 3. **防止重新获得控制终端**:再次调用fork,创建的第二个子进程不再是会话组长,无法...

    守护进程介绍

    在Linux系统中,管理和控制守护进程通常通过控制系统服务(如System V init、Upstart或Systemd)进行,这些服务可以根据不同的运行级别启动、停止或重启守护进程。 总的来说,守护进程是Linux系统中的核心元素,...

    Linux的守护进程简介

    3. 调用setsid()函数创建新的会话和进程组,使进程成为会话组长,不再与控制终端关联。 4. 更改工作目录:通常将工作目录更改为根目录("/"),以避免对特定文件系统的依赖。 5. 关闭不必要的文件描述符:关闭所有...

    Linux守护进程的研究.pdf

    4. 单独的进程组和会话:守护进程是其所属进程组和会话中的唯一进程。 启动守护进程的方式主要有: 1. 系统引导时启动:通过系统初始化脚本(通常位于/etc/rc.d下)启动,这是基本服务程序的常见启动方式。 2. 由...

    开发Linux操作系统后台服务进程详解

    2. **脱离控制终端、登录会话和进程组**:调用`setsid()`函数,使得进程成为新的会话组长和进程组长,从而与原来的登录会话和终端分离,避免受到终端的影响。 3. **禁止重新打开控制终端**:再进行一次`fork()`,...

    后台进程与守护进程的区别

    在UNIX及类UNIX系统中,后台进程的一个显著特征是其进程组ID(PGID)与控制终端进程组ID(TPGID)不同,这使得它们可以被轻易识别出来。此外,后台进程不会接收到从键盘发出的中断信号(如Ctrl-C),但这并不作为...

Global site tag (gtag.js) - Google Analytics