ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux 进程环境 ---- 进程组、会话、前台进程组、后台进程组、终端控制

2019-07-21 10:38:52  阅读:155  来源: 互联网

标签:调用 pid 会话 终端 Linux 进程 ID


进程组与会话的相关概念:

    进程组是一组相关关进程的集合,会话是一組相关进程组的集合。进程都有父进程,父进程也有父进程,这就形成了一个以ini进程为根的家族树。除此以外,进程还有其他乘次关系:进程、进程组、会话。进程组合会话在进程之前形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。
    这样说来,一个进程会有如下ID:
    .PID:进程的唯一标识。对于多线程的进程而言所有线程调用getpid()函数会返回相同值。
    .PGID:进程组ID。每个进程都会有进程组ID,表示该进程所属的进程组。默认情况下新创建的进程会继承父进程的进程组ID。
    .SID:会话ID。每个进程也都有会话ID。默认情况下,新创建的进程会继承父进程的会话ID
    
    可以调用如下指令来查看所有进程的层次关系:
    ps axjf

获取进程组ID与会话ID的方式:
    pid_t getpgrp(void);
    pid_t getsid(pid_t pid);

    前面前面提到过,新进程默认继承父进程的进程组ID和会话ID,如果都是默认情况的话,那么追根溯源可知,所有的进程应该有共同的进程组ID和会话ID。但是调用ps axjf可以看到,实际情况并非如此,系统中存在很多不同的会话,每个会话下也有不同的进程组。
为何会如此呢?
    就像家族企业一样,如果从创业之初,所有家族成员都墨守成规,循规蹈矩,默认情况下,就只会有一个公司、一个部门。但是也有些“叛逆”的子弟,愿意为家族公司开疆拓土,愿意成立新的部门。这些新的部门就是新创建的进程组。如果有子弟“离经叛道”,甚至不愿意呆在家族公司里,他别开天地,另创了一个公司,那这个新公司就是新创建的会话组。由此可见,系统必须要有改变和设置进程组ID和会话ID的函数接口,否则,系统中只会存在一个会话、一个进程组。

修改进程组ID的接口如下:

    int setpgid(pid_t pid, pid_t pgid);
    这个函数的含义是,找到进程ID为pid的进程,将其进程组ID修改为pgid,如果pid的值为0,则表示要修改调用进程的进程组ID。该接口一般用来创建一个新的进程组。
    setpgid(0,0)
    setpgid(getpid(),0)
    setpgid(getpid(),getpid()) 
    以上三个接口含义一致,都是创立新的进程组,并且指定的进程会成为进程组的首进程。如果参数pid和pgid的值不匹配,那么setpgid函数会将一个进程从原来所属的进程组迁移到pgid对应的进程组。

    setpgid函数有很多限制:
   1、 ·pid参数必须指定为调用setpgid函数的进程或其子进程,不能随意修改不相关进程的进程组ID,如果违反这条规则,则返回-1,并置errno为ESRCH。
   2、 ·pid参数可以指定调用进程的子进程,但是子进程如果已经执行了exec函数,则不能修改子进程的进程组ID。如果违反这条规则,则返回-1,并置errno为EACCESS。
    3、·在进程组间移动,调用进程,pid指定的进程及目标进程组必须在同一个会话之内。这个比较好理解,不加入公司(会话),就无法加入公司下属的部门(进程组),否则就是部门要造反的节奏。如果违反这条规则,则返回-1,并置errno为EPERM。
    4、pid指定的进程,不能是会话首进程。如果违反这条规则,则返回-1,并置errno为EPERM。

有了创建进程组的接口,新创建的进程组就不必继承父进程的进程组ID了。最常见的创建进程组的场景就是在shell中执行管道命令,代码如下:cmd1 | cmd2 | cmd3
    下面用一个最简单的命令来说明,其进程之间的关系如图4-2所示。
    ps ax|grep nfsd 

    ps进程和grep进程都是bash创建的子进程,两者通过管道协同完成一项工作,它们隶属于同一个进程组,其中ps进程是进程组的组长。
    引入进程组的概念,是为了可以更方便的管理这一组进程。比如这项工作放弃了,不必向每个进程一一发送信号,可以直接将信号发送给进程组,进程组内所有进程都会收到该信号。
    前文曾提到过,子进程一旦执行exec,父进程就无法调用setpgid函数来设置子进程的进程组ID了,这条规则会影响shell的作业控制。出于保险的考虑,一般父进程在调用fork创建子进程后,会调用setpgid函数设置子进程的进程组ID,同时子进程也要调用setpgid函数来设置自身的进程组ID。这两次调用有一次是多余的,但是这样做能够保证无论是父进程先执行,还是子进程先执行,子进程一定已经进入了指定的进程组中。由于fork之后,父子进程的执行顺序是不确定的,因此如果不这样做,就会造成在一定的时间窗口内,无法确定子进程是否进入了相应的进程组。


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


