Linux中的进程关系详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangyifei216/article/details/49705515

进程组的概念

​   每一个进程除了有一个进程ID之外,还属于一个进程组,进程组通常是一个或多个进程的集合。这些进程通常是与一个作业相关的。例如:ps axu|grep bash|wc -l 这是三个进程,他们直接通过管道传递数据,为了是完成一个作业,对于这个整体来说是一个进程组,其中ps进程是进程组的组长进程。进程组也是由一个id来标识进程组的,通过使用PGID来标识,然而这个PGID==进程组组长进程的PID的使用下面的这个命令来验证这一事实:

[root@localhost common]# ps -o pid,ppid,pgid,sid,comm|more
  PID  PPID  PGID   SID COMMAND
 2445  2441  2445  2445 bash
 3882  2445  3882  2445 ps
 3883  2445  3882  2445 more
 ps 是其中的第一个进程,也是这个进程组的组长进程,那么其
 PID==PGID==2882 more和ps是同一个进程组的,因为其PGID相同

相关的系统API

下面这两个方法是用来设置和获取进程组的方法

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
如果pid等于pgid,那么pid就成为了其所属的进程组的组长进程,
如果pid等于0,则标识把当前进程设置为pgid的组长进程
如果pgid等于0,则使用pid作为目标pgid
pid_t getpgid(pid_t pid); //这个方法的使用很简单,这里不做介绍

下面是一个测试的例子,在这个例子显示出一个只有单个进程的进程组

#include <unistd.h>
#include <stdio.h>
int main()
{
        printf("%d\n",getpgid(getpid()));
        while(1);
}
测试结果如下: a.out属于4082这个进程组的,并且是组长进程
[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm a
  PID  PPID  PGID   SID COMMAND
 1698   815  1698  1698 bash
 2445  2441  2445  2445 bash
 4082  2445  4082  2445 a.out
 4087  4083  4087  4087 bash
 4206  4087  4206  4087 ps

​   可以看出正常情况下,直接运行的进程通常是一个新进程组的组长进程,与这个进程有通信的,或者是这个进程派生的都属于这个进程组.

会话的概念

​   会话就是一些有关联的进程组的集合而已.概念上来说其实不是很难理解,下面是一个展示会话其含义的例子:

[root@localhost ~]#  ps -o pid,ppid,pgid,sid,comm|more
  PID  PPID  PGID   SID COMMAND
 4087  4083  4087  4087 bash
 4521  4087  4521  4087 ps
 4522  4087  4521  4087 more

​&emsp; &emsp;再次使用这个例子,ps 和more进程是一个进程组,bash进程属于另外一个进程组,bash执行了ps和more进程,所以这两个进程组存在关联,属于一个会话。其中SID代表这个会话的ID,从上面的结果可以看书,ps more bash是两个进程组的,但是都是一个会话的。会话ID,为首领进程组的组长进程的PID

相信通过上面的这个例子,你应该对会话有了一个新的概念了吧.

相关的系统API

用于设置和获取session id的API

#include <unistd.h>
pid_t getsid(pid_t pid); //获取指定进程的sid
pid_t setsid(void);
1.不能由进程组的组长进程调用,否则会产生错误
2.非进程组的组长进程调用将会创建会话并且会产生下面的效果:
    1.调用进程成为会话的首领,此时该进程是新会话的唯一成员
    2.新建一个进程组,其PGID为其PID,该进程成为该组的组长进程
    3.调用进程讲脱离终端(守护进程中常用)

一个错误使用setsid的例子

#include <unistd.h>
#include <stdio.h>
int main()
{
        setsid();
        perror("setsid error");
        while(1);
}
setsid error: Operation not permitted
[root@localhost ~]#  ps -o pid,ppid,pgid,sid,comm a
  PID  PPID  PGID   SID COMMAND
 1698   815  1698  1698 bash
 2445  2441  2445  2445 bash
 4087  4083  4087  4087 bash
 4960  2445  4960  2445 a.out
 4961  4087  4961  4087 ps

​   上面的例子会执行失败,其执行失败的原因在于执行这个程序后,这个程序是一个进程组的组长进程,组长进程是无法调用setsid的.下面的例子是一个正确使用setsid的例子:

#include <unistd.h>
#include <stdio.h>
int main()
{
        if(fork > 0)
                //父进程等待
                while(1);
        else{
        //子进程,创建会话,并创建一个新的进程组成为组长进程
                setsid();
                while(1)
        }
}
 PID  PPID  PGID   SID COMMAND
 5070  2445  5070  2445 a.out
 5071  5070  5071  5071 a.out

​   上面的例子中通过在子进程中调用setsid成功创建了一个新的会话,和新的进程组,并且成为了新进程组的组长进程以及会话的领导进程组.子进程之所以可以成功创建会话是因为父进程是进程组的组长进程,子进程只是属于这个进程组中的一个进程而已,除此之外什么也不是,所以这个子进程具备setsid的使用条件.

会话和终端的关系

  • 一个会话可以有一个控制终端(controlling terminal)。
  • 建立与控制终端连接的会话首进程被称为控制进程(controlling process)。
  • 一个会话中的几个进程组可被分成一个前台进程组(forkground process group)和几个后台进程组(background process group)。
  • 如果一个会话有一个控制终端,则它有一个前台进程组。
  • 无论何时键入终端的中断键(DELETE或Ctrl+C),就会将中断信号发送给前台进程组的所有进程。
  • 无论何时键入终端的退出键(Ctrl+),就会将退出信号发送给前台进程组的所有进程。
  • 如果终端检测到调制解调器(或网络)已经断开连接,则将挂断信号发送给控制进程(会话首进程)。

没有更多推荐了,返回首页