设为首页 收藏本站
查看: 736|回复: 0

[经验分享] linux 进程组 作业 会话

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-5-12 09:21:21 | 显示全部楼层 |阅读模式
1. 进程组
      每个进程除了有一个进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合。
      通常,它们与同一作业相关联,可以接收来自同一终端的各种信号。 每个进程组有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程的标识是其进程组ID等于其进程ID。组长进程可以创建一个进程组,创建该组中的进程,然后终止。 只要在某个进程组中一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。
2.作业
     Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group) 。一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成, Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。
     作业与进程组的区别:如果作业中的某个进程又创建了一个进程,则改进程不属于作业。一旦作业运行结束, Shell就把自己提到前台,如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。
3.会话
     会话(Session)是一个或多个进程组的集合。
     一个会话可以有一个控制终端。 这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。 建立与控制终端连接的会话首进程被称为控制进程。一个会话中的一个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。
     1  $ proc1 | proc2 &  
     2  $ proc3 | proc4 | proc5   
     其中proc1与proc2属于同一个后台进程组, proc3, proc4和proc5属于同一个前台进程组,
     Shell本来属于一个单独的进程组。这些进程组的控制终端相同,它们同属于一个会话,当用户在控制终端输入特殊的控制键(如Ctrl+C,产生SIGINT,Ctrk+\,产生SIGQUIT,Ctrl+Z,产生SIGTSTP), 内核发送相应的信号给前台进程组中的所有进程。
                                                       终端
       A. 终端的基本概念
          在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端 (Controlling Terminal),控制终端是保存在PCB中的信息,而我们知 道fork会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端。默认情况 下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准 输入读也就是读用户的键盘输人,进程往标准输出或标准错误输出写也就是输出到显器上。此外 在控制终端输入一些特殊的控制键可以给前台进程发信号,例如Ctrl-C表 示SIGINT,Ctrl-\表示SIGQUIT。每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终 端。事实上每个终端设备都对应一个不同的设备件,/dev/tty提供了一个通用的接 ,一个进程 要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访 问。 ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不 能是任意文件。下面我们通过实验看以下各种不同的终端所对应的设备文件名。
查看终端对应的设备
1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
#include<unistd.h>

int main()
{
    printf("fd:%d->%s\n",0,ttyname(0));
    printf("fd:%d->%s\n",1,ttyname(1));
    printf("fd:%d->%s\n",2,ttyname(2));
    return 0;
}



终端1结果:

wKiom1cyu1CzFchdAAANqYCcIX8702.jpg

重新开一个终端结果:
wKiom1cyu1Cixe-7AAAHHR0MG5g623.jpg
再重新起一个终端结果:
wKioL1cyvDDxyqpbAAAP1TpPJ5s608.jpg
再字符终端下运行:
QQ截图20160512092027.png
        B. 终端登录过程
         一台PC通常只有一套键盘和显示器,也就是只有一套终端设备,但是可以通过Ctrl-Alt-F1~Ctrl-Alt- F6切换到6个字符终端,相当于有6套虚拟的终端设备,它们共用同一套物理终端设备,对应的设备 文件分别是/dev/tty1~/dev/tty6,所以称为虚拟终端(Virtual Terminal)。设备文件/dev/tty0表 示当前虚拟终端,比如切换到Ctrl-Alt-F1的字符终端时/dev/tty0就表示/dev/tty1,切换到Ctrl-Alt- F2的字符终端时/dev/tty0就表示/dev/tty2,就像/dev/tty一样也是一个通用的接 ,但它不能表 示图形终端窗所对应的终端。
        再举个例子,做嵌入式开发时经常会用到串终端,目标板的每个串对应一个终端设备,比如/dev/ttyS0、 /dev/ttyS1等,将主机和目标板用串线连起来,就可以在主机上通 过Linux的minicom或Windows的超级终端工具登录到目标板的系统。内核中处理终端设备的模块包括硬件驱动程序和线路规程(Line Discipline)。
