进程组

Linux-进程、进程组、作业、会话、控制终端详解

一、进程

  传统上,Unix操作系统下运行的应用程序、 服务器以及其他程序都被称为进程,而Linux也继承了来自unix进程的概念。必须要理解下,程序是指的存储在存储设备上(如磁盘)包含了可执行机器指 令(二进制代码)和数据的静态实体;而进程可以认为是已经被OS从磁盘加载到内存上的、动态的、可运行的指令与数据的集合,是在运行的动态实体。这里指的 指令和数据的集合可以理解为Linux上ELF文件格式中的.text .data数据段。

二、进程组

  每个进程除了有一个进程ID之外,还属于一个进程组,那什么是进程组呢?

  顾名思义,进程组就是一个或多个进程的集合。这些进程并不是孤立的,他们彼此之间或者存在父子、兄弟关系,或者在功能上有相近的联系。每个进程都有父进程,而所有的进程以init进程为根,形成一个树状结构

  那为啥Linux里要有进程组呢?其实,提供进程组就是为了方便对进程进行管理。假设要完成一个任务,需要同时并发100个进程。当用户处于某种原因要终止 这个任务时,要是没有进程组,就需要手动的一个个去杀死这100个进程,并且必须要严格按照进程间父子兄弟关系顺序,否则会扰乱进程树。有了进程组,就可以将这100个进程设置为一个进程组,它们共有1个组号(pgrp),并且有选取一个进程作为组长(通常是“辈分”最高的那个,通常该进程的ID也就作为进程组的ID)。现在就可以通过杀死整个进程组,来关闭这100个进程,并且是严格有序的。组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。

   进程必定属于一个进程组,也只能属于一个进程组。 一个进程组中可以包含多个进程。 进程组的生命周期从被创建开始,到其内所有进程终止或离开该组。

  内核中,sys_getpgrp()系统调用用来获取当前进程所在进程组号;sys_setpgid(int pid, int pgid)调用用来设置置顶进程pid的进程组号为pgid。

三、作业

  Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成,Shell可以运行一个前台作业任意多个后台作业,这称为作业控制。

  作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业。一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。

四、会话

  再看下会话。由于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话 。一个会话可包含多个进程组,但只能有一个前台进程组。每个会话都有一个会话首领(leader),即创建会话的进程。 sys_setsid()调用能创建一个会话。必须注意的是,只有当前进程不是进程组的组长时,才能创建一个新的会话。调用setsid 之后,该进程成为新会话的leader。

  一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。 

  一次登录形成一个会话

  一个会话可包含多个进程组,但只能有一个前台进程组

五、控制终端

  会话的领头进程打开一个终端之后, 该终端就成为该会话的控制终端 (SVR4/Linux)  

  与控制终端建立连接的会话领头进程称为控制进程 (session leader) 

  一个会话只能有一个控制终端 

  产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程 

  终端上的连接断开时 (比如网络断开或 Modem 断开), 挂起信号将发送到控制进程(session leader)

 

  进程属于一个进程组,进程组属于一个会话,会话可能有也可能没有控制终端。一般而言,当用户在某个终端上登录时,一个新的会话就开始了。进程组由组中的领头进程标识,领头进程的进程标识符就是进程组的组标识符。类似地,每个会话也对应有一个领头进程。

  同一会话中的进程通过该会话的领头进程和一个终端相连,该终端作为这个会话的控制终端。一个会话只能有一个控制终端,而一个控制终端只能控制一个会话。用户通过控制终端,可以向该控制终端所控制的会话中的进程发送键盘信号。

 

   同一会话中只能有一个前台进程组,属于前台进程组的进程可从控制终端获得输入,而其他进程均是后台进程,可能分属于不同的后台进程组。

   当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都会有自己的前台工作和后台工作。

