进程组,会话,进程之间的关系

本文详细解析了 Linux 中的进程组、会话、控制终端等核心概念,并通过实验展示了这些概念的实际应用。文章解释了如何使用 setpgid 和 getpgid 等函数来管理和获取进程组信息,以及如何利用 setsid 创建新的会话。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文中内容是参考了多位网友的资料整理而成的,个人一直对进程组,线程组,会话,终端的概念比较模糊,在学习的过程中整理了这篇文章。

将阐述Linux内核中的如下几个概念


1.概念:
a)进程组
Shell 上的一条命令行形成一个进程组 ,譬如 :./test | ./test1一条命令会创建两个进程test,test1这两个进程就属于一个进程组。在test,test1中创建的子进程也都属于该进程组。
每个进程属于一个进程组 ,每个进程组有一个领头进程 ,通常领头进程是进程组的第一个进程,进程组号是第一个进程的pid.
进程组的生命周期到组中最后一个进程终止, 或加入其他进程组为止。 
getpgrp: 获得进程组 id, 即领头进程的 pid 。
setpgid: 加入进程组和建立新的进程组 。
前台进程组和后台进程组,前台进程组可以和控制终端交互,处理控制终端发出的各种信号,比如:ctrl+c等,ctrl+c会将前台进程组所有进程都给杀死,后台进程组则不会处理相关的控制终端的信号。 即使关闭终端和会话,后台进程组也不会退出。
===============================================================================
       #include 
        int setpgid (pid_t pid, pid_t pgid);
        pid_t getpgid (pid_t pid);
        int setpgrp (void);
        pid_t getpgrp (void);
-------------------------------------------------------------------------------
   进程只能将自身和其子进程设置为进程组 id. 
   某个子进程调用 exec 函数之后, 就不能再将该子进程的 id 作为进程组 id.

后台的文件描述符也是继承于父进程,例如shell,所以它也可以在当前终端下显示输出数据。
但是daemon进程自己变成了进程组长,其文件描述符号和控制终端没有关联,是控制台无关的。
===============================================================================
b)会话
一次登录形成一个会话 
一个会话可包含多个进程组, 但只能有一个前台进程组. 
setsid 可建立一个新的会话 
===============================================================================
       #include 
       pid_t setsid(void);
-------------------------------------------------------------------------------
    如果调用进程不是进程组的领头进程, 该函数才能建立新的会话.
    调用 setsid 之后, 进程成为新会话的领头进程.进程成为新进程组的领头进程. 进程失去控制终端
===============================================================================
c)控制终端
会话的领头进程打开一个终端之后, 该终端就成为该会话的控制终端 (SVR4/Linux) 
控制终端建立连接的会话领头进程称为控制进程 (session leader) 
一个会话只能有一个控制终端 
产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程 
终端上的连接断开时 (比如网络断开或 Modem 断开), 挂起信号将发送到控制进程(session leader) 

2实验

编写程序:

[cpp]  view plain copy
  1. /*test.c*/  
  2. #include "../include/apue.h"  
  3.   
  4. void main() {  
  5.   pid_t pid;  
  6.   char *args[] = {"/bin/echo""Hello""World!", NULL };  
  7.   if((pid = fork()) < 0)   
  8.     err_sys("fail to fork!");  
  9.   else if(pid == 0) {  
  10.         execve("./test1", args, NULL);  
  11.   }  
  12.  /* 
  13.   if((pid = fork()) < 0)  
  14.     err_sys("fail to fork!"); 
  15.   if(pid == 0) { 
  16.         execve("./test2", NULL, NULL); 
  17.    } 
  18. */  
  19.    while(1) { };  
  20. }  
  21.   
  22. /*test1.c*/  
  23. #include <stdio.h>  
  24. int main(int args, char* argv[]) {  
  25.     char s[5] = "ABCD";  
  26.     while(1) {}  
  27.     return;  
  28. }  
  29.   
  30. /* in test2.c */  
  31. #include <stdio.h>  
  32. #include <stdlib.h>  
  33.   
  34. int main()  
  35. {  
  36.         pid_t   pid;  
  37.         pid = fork();  
  38.         if (pid <0) {  
  39.                 printf("fork err\n");  
  40.                 exit(-1);  
  41.         } else if (pid == 0) {  
  42.                 /* in child process */  
  43.             execve("./test3", NULL, NULL);  
  44.   
  45.         }   
  46.     while(1) {}  
  47. }  
  48.   
  49. /*test3.c*/  
  50. #include <stdio.h>  
  51. void main() {  
  52.   
  53.     while(1) {  
  54.         //printf("test3\n\n");  
  55.     }  
  56. }  