QQ截图20160512092032.png
硬件驱动程序负责读写实际的硬件设备,比如从键盘读取字符和把字符输出到显示器,线路规程像 一个过滤器,对于某些特殊字符并不是让它直接通过,而是做特殊处理,比如在键盘上按下Ctrl- Z,对应的字符并不会被用户程序的read读到,而是被线路规程截获,解释成SIGTSTP信号发给前台进程,通常会使该进程停止。线路规程应该过滤哪些字符和做哪些特殊处理是可以配置的。
终端设备有输入和输出队列缓冲区,如下图所示。
wKioL1cywRyS7MXSAACURUQhVhE513.jpg
以输入队列为例,从键盘输入的字符经线路规程过滤后进入输入队列,用户程序以先进先出的顺序 从队列中读取字符,一般情况下,当输入队列满的时候再输入字符会丢失,同时系统会响铃警报。 终端可以配置成回显(Echo)模式,在这种模式下,输入队列中的每个字符既送给用户程序也送给 输出队列,因此我们在命令行键入字符时,该字符不仅可以被程序读取,我们也可以同时在屏幕上 看到该字符的回显。现在我们来看终端登录的过程:
    1、系统启动时,init进程根据配置文件/etc/inittab确定需要打开哪些终端。例如配置文件中有这样一行:
    1:2345:respawn:/sbin/getty 9600 tty1和/etc/passwd类似,每个字段用 : 号隔开。开头的1是这一行配置的id,通常要和tty的后缀一致,配置tty2的那一行id就应该是2。第二个字段2345表示运行级别2~5都执行这个配置。最后一个字 段/sbin/getty 9600 tty1是init进程要fork/exec的命令,打开终端/dev/tty1,波特率 是9600(波特率只对串和Modem终端有意义),然后提示用户输入帐号。中间的respawn字段表示init进程会监视getty进程的运行状态,一旦该进程终止,init会再次fork/exec这个命令,所以 我们从终端退出登录后会再次提示输入帐号。有些新的Linux发行版已经不用/etc/inittab这个配置文件了,例如Ubuntu用/etc/event.d目录下的配置文件来配置init。
    2、 getty根据命令行参数打开终端设备作为它的控制终端,把文件描述符0、 1、 2都指向控制终 端,然后提示用户输入帐号。用户输入帐号之后,getty的任务就完成了,它再执行login程序:
        execle("/bin/login", "login", "-p", username, NULL, envp);
    3、 login程序提示用户输入密码(输入密码期间关闭终端的回显),然后验证帐号密码的正确性。 如果密码不正确,login进程终止,init会重新fork/exec一个getty进程。如果密码正确,login程 序设置一些环境变量,设置当前工作目录为该用户的主目录,然后执行Shell:
        execl("/bin/bash", "-bash", NULL);
    注意:argv[0]参数的程序名前面加了一个  - ,这样bash就知道自己是作为登录Shell启动的,执行登录Shell的启动脚本。 从getty开始exec到login,再exec到bash,其实都是同一个进程,因此控制终 端没变,文件描述符0、 1、 2也仍然指向控制终端。由于fork会复制PCB信息,所以由Shell启动的 其它进程也都是如此。
               
            作业控制
2.1. Session与进程组
     “Shell可以同时运行一个前台进程和任意多个后台进程”其实是 不全对的,现在我们来研究更复杂的情况。事实上,Shell分前后台来控制的不是进程而是作业 (Job)或者进程组(Process  Group)。 一个前台作业可以由多个进程组成,一个后台作业也可以 由多个进程组成,Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)。
        例如用以下命令启动5个进程(这个例子出自 [APUE2e] ):
            $ proc1 | proc2 &
            $ proc3 | proc4 | proc5
        其中proc1和proc2属于同一个后台进程组,proc3、 proc4、 proc5属于同一个前台进程组,Shell进程本来属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个Session。当用户在 控制终端输入特殊的控制键(例如Ctrl-C)时,内核会发送相应的信号(例如SIGINT)给前台进程 组的所有进程。
        各进程、进程组、 Session的关系如下图所示。