<think>嗯,用户想了解进程组和管道串联的概念及实现方法。首先,我需要确定这两个术语的具体含义,以及它们在操作系统中的实际应用。 进程组,我记得在Unix-like系统中,进程组是一组相关进程的集合,通常由同一个父进程创建,方便进行信号管理和作业控制。比如,在shell中运行一个命令管道,如`ls | grep txt`,这两个进程`ls`和`grep`可能属于同一个进程组,这样shell可以一次性发送信号给整个组。 然后是管道串联,也就是将多个进程通过管道连接起来,前一个进程的输出作为后一个的输入。这在命令行中很常见,比如`cat file | sort | uniq`。用户可能需要知道如何创建这样的管道,以及背后的机制,比如匿名管道和命名管道的区别。 接下来,实现方式方面,匿名管道通常用pipe()系统调用创建,适用于父子进程间通信。而命名管道(FIFO)则通过mkfifo创建,允许无亲缘关系的进程通信。进程组的话,可能需要使用setpgid或类似的函数来设置进程组ID。 示例部分,应该提供代码示例,比如C语言中使用fork和pipe创建管道,或者shell脚本中的多级管道。同时,结合用户提供的引用,特别是引用[2]和[4],里面提到了管道在进程间通信的应用,比如日志系统或任务调度,可以引用这些例子来说明实际应用场景。 用户可能还关心进程组如何与管道结合使用,比如在shell中如何管理后台任务,或者如何通过进程组来终止整个管道命令。需要解释进程组的创建和管理方法,以及信号如何影响整个组。 另外,注意用户提到的引用内容,比如引用[2]中的流水线处理例子,和引用[4]中的命名管道使用场景,这些都需要在回答中自然地引用,以增强可信度。 最后,确保回答符合格式要求,使用中文,行内公式用$...$,独立公式用$$...$$,正确引用标记,并生成相关问题。需要检查是否有遗漏的概念,或者是否需要进一步澄清进程组与管道串联之间的关系,以及具体的实现步骤和可能遇到的问题。</think>### 进程组与管道串联的实现方法 #### 1. **进程组的概念与作用** - **定义**:进程组是操作系统中的一组相关联进程的集合,共享同一进程组ID(PGID)。通常用于信号传递和作业控制,例如通过`kill`命令向整个进程组发送终止信号[^2]。 - **作用**: - 统一管理进程的生命周期(如同时暂停或终止)。 - 在Shell中,管道命令(如`ls | grep.txt`)中的多个进程默认属于同一进程组,方便批量操作。 #### 2. **管道串联的实现方式** - **匿名管道(Anonymous Pipe)**: - 通过`pipe()`系统调用创建,适用于父子进程间的单向通信。 - 示例:在C语言中创建父子进程的管道通信: ```c int fd[2]; pipe(fd); // 创建管道 if (fork() == 0) { // 子进程 close(fd[0]); // 关闭读端 write(fd[1], "data", 4); } else { // 父进程 close(fd[1]); // 关闭写端 char buf[4]; read(fd[0], buf, 4); } ``` - 特点:数据单向流动,生命周期随进程结束而终止[^4]。 - **命名管道(Named Pipe/FIFO)**: - 通过`mkfifo`命令或函数创建,允许无亲缘关系的进程通信。 - 示例:Shell中使用命名管道实现进程间通信: ```bash mkfifo mypipe # 创建命名管道 echo "data" > mypipe & # 进程A写入 cat mypipe # 进程B读取 ``` - 应用场景:日志收集系统、分布式任务调度[^4]。 #### 3. **进程组与管道的结合示例** - **Shell中的管道串联**: ```bash ls -l | grep ".txt" | wc -l ``` - 实现原理:Shell为`ls`、`grep`、`wc`创建同一进程组,通过匿名管道串联输出/输入。 - 进程组管理:使用`setpgid()`将子进程加入父进程组,实现信号统一处理。 - **C语言实现多级管道**: ```c int fd1[2], fd2[2]; pipe(fd1); // 第一级管道 if (fork() == 0) { // 进程1:ls dup2(fd1[1], STDOUT_FILENO); execlp("ls", "ls", NULL); } pipe(fd2); // 第二级管道 if (fork() == 0) { // 进程2:grep dup2(fd1[0], STDIN_FILENO); dup2(fd2[1], STDOUT_FILENO); execlp("grep", "grep", ".txt", NULL); } if (fork() == 0) { // 进程3:wc dup2(fd2[0], STDIN_FILENO); execlp("wc", "wc", "-l", NULL); } ``` #### 4. **关键技术点** - **信号传递**:向进程组发送信号(如`kill -TERM -PGID`)可终止所有关联进程。 - **管道阻塞特性**:命名管道在未同时打开读写端时会阻塞,需合理设计打开顺序。 - **资源管理**:管道缓冲区大小有限,需处理读写阻塞和缓冲区溢出问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值