linux之進(jìn)程的基本概念(進(jìn)程,進(jìn)程組,會(huì)話關(guān)系)
進(jìn)程
進(jìn)程是操作系統(tǒng)的一個(gè)核心概念。每個(gè)進(jìn)程都有自己唯一的標(biāo)識(shí):進(jìn)程ID,也有自己的生命周期。一個(gè)典型的進(jìn)程的生命周期如圖4-1所示。
進(jìn)程組和會(huì)話在進(jìn)程之間形成了兩級(jí)的層次:進(jìn)程組是一組相關(guān)進(jìn)程的集合,會(huì)話是一組相關(guān)進(jìn)程組的集合。
這樣說來,一個(gè)進(jìn)程會(huì)有如下ID:
·PID:進(jìn)程的唯一標(biāo)識(shí)。對(duì)于多線程的進(jìn)程而言,所有線程調(diào)用getpid函數(shù)會(huì)返回相同的值。
·PGID:進(jìn)程組ID。每個(gè)進(jìn)程都會(huì)有進(jìn)程組ID,表示該進(jìn)程所屬的進(jìn)程組。默認(rèn)情況下新創(chuàng)建的進(jìn)程會(huì)繼承父進(jìn)程的進(jìn)程組ID。
·SID:會(huì)話ID。每個(gè)進(jìn)程也都有會(huì)話ID。默認(rèn)情況下,新創(chuàng)建的進(jìn)程會(huì)繼承父進(jìn)程的會(huì)話ID。
可以調(diào)用如下指令來查看所有進(jìn)程的層次關(guān)系:
ps -ejH
ps axjf
進(jìn)程,可以調(diào)用以下函數(shù)獲取進(jìn)程組ID跟會(huì)話ID.pid_t getpgrp(void);
pid_t getsid(pid_t pid);
前面提到過,新進(jìn)程默認(rèn)繼承父進(jìn)程的進(jìn)程組ID和會(huì)話ID,如果都是默認(rèn)情況的話,那么追根溯源可知,所有的進(jìn)程應(yīng)該有共同的進(jìn)程組ID和會(huì)話ID。但是調(diào)用ps axjf可以看到,實(shí)際情況并非如此,系統(tǒng)中存在很多不同的會(huì)話,每個(gè)會(huì)話下也有不同的進(jìn)程組。為何會(huì)如此呢?
就像家族企業(yè)一樣,如果從創(chuàng)業(yè)之初,所有家族成員都墨守成規(guī),循規(guī)蹈矩,默認(rèn)情況下,就只會(huì)有一個(gè)公司、一個(gè)部門。但是也有些“叛逆”的子弟,愿意為家族公司開疆拓土,愿意成立新的部門。這些新的部門就是新創(chuàng)建的進(jìn)程組。如果有子弟“離經(jīng)叛道”,甚至不愿意呆在家族公司里,他別開天地,另創(chuàng)了一個(gè)公司,那這個(gè)新公司就是新創(chuàng)建的會(huì)話組。由此可見,系統(tǒng)必須要有改變和設(shè)置進(jìn)程組ID和會(huì)話ID的函數(shù)接口,否則,系統(tǒng)中只會(huì)存在一個(gè)會(huì)話、一個(gè)進(jìn)程組。
進(jìn)程組和會(huì)話是為了支持shell作業(yè)控制而引入的概念。
當(dāng)有新的用戶登錄Linux時(shí),登錄進(jìn)程會(huì)為這個(gè)用戶創(chuàng)建一個(gè)會(huì)話。用戶的登錄shell就是會(huì)話的首進(jìn)程。會(huì)話的首進(jìn)程ID會(huì)作為整個(gè)會(huì)話的ID。會(huì)話是一個(gè)或多個(gè)進(jìn)程組的集合,囊括了登錄用戶的所有活動(dòng)。
在登錄shell時(shí),用戶可能會(huì)使用管道,讓多個(gè)進(jìn)程互相配合完成一項(xiàng)工作,這一組進(jìn)程屬于同一個(gè)進(jìn)程組。
當(dāng)用戶通過SSH客戶端工具(putty、xshell等)連入Linux時(shí),與上述登錄的情景是類似的。
進(jìn)程組
修改進(jìn)程組ID的接口如下int setpgid(pid_t pid, pid_t pgid);
這個(gè)函數(shù)的含義是,找到進(jìn)程ID為pid的進(jìn)程,將其進(jìn)程組ID修改為pgid,如果pid的值為0,則表示要修改調(diào)用進(jìn)程的進(jìn)程組ID。該接口一般用來創(chuàng)建一個(gè)新的進(jìn)程組。下面三個(gè)接口含義一致,都是創(chuàng)立新的進(jìn)程組,并且指定的進(jìn)程會(huì)成為進(jìn)程組的首進(jìn)程。如果參數(shù)pid和pgid的值不匹配,那么setpgid函數(shù)會(huì)將一個(gè)進(jìn)程從原來所屬的進(jìn)程組遷移到pgid對(duì)應(yīng)的進(jìn)程組。
setpgid(0,0)
setpgid(getpid(),0)
setpgid(getpid(),getpid())
setpgid函數(shù)有很多限制:·pid參數(shù)必須指定為調(diào)用setpgid函數(shù)的進(jìn)程或其子進(jìn)程,不能隨意修改不相關(guān)進(jìn)程的進(jìn)程組ID,如果違反這條規(guī)則,則返回-1,并置errno為ESRCH。
·pid參數(shù)可以指定調(diào)用進(jìn)程的子進(jìn)程,但是子進(jìn)程如果已經(jīng)執(zhí)行了exec函數(shù),則不能修改子進(jìn)程的進(jìn)程組ID。如果違反這條規(guī)則,則返回-1,并置errno為EACCESS。
·在進(jìn)程組間移動(dòng),調(diào)用進(jìn)程,pid指定的進(jìn)程及目標(biāo)進(jìn)程組必須在同一個(gè)會(huì)話之內(nèi)。這個(gè)比較好理解,不加入公司(會(huì)話),就無法加入公司下屬的部門(進(jìn)程組),否則就是部門要造反的節(jié)奏。如果違反這條規(guī)則,則返回-1,并置errno為EPERM。
·pid指定的進(jìn)程,不能是會(huì)話首進(jìn)程。如果違反這條規(guī)則,則返回-1,并置errno為EPERM。
有了創(chuàng)建進(jìn)程組的接口,新創(chuàng)建的進(jìn)程組就不必繼承父進(jìn)程的進(jìn)程組ID了。最常見的創(chuàng)建進(jìn)程組的場景就是在shell中執(zhí)行管道命令,代碼如下:cmd1 | cmd2 | cmd3
下面用一個(gè)最簡單的命令來說明,其進(jìn)程之間的關(guān)系如圖4-2所示。
ps ax|grep nfsd
ps進(jìn)程和grep進(jìn)程都是bash創(chuàng)建的子進(jìn)程,兩者通過管道協(xié)同完成一項(xiàng)工作,它們隸屬于同一個(gè)進(jìn)程組,其中ps進(jìn)程是進(jìn)程組的組長。
進(jìn)程組的概念并不難理解,可以將人與人之間的關(guān)系做類比。一起工作的同事,自然比毫不相干的路人更加親近。shell中協(xié)同工作的進(jìn)程屬于同一個(gè)進(jìn)程組,就如同協(xié)同工作的人屬于同一個(gè)部門一樣。
引入了進(jìn)程組的概念,可以更方便地管理這一組進(jìn)程了。比如這項(xiàng)工作放棄了,不必向每個(gè)進(jìn)程一一發(fā)送信號(hào),可以直接將信號(hào)發(fā)送給進(jìn)程組,進(jìn)程組內(nèi)的所有進(jìn)程都會(huì)收到該信號(hào)。
前文曾提到過,子進(jìn)程一旦執(zhí)行exec,父進(jìn)程就無法調(diào)用setpgid函數(shù)來設(shè)置子進(jìn)程的進(jìn)程組ID了,這條規(guī)則會(huì)影響shell的作業(yè)控制。出于保險(xiǎn)的考慮,一般父進(jìn)程在調(diào)用fork創(chuàng)建子進(jìn)程后,會(huì)調(diào)用setpgid函數(shù)設(shè)置子進(jìn)程的進(jìn)程組ID,同時(shí)子進(jìn)程也要調(diào)用setpgid函數(shù)來設(shè)置自身的進(jìn)程組ID。這兩次調(diào)用有一次是多余的,但是這樣做能夠保證無論是父進(jìn)程先執(zhí)行,還是子進(jìn)程先執(zhí)行,子進(jìn)程一定已經(jīng)進(jìn)入了指定的進(jìn)程組中。由于fork之后,父子進(jìn)程的執(zhí)行順序是不確定的,因此如果不這樣做,就會(huì)造成在一定的時(shí)間窗口內(nèi),無法確定子進(jìn)程是否進(jìn)入了相應(yīng)的進(jìn)程組。
用戶在shell中可以同時(shí)執(zhí)行多個(gè)命令。對(duì)于耗時(shí)很久的命令(如編譯大型工程),用戶不必傻傻等待命令運(yùn)行完畢才執(zhí)行下一個(gè)命令。用戶在執(zhí)行命令時(shí),可以在命令的結(jié)尾添加“&”符號(hào),表示將命令放入后臺(tái)執(zhí)行。這樣該命令對(duì)應(yīng)的進(jìn)程組即為后臺(tái)進(jìn)程組。在任意時(shí)刻,可能同時(shí)存在多個(gè)后臺(tái)進(jìn)程組,但是不管什么時(shí)候都只能有一個(gè)前臺(tái)進(jìn)程組。只有在前臺(tái)進(jìn)程組中進(jìn)程才能在控制終端讀取輸入。當(dāng)用戶在終端輸入信號(hào)生成終端字符(如ctrl+c、ctrl+z、ctr+等)時(shí),對(duì)應(yīng)的信號(hào)只會(huì)發(fā)送給前臺(tái)進(jìn)程組。
shell中可以存在多個(gè)進(jìn)程組,無論是前臺(tái)進(jìn)程組還是后臺(tái)進(jìn)程組,它們或多或少存在一定的聯(lián)系,為了更好地控制這些進(jìn)程組(或者稱為作業(yè)),系統(tǒng)引入了會(huì)話的概念。會(huì)話的意義在于將很多的工作囊括在一個(gè)終端,選取其中一個(gè)作為前臺(tái)來直接接收終端的輸入及信號(hào),其他的工作則放在后臺(tái)執(zhí)行。
會(huì)話
會(huì)話是一個(gè)或多個(gè)進(jìn)程組的集合,以用戶登錄系統(tǒng)為例,可能存在如圖4-3所示的情況。系統(tǒng)提供setsid函數(shù)來創(chuàng)建會(huì)話,其接口定義如下:
#include
pid_t setsid(void);
如果這個(gè)函數(shù)的調(diào)用進(jìn)程不是進(jìn)程組組長,那么調(diào)用該函數(shù)會(huì)發(fā)生以下事情:1)創(chuàng)建一個(gè)新會(huì)話,會(huì)話ID等于進(jìn)程ID,調(diào)用進(jìn)程成為會(huì)話的首進(jìn)程。
2)創(chuàng)建一個(gè)進(jìn)程組,進(jìn)程組ID等于進(jìn)程ID,調(diào)用進(jìn)程成為進(jìn)程組的組長。
3)該進(jìn)程沒有控制終端,如果調(diào)用setsid前,該進(jìn)程有控制終端,這種聯(lián)系就會(huì)斷掉。
調(diào)用setsid函數(shù)的進(jìn)程不能是進(jìn)程組的組長,否則調(diào)用會(huì)失敗,返回-1,并置errno為EPERM。
這個(gè)限制是比較合理的。如果允許進(jìn)程組組長遷移到新的會(huì)話,而進(jìn)程組的其他成員仍然在老的會(huì)話中,那么,就會(huì)出現(xiàn)同一個(gè)進(jìn)程組的進(jìn)程分屬不同的會(huì)話之中的情況,這就破壞了進(jìn)程組和會(huì)話的嚴(yán)格的層次關(guān)系了。
原文鏈接:http://www.cnblogs.com/zengyiwen/p/5755191.html