Linux session和進程組概述

上一篇中介紹了tty的相關原理,這篇將介紹跟tty密切相關的session和進程組。html

本篇主要目的是澄清一些概念,不涉及細節前端

session

session就是一組進程的集合,session id就是這個session中leader的進程ID。node

session的特色

session的主要特色是當session的leader退出後,session中的全部其它進程將會收到SIGHUP信號,其默認行爲是終止進程,即session的leader退出後,session中的其它進程也會退出。linux

若是session和tty關聯的話,它們之間只能一一對應,一個tty只能屬於一個session,一個session只能打開一個tty。固然session也能夠不和任何tty關聯。shell

session的建立

session能夠在任什麼時候候建立,調用setsid函數便可,session中的第一個進程即爲這個session的leader,leader是不能變的。常見的建立session的場景是:segmentfault

  • 用戶登陸後,啓動shell時將會建立新的session,shell會做爲session的leader,隨後shell裏面運行的進程都將屬於這個session,當shell退出後,全部該用戶運行的進程將退出。這類session通常都會和一個特定的tty關聯,session的leader會成爲tty的控制進程,當session的前端進程組發生變化時,控制進程負責更新tty上關聯的前端進程組,當tty要關閉的時候,控制進程所在session的全部進程都會收到SIGHUP信號。後端

  • 啓動deamon進程,這類進程須要和父進程劃清界限,因此須要啓動一個新的session。這類session通常不會和任何tty關聯。bash

進程組

進程組(process group)也是一組進程的集合,進程組id就是這個進程組中leader的進程ID。session

進程組的特色

進程組的主要特色是能夠以進程組爲單位經過函數killpg發送信號函數

進程組的建立

進程組主要用在shell裏面,shell負責進程組的管理,包括建立、銷燬等。(這裏shell就是session的leader)

  • 對大部分進程來講,它本身就是進程組的leader,而且進程組裏面就只有它本身一個進程

  • shell裏面執行相似ls|more這樣的以管道鏈接起來的命令時,兩個進程就屬於同一個進程組,ls是進程組的leader。

  • shell裏面啓動一個進程後,通常都會將該進程放到一個單獨的進程組,而後該進程fork的全部進程都會屬於該進程組,好比多進程的程序,它的全部進程都會屬於同一個進程組,當在shell裏面按下CTRL+C時,該程序的全部進程都會收到SIGINT而退出。

後臺進程組

shell中啓動一個進程時,默認狀況下,該進程就是一個前端進程組的leader,能夠收到用戶的輸入,而且能夠將輸出打印到終端,只有當該進程組退出後,shell才能夠再響應用戶的輸入。

但咱們也能夠將該進程組運行在後臺,這樣shell就能夠繼續相應用戶的輸入,常見的方法以下:

  • 啓動程序時,在後面加&,如sleep 1000 &,進程將會進入後臺繼續運行

  • 程序啓動後,能夠按CTRL+Z讓它進入後臺,和後面加&不一樣的是,進程會被暫停執行