实验一:

编写shell文件,shell文件的作用是同时启动两个进程:

#!/bin/bash

./test | ./test2    

$ps -ejH

  PID  PGID   SID TTY          TIME CMD


 3888  3888  3888 pts/4    00:00:00     bash
 4961  4961  3888 pts/4    00:00:00       ps
 4394  4394  4394 pts/0    00:00:00     bash
 4957  4957  4394 pts/0    00:00:00       test
 4959  4957  4394 pts/0    00:00:00         test1
 4958  4957  4394 pts/0    00:00:00       test2
 4960  4957  4394 pts/0    00:00:00         test3

$pstree -H 4394


  |-gnome-terminal

  |   |-bash
  |   |   `-pstree -all
  |   |-bash
  |   |-bash
  |   |   |-test
  |   |   |   `-test1 Hello World!
  |   |   `-test2
  |   |       `-(test3)
  |   |-gnome-pty-helpe
  |   `-3*[{gnome-terminal}]

结果分析:

PID为4394的进程是我开启的用于运行程序的终端,也就是一个会话(session),其ID为4394。在该session下运行的程序的SID当然都是

4394.我们同时运行了两个进程test和test2,其PID分别为4057和4958,其组ID都是4057。看到,test和test2同属一个进程组,其组ID为该

组创建的第一个进程的ID,即test的ID。由test和test2创建的子进程的组ID同样为4057。


实验二:

$./test  &    //在后台运行test

$./test2 &   //在后台运行test2

  PID  PGID   SID TTY          TIME CMD


 2697  1501  1501 ?        00:08:20   gnome-terminal
 2705  1501  1501 ?        00:00:00     gnome-pty-helpe
 3050  3050  3050 pts/1    00:00:00     bash
 3888  3888  3888 pts/4    00:00:00     bash
 5294  5294  3888 pts/4    00:00:00       ps
 4394  4394  4394 pts/0    00:00:00     bash
 5288  5288  4394 pts/0    00:00:05       test
 5289  5288  4394 pts/0    00:00:04         test1
 5290  5290  4394 pts/0    00:00:00       test2
 5291  5290  4394 pts/0    00:00:01         test3

$pstree -all


  |-gnome-terminal
  |   |-bash
  |   |   `-pstree -all
  |   |-bash
  |   |-bash
  |   |   |-test
  |   |   |   `-test1 Hello World!
  |   |   `-test2
  |   |       `-(test3)
  |   |-gnome-pty-helpe
  |   `-3*[{gnome-terminal}]

实验结果分析:

我们先后运行了两个进程,可以看到其组ID是不一致的。


实验三:

修改test.c源程序,取消对注释部分的注释,重新编译

$gcc -g -o test test.c

$./test

   PID  PGID   SID TTY          TIME CMD

 2697  1501  1501 ?        00:08:20   gnome-terminal
 2705  1501  1501 ?        00:00:00     gnome-pty-helpe
 3050  3050  3050 pts/1    00:00:00     bash
 3888  3888  3888 pts/4    00:00:00     bash
 5397  5397  3888 pts/4    00:00:00       ps
 4394  4394  4394 pts/0    00:00:00     bash
 5393  5393  4394 pts/0    00:00:01       test
 5394  5393  4394 pts/0    00:00:01         test1
 5395  5393  4394 pts/0    00:00:00         test2
 5396  5393  4394 pts/0    00:00:01           test3

  |-gnome-keyring-d --daemonize --login
  |   `-4*[{gnome-keyring-}]
  |-gnome-screensav --no-daemon
  |   `-2*[{gnome-screensa}]
  |-gnome-terminal
  |   |-bash
  |   |   `-pstree -all
  |   |-bash
  |   |-bash
  |   |   `-test
  |   |       |-test1 Hello World!
  |   |       `-test2 Hello World!
  |   |           `-(test3)
  |   |-gnome-pty-helpe
  |   `-3*[{gnome-terminal}]
 

结果分析:

由进程树可以看到test3是test2的子进程,test2是test的子进程。其组ID都是test的PID。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值