前台进程与后台进程:

     用户在shell中可以同时执行多个命令。对于耗时很久的命令(如编译大型工程),用户不必傻傻等待命令运行完毕才执行下一个命令。用户在执行命令时,可以在命令的结尾添加“&”符号,表示将命令放入后台执行。这样该命令对应的进程组即为后台进程组。在任意时刻,可能同时存在多个后台进程组,但是不管什么时候都只能有一个前台进程组。只有在前台进程组中进程才能在控制终端读取输入。当用户在终端输入信号生成终端字符(如ctrl+c、ctrl+z、ctr+\等)时,对应的信号只会发送给前台进程组。
     shell中可以存在多个进程组,无论是前台进程组还是后台进程组,它们或多或少存在一定的联系,为了更好地控制这些进程组(或者称为作业),系统引入了会话的概念。会话的意义在于将很多的工作囊括在一个终端,选取其中一个作为前台来直接接收终端的输入及信号,其他的工作则放在后台执行。


会话:

    由于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话。一个会话包含多个进程组,但是只能有一个前台进程组。每个会话都有一个会话首领(leader),即创建会话的进程sys_setsid()/setsid()调用能创建一个会话。单必须注意的是,只有当前进程不是进程组组长时,才能创建一个新的会话。调用setsid之后,该进程成为会话的leader。一个会话只能有一个控制终端。这通常是登录到其上的终端设备(在终端登录情况下)或者伪终端设备在网络登录情况下。建立与控制终端连接的会话被称为控制进程一个会话中的几个进程组可以分为前台进程组与后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组与任意多个后台进程组。


创建会话的接口:

    #include <unistd.h>
    pid_t setsid(void);
    如果这个函数的调用进程不是进程组组长,那么调用该函数会发生以下事情:
    1)创建一个新会话,会话ID等于进程ID,调用进程成为会话的首进程。
    2)创建一个进程组,进程组ID等于进程ID,调用进程成为进程组的组长。
    3)该进程没有控制终端,如果调用setsid前,该进程有控制终端,这种联系就会断掉。

    调用setsid函数的进程不能是进程组的组长,否则调用会失败,返回-1,并置errno为EPERM。这个限制是比较合理的。如果允许进程组组长迁移到新的会话,而进程组的其他成员仍然在老的会话中,那么,就会出现同一个进程组的进程分属不同的会话之中的情况,这就破坏了进程组和会话的严格的层次关系了。


终端控制:

    会话的领头进程打开一个终端后,该终端就会成为该会话的控制终端(SVR4/linux)
    与控制终端建立连接的会话领头进程成为控制进程(session leader)
    一个会话只能有一个控制终端,
    产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程
    终端上的连接断开时(比如网络断开或Modem断开),挂起信号将发送到控制进程(session leader)

综上:
    进程属于一个进程组,进程组属于一个会话,会话可能有也可能没有控制终端。一般而言,当用户在某个终端上登录时,一个新的会话就开始了。进程组由组中的领头进程标识,领头进程的进程标识符就是进程组的组 标识符。类似的,每个会话也有对应一个领头进程。同一会话中的进程通过该会话的领头进程和一个终端相连,该终端作为这个会话的控制终端。
    一个会话只有一个控制终端,而一个控制终端只能控制一个会话。用户通过控制终端,可以向控制终端所控制的会话中的进程发送键盘信号。
    同一个会话中只能有一个前台进程组,属于前台进程组的进程可以从控制终端获得输入,而其他进程均是后台进程,可能分属于不同的后台进程组。
    当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都有自己的前台工作和后台工作

 

以上只是点均摘抄自网络,如若侵权,请私信联系删除

 

标签:调用,pid,会话,终端,Linux,进程,ID
来源: https://blog.csdn.net/huangan_xixi/article/details/96699445

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有