wKioL1cyxffhlQ3eAACk8unrczE374.jpg
        现在我们从Session和进程组的角度重新来看登录和执行命令的过程。
            1. getty或telnetd进程在打开终端设备之前调用setsid函数创建一个新的Session,该进程称 为Session Leader,该进程的id也可以看作Session的id,然后该进程打开终端设备作为这 个Session中所有进程的控制终端。在创建新Session的同时也创建了一个新的进程组,该进程是这个进程组的Process Group Leader,该进程的id也是进程组的id。
            2. 在登录过程中,getty或telnetd进程变成login,然后变成Shell,但仍然是同一个进程,仍然是Session Leader。
            3. 由Shell进程fork出的子进程本来具有和Shell相同的Session、进程组和控制终端,但 是Shell调用setpgid函数将作业中的某个子进程指定为一个新进程组的Leader,然后调 用setpgid将该作业中的其它子进程也转移到这个进程组中。如果这个进程组需要在前台运 行,就调用tcsetpgrp函数将它设置为前台进程组,由于一个Session只能有一个前台进程 组,所以Shell所在的进程组就自动变成后台进程组。 在上面的例子中,proc3、 proc4、 proc5被Shell放到同一个前台进程组,其中有一个进程是该进程组的Leader,Shell调用wait等待它们运行结束。一旦它们全部运行结束,Shell就调 用tcsetpgrp函数将自己提到前台继续接受命令。 但是注意,如果proc3、 proc4、 proc5中的 某个进程又fork出子进程,子进程也属于同一进程组,但是Shell并不知道该进程的存在,也不会调用wait等待它结束。 换句话说,proc3 | proc4 | proc5是Shell的作业,而这个子进程 不是,这是作业和进程组在概念上的区别。一旦作业运行结束,Shell就把自己提到前台,如 果原来的前台进程组还存在(如果这个子进程还没终止),则它自动变成后台进程组下面看两个例子。
                $ ps -o pid,ppid,pgrp,session,tpgid,comm | cat
                 PID PPID PGRP SESS TPGID COMMAND
                 6994 6989 6994 6994 8762 bash
                 8762 6994 8762 6994 8762 ps
                 8763 6994 8762 6994 8762 cat
            这个作业由ps和cat两个进程组成,在前台运行。从PPID列可以看出这两个进程的父进程是bash。 从PGRP列可以看出,bash在id为6994的进程组中,这个id等于bash的进程id,所以它是进程组 的Leader,而两个子进程在id为8762的进程组中,ps是这个进程组的Leader。从SESS可以看出三个 进程都在同一Session中,bash是Session Leader。从TPGID可以看出,前台进程组的id是8762,也 就是两个子进程所在的进程组。
                ps -o pid,ppid,pgrp,session,tpgid,comm | cat &
                [1] 8835
                $ PID PPID PGRP SESS TPGID COMMAND
                6994 6989 6994 6994 6994 bash
                8834 6994 8834 6994 6994 ps
                8835 6994 8834 6994 6994 cat
         这个作业由ps和cat两个进程组成,在后台运行,bash不等作业结束就打印提示信息[1] 8835然后给出提示符接受新的命令,[1]是作业的编号,如果同时运行多个作业可以用这个编号区分,8835是该作业中某个进程的id。
2.2. 与作业控制有关的信号
        我们通过实验来理解与作业控制有关的信号。
         wKioL1cyyrniyLe_AAAH2qYeYjc633.jpg
        将cat放到后台运行,由于cat需要读标准输入(也就是终端输入),而后台进程是不能读终端输入的,因此内核发SIGTTIN信号给进程,该信号的默认处理动作是使进程停止。
wKiom1cyzF-xSJrhAAAQvQk7viQ095.jpg       
          jobs 命令可以查看当前有哪些作业。 fg命令可以将某个作业提至前台运行,如果该作业的进程组正在后台运行则提至前台运行,如果该作业处于停止状态,则给进程组的每个进程发SIGCONT信号使它继续运行。参数%1表示将第1个作业提至前台运行。 cat提示前台运行后,挂起等待终端输入,当 输入i'm very cool!并回车后,cat打印出同样的消息,然后继续挂起等待输入。如果输入Ctrl-Z则向所有前 台进程发SIGTSTP信号,该信号的默认动作是使进程停止, cat继续以后台作业的形式存在。
