進程組(process group)html
進程組顧名思義是指一個或多個進程的集合。他們一般與同一個job(能夠從同一個終端接收信號)相關聯。每一個進程組擁有一個惟一的Process Group Id。可使用getpgrp或getpgid獲取進程的Process Group Id:shell
1 #include <unistd.h> 2 3 /* 4 返回調用進程的進程組Id 5 */ 6 pid_t getpgrp(void); 7 8 /* 9 若pid爲0,做用與getpgrp相同 10 */ 11 pid_t getpgid(pid_t pid);
每一個進程組能夠有一個進程組leader,leader的進程Id與其進程組id相同。不管進程組leader是否終止,只要進程組中存在未終止的進程這個進程組就不會消失。進程組leader終止後進程組中便不存在進程組leader,它不會自動推選新的進程組leader。參見 : Must a process group have a running leader process? 網絡
能夠經過setpgid函數設置進程組Id:session
1 #include <unistd.h> 2 3 /* 4 設置pid所在進程組Id爲pgid 5 */ 6 int setpgid(pid_t pid, pid_t pgid);
調用進程只能設置他本身和他的子進程的Process Group Id,而且若是他的子進程調用了exec類函數,那麼調用進程也沒法更改它的這個子進程的Process Group Id。在 job-control shells中,這個方法經常使用於fork調用後父進程來設置子進程的Process Group Id,或者子進程設置它本身的Process Group Id。固然,fork調用後子進程是會繼承父進程的Process Group Id的,fork後setpgid目的是爲了確保子進程的Process Group Id而避免由於併發竟態致使的意外(這個地方不太懂,沒構建出做者設想的竟態環境)。併發
會話(session)函數
會話(session)指一個或多個進程組的集合。spa
上圖所示的session結構可使用shell中的pipeline來表示:unix
proc1 | proc2 &
proc3 | proc4 | proc5
在上面示例中,login shell所在的進程組和proc一、proc2所在的進程組爲後臺進程組, proc3,proc4,proc5所在的進程組爲前臺進程組。一個會話中只能有一個前臺進程組,能夠有一個或多個後臺進程組。關於前臺進程組與後臺進程組咱們在後面介紹。code
進程能夠經過setsid來建立新的session。htm
1 #include <unistd.h> 2 3 /* 4 成功,返回進程組ID,不然返回 -1 5 */ 6 pid_t setsid(void);
setsid函數成功時返回的是調用進程的Process Group Id,也是調用進程的Process Id, 由於session leader 永遠是它所在的進程組leader。其實UNIX中是沒有相似 Process Id 或 Process Group Id 的 "Session Id" 這個東西的,有的僅僅是"Session leader"。 咱們能夠認爲 session leader 的 Process Group Id 或 Process Id爲其所在 session 的 Session Id。
- 調用進程成爲新session的session leader(A session leader is the process that creates a session)。此時此進程是新session中的惟一進程。
- 調用進程成爲新進程組的leader。新進程組的Process Group ID 等於 調用進程的 Process ID。
- 調用進程不會再有控制終端。若是調用進程在調用setsid前擁有控制終端的話,那麼調用setsid後他將斷開與其控制終端的聯繫。
1 #include <unistd.h> 2 3 /* 4 獲取pid所在session的session leader 5 的Process Group ID 6 */ 7 pid_t getsid(pid_t pid);
控制終端(controlling terminal)
- 一個session能夠擁有一個控制終端(固然也能夠沒有)。他一般是咱們登陸時的終端設備或僞終端。
- 建立與控制終端的連接的session leader被稱爲控制進程 (Controlling Process)。
- 一個會話中的進程組能夠劃分爲一個前臺進程組和一個或多個後臺進程組。
- 若是一個會話擁有控制終端,那麼它有一個前臺進進程組,這個會話中的其餘進程組都是後臺進程組。
- 不管什麼時候咱們按下終端的停止鍵(一般是 DELETE或Crtl-C),就會有一個停止信號發送給前臺進程組中的全部進程。
- 不管什麼時候咱們按下終端的退出鍵(一般是Crtl-Backslash),就會有一個退出信號發送給前臺進程組中的全部進程。
- 若是終端接口檢測到網絡斷開,那麼hang-up信號就會發送給控制進程,即the session leader。
通常咱們沒必要關心控制終端,它在咱們登陸時自動被建立。有事程序須要與控制終端通訊,不管是使用標準輸出仍是標準輸入重定向。程序保證與控制終端進行通訊的方法時讀寫 /dev/tty文件。這個特殊文件在內核中是控制終端的代名詞。若是程序沒有控制終端,那麼它打開/dev/tty文件將會失敗。
可使用tcgetpgrp函數獲取前臺進程組Id,使用tcsetpgrp設置前臺進程組:
1 #include <unistd.h> 2 3 /* 4 經過打開終端的文件描述符獲取前臺進程組Id 5 */ 6 pid_t tcgetpgrp(int fd); 7 8 /* 9 pgrpid 必須是相同session中的一個進程組Id 10 */ 11 int tcsetpgrp(int fd, pid_t pgrpid);
關於session、process group、controlling terminal的更多信息 , 參見:The controlling-terminal and process-groups.
Job Control
- shell須要支持job control
- 內核中的終端驅動必須支持job control
- 內核必須支持肯定的job-control信號
# 在前臺啓動包含一個進程的job vi main.c # 兩個後臺jobs調用的全部進程都是後臺進程 pr *.c | lpr & make all & # 當咱們啓動一個後臺job時,shell會給這個job分配一個job id, # 並打印這個job中的一個或多個進程IDs $ make all > Make.out & [1] 1475 $ pr *.c | lpr & [2] 1490
- 停止字符(一般是DELETE或Crtl-C)產生SIGINT
- 退出字符(一般是Crtl-Backslash)產生SIGQUIT
- 暫停字符(一般是Crtl-Z)產生SIGSTP
如前所述,只有前臺job會接收到終端輸入,可是後臺job嘗試讀取終端並非錯誤的,終端驅動會檢測後臺進程的這種舉動並向後臺job發送一個特殊的信號:SIGTTIN。SIGTTIN一般會停止後臺job。
總結