對於後臺運行的進程組,在shell裏面體現爲job的概念,即一個後臺進程組就是一個job,job有以下限制:

  • 默認狀況下,只要後臺進程組的任何一個進程讀tty,將會使整個進程組的全部進程暫停

  • 默認狀況下,只要後臺進程組的任何一個進程寫tty,將有可能會使整個進程組的全部進程暫停(依賴於tty的配置,請參考TTY/PTS概述

全部後臺運行的進程組能夠經過jobs命令查看到,也能夠經過fg命令將後臺進程組切換到前端,這樣就能夠繼續接收用戶的輸入了。這兩個命令的具體用法請參考它們的幫助文件,這裏只給出一個簡單的例子:

#一般狀況下,sleep命令會一直等待在那裏,直到指定的時間過去後才退出。
#shell啓動sleep程序時,就將sleep放到了一個新的進程組,
#而且該進程組爲前端進程組,雖然sleep不須要輸入,也沒有輸出,
#但當前session的標準輸入和輸出仍是歸它,別人用不了,
#只有咱們按下CTRL+C使sleep進程退出後,shell本身從新變成了前端進程組,
#因而shell從新具有了響應輸入以及輸出能力
dev@debian:~$ sleep 1000
^C

#咱們能夠在命令行的後面加上&符號,shell仍是照樣會建立新的進程組,
#而且sleep進程就是新進程組的leader,
#可是shell會將sleep進程組放到後端,讓它成爲後臺進程組
#這裏[1]是job id,1627是進程組的ID,即sleep進程的id
dev@debian:~$ sleep 1000 &
[1] 1627

#能夠經過jobs命令看到當前有哪些後臺進程組(job)
dev@debian:~$ jobs
[1]+  Running                 sleep 1000 &

#使用fg命令帶上job id,便可讓後端進程組回到前端,
#而後咱們使用CTRL+Z命令可讓它再次回到後端,並暫停進程的執行
#CTRL+Z和&不同的地方就是CTRL+Z會讓進程暫停執行,而&不會
dev@debian:~$ fg 1
sleep 1000
^Z
[1]+  Stopped                 sleep 1000
#Stopped狀態表示進程在後臺已經暫停執行了
dev@debian:~$ jobs
[1]+  Stopped                 sleep 1000

session和進程組的關係

deamon程序雖然也是一個session的leader,但通常它不會建立新的進程組,也沒有job的管理功能,因此這種狀況下一個session就只有一個進程組,全部的進程都屬於一樣的進程組和session。

咱們這裏看一下shell做爲session leader的狀況,假設咱們在shell裏面執行了這些命令:

dev@debian:~$ sleep 1000 &
[1] 1646
dev@debian:~$ cat | wc -l &
[2] 1648
dev@debian:~$ jobs
[1]-  Running                 sleep 1000 &
[2]+  Stopped                 cat | wc -l

下面這張圖標明瞭這種狀況下它們之間的關係:

+--------------------------------------------------------------+
|                                                              |
|      pg1             pg2             pg3            pg4      |
|    +------+       +-------+        +-----+        +------+   |
|    | bash |       | sleep |        | cat |        | jobs |   |
|    +------+       +-------+        +-----+        +------+   |
| session leader                     | wc  |                   |
|                                    +-----+                   |
|                                                              |
+--------------------------------------------------------------+
                            session

pg = process group(進程組)

  • bash是session的leader,sleep、cat、wc和jobs這四個進程都由bash fork而來,因此他們也屬於這個session

  • bash也是本身所在進程組的leader

  • bash會爲本身啓動的每一個進程都建立一個新的進程組,因此這裏sleep和jobs進程屬於本身單獨的進程組

  • 對於用管道符號「|」鏈接起來的命令,bash會將它們放到一個進程組中

nohup

nohup是咋回事呢?nohup幹了這麼幾件事:

  • 將stdin重定向到/dev/null,因而程序讀標準輸入將會返回EOF

  • 將stdout和stderr重定向到nohup.out或者用戶經過參數指定的文件,程序全部輸出到stdout和stderr的內容將會寫入該文件(有時在文件中看不到輸出,有多是程序沒有調用flush)

  • 屏蔽掉SIGHUP信號

  • 調用exec啓動指定的命令(nohup進程將會被新進程取代,但進程ID不變)

從上面nohup乾的事能夠看出,經過nohup啓動的程序有這些特色:

  • nohup程序不負責將進程放到後臺,這也是爲何咱們常常在nohup命令後面要加上符號「&」的緣由

  • 因爲stdin、stdout和stderr都被重定向了,nohup啓動的程序不會讀寫tty

  • 因爲stdin重定向到了/dev/null,程序讀stdin的時候會收到EOF返回值

  • nohup啓動的進程本質上仍是屬於當前session的一個進程組,因此在當前shell裏面能夠經過jobs看到nohup啓動的程序

  • 當session leader退出後,該進程會收到SIGHUP信號,但因爲nohup幫咱們忽略了該信號,因此該進程不會退出

  • 因爲session leader已經退出,而nohup啓動的進程屬於該session,因而出現了一種狀況,那就是經過nohup啓動的這個進程組所在的session沒有leader,這是一種特殊的狀況,內核會幫咱們處理這種特殊狀況,這裏就再也不深刻介紹

經過nohup,咱們最後達到了就算session leader(通常是shell)退出後,進程還能夠照常運行的目的。

deamon

經過nohup,就能夠實現讓進程在後臺一直執行的功能,爲何咱們還要寫deamon進程呢?

從上面的nohup的介紹中能夠看出來,雖然進程是在後臺執行,但進程跟當前session仍是有着千絲萬縷的關係,至少其父進程仍是被session管着的,因此咱們仍是須要一個跟任何session都沒有關係的進程來實現deamon的功能。實現deamon進程的大概步驟以下:

  • 調用fork生成一個新進程,而後原來的進程退出,這樣新進程就變成了孤兒進程,因而被init進程接收,這樣新進程就和調用進程沒有父子關係了。

  • 調用setsid,建立新的session,新進程將成爲新session的leader,同時該新session不和任何tty關聯。

  • 切換當前工做目錄到其它地方,通常是切換到根目錄,這樣就取消了對原工做目錄的引用,若是原工做目錄是某個掛載點下面的目錄,這樣就不會影響該掛載點的卸載。

  • 關閉一些從父進程繼承過來而本身不須要的fd,避免不當心讀寫這些fd。

  • 重定向stdin、stdout和stderr,避免讀寫它們出現錯誤。

參考

相關文章
相關標籤/搜索