wKiom1cy0uiDreNPAAAK7mYLS_E798.jpg
bg命令可以让某个停止的作业在后台继续运行,也需要给该作业的进程组的每个进程发SIGCONT信号。 cat进程继续运行,又要读终端输入,然而它在后台不能读终端输入,所以又收到SIGTTIN信号 而停止。

wKioL1cy1mzAY6oBAAAbTYpp2x8679.jpg
kill命令给一个停止的进程发SIGTERM信号,这个信号并不会立刻处理,而要等进程准备继续运行之前处理,默认动作是终止进程。但如果给一个停止的进程发SIGKILL信号就不同了。
SIGKILL信号既不能被阻塞也不能被忽略,也不能自己定义函数捕捉,只能按系统的默认动作立刻处理。与此类似的还有SIGSTOP信号,给一个进程发SIGSTOP信号会使进程停止,这个默认的处理动 作不能改变。这样保证了不管什么样的进程都能用SIGKILL终止或者用SIGSTOP停止,当系统出现异 常时管理员总是有办法杀掉有问题的进程或者暂时停掉怀疑有问题的进程。上面讲了如果后台进程试图从控制终端读,会收到SIGTTIN信号而停止,如果试图向控制终端写 呢?通常是允许写的。如果觉得后台进程向控制终端输出信息干扰了用户使用终端,可以设置一个 终端选项禁止后台进程写。
wKioL1cy5ZHy59hGAAGk8QQlssc893.jpg 首先用stty命令设置终端选项,禁止后台进程写,然后启动一个后台进程准备往终端写,
wKioL1cy5d2D9aP4AAGTlGNgOrU138.jpg
这时进程 收到一个SIGTTOU信号,默认处理动作也是停止进程。 紧接着放置前台, cat进程运行完成,退出。
             守护进程
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。
Linux的大多数服务器就是用守护进程实现的。比如, Internet服务器inetd, Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等
Linux系统启动时会启动很多系统服务进程,例 “网络登录过程”的inetd,这些系统服 务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。
下面我们用ps axj命令查看系统中的进程。
QQ截图20160512092047.png
参数a表不仅列当前户的进程,也列出所有其他用 户的进程,参数x表示不仅列有控制终端的进程,也列出所有的控制终端的进程,参数j表示列出与 作业控制相关的信息。凡是TPGIDy栏i写着-1的都是没有控制终端的进程,也就是守护进程。
COMMAND列[]括起来的 名字表示内核线程,这些线程在内核创建,没有户空间代码,因此没有程序件名和命令, 通常采以k开头的名字,表Kernel。 init进程我们已经很熟悉了,udevd负责维护/dev录下的 设备件,acpid负责电源管理,syslogd负责维护/var/log下的志件,可以看出,守护进程通 常采以d结尾的名字,表Daemon。我的centos中部分daemon进程创建守护进程最关键的步是调setsid函数创建个新的Session,并成为Session Leader。
wKioL1czBjvjw3UzAABR1hh2KYg500.jpg
该函数调成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进 程组的Leader也很容易,只要先fork再调setsid就了。 fork创建的进程和进程在同个进 程组中,进程组的Leader必然是该组的第个进程,所以进程不可能是该组的第个进程,在 进程中调setsid就不会有问题了。成功调该函数的结果是:
        1. 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
        2. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
        3. 如果当前进程原本有个控制终端,则它失去这个控制终端,成为个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是个普通的打开件不是控制终端了。
创建守护进程:
        1. 调umask将件模式创建屏蔽字设置为0.
        2. 调fork,进程退出(exit) 。原因: 1)如果该守护进程是作为条简单的shell命令启动的,那么进程终使得shell认为该命令已经执完毕。 2)保证进程不是个进程组的组长进程。

        3. 调setsid创建个新会话。 setsid会导致: 1)调进程成为新会话的进程。  2)调进程成为个进程组的组长进程 。 3)调进程没有控制终端。(再次fork次,保证daemon进程,之后不会打开tty设备)
        4. 将当前作录更改为根录。
        5. 关闭不在需要的件描述符。
        6. 其他:忽略SIGCHLD信号。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-215873-1-1.html 上篇帖子: MongoDB在CentOS下的安装、启动和配置 下篇帖子: Centos 5.X常见服务优化与说明v 1.0 linux
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表