在Linux系統中,執行一個程式或命令就可以觸發一個進程,系統會給予這個進程一個ID,稱為PID,同時根據觸發這個進程的用戶與相關屬性關係,基於這個PID一組有效的許可權設置。舉個常見的例子,我們要操作系統的時候通常是利用ssh連接程式或直接在主機上登錄,然後獲取shell。預設的shell是bash... ...
1 進程與程式
在Linux系統中,執行一個程式或命令就可以觸發一個進程,系統會給予這個進程一個ID,稱為PID,同時根據觸發這個進程的用戶與相關屬性關係,基於這個PID一組有效的許可權設置。如下圖所示(圖片來自《鳥哥的Linux私房菜》[1]):
舉個常見的例子,我們要操作系統的時候通常是利用ssh連接程式或直接在主機上登錄,然後獲取shell。預設的shell是bash,對應的路徑為/bin/bash
,那麼同時間的每個人登錄都是執行/bin/bash
,不過每個人獲取的許可權不同,如下圖所示:
也就是說,當我們的登錄並執行/bin/bash
程式時,系統已經給了我們一個PID,這個PID就是根據登陸者的UID/GID(/etc/passwd
)而來,而所謂用戶的許可權就是這個bash進程的許可權。當這個進程執行其它作業時,比如我們在shell中執行touch
命令時,這個進程觸發出來的其它進程也會沿用這個進程的相關許可權。
我們對程式與進程做一個總結:
- 程式(program):通常為二進位程式,存儲在存儲媒介中(如磁碟等),以物理文件的形式存在。
- 進程(process):程式被觸發後,執行者的許可權與屬性、程式的代碼與所需數據等都會被載入到記憶體中,操作系統給予這個記憶體中的單元一個標識符(PID),可以說進程就是一個正在運行中的程式。
進程彼此之間是有關係的。從下圖來看,連續執行兩個bash後,第二個bash的父進程就是前一個bash,通過Parent PID(PPID)可獲取其父進程的PID:
此外,子進程可以獲取父進程的環境變數。
下麵這個例子我們會展示子進程和父進程的關係。我們在當前的bash環境下,再觸發一次bash,並用ps -l
命令查看進程相關的輸出信息,
bash-3.2$ ps -l
UID PID PPID F CPU PRI NI SZ RSS WCHAN S ADDR TTY TIME CMD
501 9892 9827 4006 0 31 0 408650672 1968 - S 0 ttys001 0:00.01 /bin/bash
501 9905 9892 4006 0 31 0 408657840 2736 - S 0 ttys001 0:00.01 /bin/bash
可以看到第一個bash的PID和第二個bash的PPID都是9892,這是因為第二個bash是來自第一個所產生的。
很多常常會發現,“咦,我們們將有問題的進程關閉了(比如Ctrl+C
殺掉),怎麼過一陣子它又自動產生了?而且新產生進程的PID還與原先不同這是怎麼回事呢?”如果不是crontab計劃任務的影響,那麼肯定有一個父進程存在,所以我們殺掉子進程後,父進程又會主動再生成一個。那怎麼辦呢?“擒賊先擒王”。我們用ps auxf
找出那個父進程,然後將它殺掉即可(後文會提到)。
fork and exec:進程調用的流程
子進程和父進程之間的關係比較複雜,最大的複雜點在於進程之間的調用。Linux程式的調用通常稱為fork-and-exec流程。進程都會藉由父進程以複製(fork)的方式產生一個一模一樣的子進程,然後被覆制出來的子進程再以exec的方式來執行時機要執行的代碼,最終就成為一個子進程。整個流程有點像下麵這張圖:
- 系統先以fork的方式複製一個與父進程相同的臨時進程,這個進程與父進程唯一的差別就是PID不同,但這個臨時進程還會多一個PPID參數。
- 然後臨時進程開始以exec的方式載入實際要執行的進程。以上圖來講,新的程式名稱為
qqq
,最終子進程的進程代碼就會變成qqq
了。
系統或網路服務:常駐在記憶體里的進程
一般的Linux命令(如ls
、touch
、rm
等)都是執行完就結束,也就是說該項命令被觸發後所產生的PID很快就會被終止,那麼有沒有一直在執行的進程呢?
當然有,我們把在後臺啟動並一直持續不斷地運行,也即常駐在記憶體當中的進程稱為守護進程(daemon)。常見的服務包括系統本身所需要的服務(例如crond、atd、rsyslogd等)和負責網路連接的服務(例如apache、named、postfix、vsftpd等)。網路服務比較有趣的地方在於,它會啟動一個可以複雜網路監聽的埠(port),以提供外部客戶端(client)的連接請求。
PS1:在Linux系統中,一般daemon類型的進程都會在文件名後面加上d。
PS2:“守護進程”這個概念由麻省理工學院MAC項目的程式員發明。費南多·柯巴托於1963年在MAC項目任務。根據他的說法,他的團隊最早採用daemon這個概念,其靈感來源於麥克斯韋妖——一種物理學和熱力學中虛構的介質,能幫助排列分子。他對此表示:“我們別出心裁地開始使用daemon這個詞來描述後臺進程,它們不知疲倦地處理系統中的雜務。”Unix系統繼承了這個術語。作為一種在後臺起作用的超自然存在,麥克斯韋妖與古希臘神話中的代蒙一致[2]。關於麥克斯韋妖的更多有趣信息可以參見梅拉妮·米歇爾的《複雜》[3]一書。
2 進程管理
想要查看系統上正在運行中的進程,可以利用靜態的ps
或者是動態的top
命令,還可以利用pstree
來查看進程樹之間的關係。
ps:將某個時間點的進程運行情況擷取下來
ps aux
可查看系統中所有的進程(註意,沒有-
號):
~/Orion-Orion # ps -aux root@qi
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4504 48 pts/0 Ss 2022 0:01 sh /root/start.sh
root 7 0.0 0.0 65520 420 ? Ss 2022 0:07 /usr/sbin/sshd
root 8 0.0 0.0 20052 264 pts/0 S+ 2022 0:00 /bin/bash
...
root 39717 0.1 0.0 93648 7608 ? Ss 08:40 0:02 sshd: root@notty
root 39727 0.0 0.0 9752 2924 ? Ss 08:40 0:00 bash
root 40140 0.5 0.0 95296 9220 ? Rs 08:54 0:02 sshd: root@notty
root 40150 0.0 0.0 9752 2832 ? Ss 08:54 0:00 bash
ps -l
則可以僅查看自己的bash相關的進程:
~/Orion-Orion # ps -l root@qi
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 0 42331 41909 0 80 0 - 5015 wait pts/312 00:00:00 bash
0 R 0 42379 42331 0 80 0 - 6920 - pts/312 00:00:00 ps
我們可以看到,系統整體運行的進程是非常多的,但使用ps -l
僅會列出與你的操作環境(bash)有關的進程,即最上層的父進程會是你自己的bash而沒有擴展到systemd
(後續會介紹)這個進程中。我們接下來來看看ps -l
顯示出來的數據有哪些?
- F:表示這個進程標識(process flags),說明這個進程的許可權,常見號碼有:
- 若為4表示此進程的許可權為root。
- 若為表示此進程僅執行複製(fork)而沒有實際執行(exec)。
- 若為0表示進程標識沒有設置。
- S: 代表這個進程的狀態(STAT),主要的狀態有:
- R(Running):該進程正在運行中(running) 或是可運行的(runnable)。
- S(Sleep):進程處於可被喚醒(signal)的睡眠狀態,也即所謂空閑狀態(idle)。這種狀態一般是進程主動進入的。
- D:進程處於不可被喚醒的睡眠狀態,通常這個進程可能在等待I/O的情況(例如列印)。這種狀態一般是進程被動進入的。
- T(Stopped):停止狀態,可能是在任務控制(後臺暫停)或跟蹤(traced)狀態。
- Z(Zombie):僵屍狀態(即所謂defunct),進程已經終止但卻無法被刪除至記憶體外。
- UID/PID/PPID:代表進程被該UID所擁有/進程的PID號碼/此進程的父進程PID號碼。
- C:代表CPU使用率,單位為百分比。
- PRI/NI:Priority/Nice的縮寫,代表此進程被CPU所執行的優先順序,數值越小代表該進程越快被CPU執行。詳細的PRI與NI將在下一小節說明。
- ADDR/SZ/WCHAN:都與記憶體相關,ADDR是kernel function,指出該進程在記憶體的哪個部分,如果是個running的進程,一般就會顯示
-
;SZ代表此進程用掉多少記憶體;WCHAN表示目前進場是否運行,同樣的,若為-
表示正在運行中。 - TTY:登錄者的終端位置,若為遠程登錄則使用動態終端介面名稱(
pts/n
)。 - TIME:使用CPU的時間,註意是進程實際花費CPU的時間,而不是運行時間。關於這兩個時間之間的區別可參見我的博客《Python:對程式做性能分析及計時統計》
- CMD:就是command的縮寫,表示觸發此進程的命令是什麼。
所以你看到的ps -l
輸出信息中,它說明的是:bash進程屬於UID為0的用戶,狀態為睡眠(Sleeping),之所以為睡眠,是因為它觸發了ps
(狀態為Running)。此進程的PID是42331,執行優先順序為80,執行bash所獲取的終端介面為pts/0
。運行狀態為等待(wait)。
接下來我們用ps auxf
列出類似進程樹的進程顯示:
(base) root@qi:~/Orion-Orion# ps -auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4504 48 pts/0 Ss 2022 0:01 sh /root/start.sh
root 7 0.0 0.0 65520 420 ? Ss 2022 0:07 /usr/sbin/sshd
root 38677 0.0 0.0 93284 7412 ? Ss 07:55 0:00 \_ sshd: root@notty
root 38687 0.0 0.0 9756 2824 ? Ss 07:55 0:00 | \_ bash
root 38765 0.0 0.0 4504 1712 ? S 07:55 0:00 | \_ sh /root/.vscode-server/bin/b7886d7461186a5
root 41898 0.1 0.0 661960 58144 ? Sl 10:38 0:06 | | \_ /root/.vscode-server/bin/b7886d7461
root 42331 0.0 0.0 20060 3788 pts/312 S 10:40 0:00 | | \_ bash
root 43592 0.0 0.0 36276 3248 pts/312 R+ 11:41 0:00 | | \_ ps -auxf
...
root 39717 0.0 0.0 93648 7608 ? Ss 08:40 0:02 \_ sshd: root@notty
root 39727 0.0 0.0 9752 2924 ? Ss 08:40 0:00 | \_ bash
root 43493 0.0 0.0 4376 672 ? S 11:40 0:00 | \_ sleep 180
root 41362 0.0 0.0 93296 7360 ? Ss 10:33 0:01 \_ sshd: root@notty
root 41372 0.0 0.0 9752 2824 ? Ss 10:33 0:00 \_ bash
root 43492 0.0 0.0 4376 700 ? S 11:39 0:00 \_ sleep 180
root 8 0.0 0.0 20052 264 pts/0 S+ 2022 0:00 /bin/bash
因為我是用ssh網路連接進入伺服器來執行一些測試的,可以看出進程之間是相關性的。從上面的例子來看,我是通過sshd
提供的網路服務獲取的一個進程,該進程提供bash給我使用,而我通過bash再去執行VSCode-Server服務啟動腳本,以運行VSCode-Server服務進程,然後該進程再提供給我一個bash, 我通過這個bash再去執行ps auxf
(瘋狂套娃哈哈哈)。
這裡說一個題外話,
sshd
進程是我們前面提到的deamon,是不能隨意殺掉的哦! 殺掉了不僅你會馬上斷開連接,下次再用ssh連你也連不上了。
除了f
這個選項,我們還可以使用pstree
來完全查看這個進程樹。
(base) root@qi:~/Orion-Orion# pstree
sh─┬─bash
└─sshd─┬─sshd───bash─┬─sh───node─┬─node───12*[{node}]
│ │ ├─node─┬─node───10*[{node}]
│ │ │ ├─node───11*[{node}]
│ │ │ ├─python───{python}
│ │ │ └─11*[{node}]
│ │ ├─node───11*[{node}]
│ │ ├─node─┬─bash───pstree
│ │ │ └─11*[{node}]
│ │ └─10*[{node}]
│ └─sleep
└─2*[sshd───bash───sleep]
除此之外,我們必須要知道的是僵屍(zombie) 進程是什麼?通常,造成僵屍進程的原因在於該進程應該已經執行完畢,或是應該要終止了,但該進程的父進程卻無法完整地將該進程結束掉,而造成該進程一直存在記憶體中。如果你發現在某個進程的CMD
/COMMAND
後面接上了defunct
時,就代表該進程是僵屍進程,例如:
apache 8683 0.0 0.9 83384 9992 ? Z 14:33 0:00 /usr/sbin/httpd <defunct>
系統不穩定的時候就容易造成所謂的僵屍進程,可能是因為程式寫得不好,或是用戶的操作習慣不良等所造成的。如果你發現系統中有很多僵屍進程時,記得要找出該進程的父進程,然後好好做個追蹤,好好進行主機的環境優化,看看有什麼地方需要改善,而不是直接將它kill掉。不然萬一它一直產生就麻煩了。
事實上,通常僵屍進程都已經無法管理,而直接交給 systemd
這個進程來負責,偏偏systemd
是系統第一個執行的進程,它是所有進程的父進程。我們是無法殺掉該進程的(殺掉它,系統就死掉了),所以如果產生僵屍進程,而系統過了一陣子還沒有辦法通過內核非經常性的特殊處理來將該進程刪除時,那你只好通過reboot
的方式來將該進程kill掉。
systemd
是目前Linux系統上主要的系統守護進程管理工具,由於init
一方面對於進程的管理是串列化的,容易出現阻塞情況,另一方面init
也僅僅是執行啟動腳本,並不能對服務本身進行更多的管理。所以最新系統(RedHat7,CentOS7,Ubuntu15…)大都由systemd取代了init
作為預設的系統進程管理工具。
top:動態查看進程的變化
相對於ps是選取一個時間點的進程狀態,top
可以持續監測進程運行的狀態,使用方式如下:
top [-d 數字] | top [-bnp]
它的選項與參數如下:
-d
:後面可以接秒數,就是整個進程界面更新的秒數,預設是5秒。-b
:以批量的方式執行top
,還有更多的參數可以使用,通常會搭配數據重定向來將批量的結果輸出為文件。-n
:與-b
搭配,意義是需要執行幾次top
的輸出結果。-p
:指定某些PID來執行查看檢測。
在top
的執行過程中可以使用下列的按鍵命令:
?
:顯示在top
中可以輸入的按鍵命令P
:以CPU的使用排序顯示。M
:以Memory的使用排序顯示。N
:以PID來排序。T
:由該進程使用的CPU時間累積(TIME+
)排序k
:給予某個PID一個信號(signal
)。r
:給予某個PID重新制定一個nice值。q
:退出top
的按鍵。
接下來我們實際查看一下如何使用top
與top
的界面。比如以下是我們輸入top -d 2
命令得到的結果,該命令表示每兩秒鐘更新一次top,查看整體信息。
top - 13:19:06 up 202 days, 5:00, 3 users, load average: 89.81, 75.65, 68.67
Tasks: 74 total, 1 running, 73 sleeping, 0 stopped, 0 zombie
%Cpu(s): 32.2 us, 4.5 sy, 35.7 ni, 27.5 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 52701926+total, 52750784 free, 49904712 used, 42436377+buff/cache
KiB Swap: 8388604 total, 7983868 free, 404736 used. 46639996+avail Mem
<==如果加入k或r時,就會有相關的字樣出現在這裡。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
38775 root 20 0 1106596 236232 37596 S 1.0 0.0 1:28.68 node
12191 root 20 0 1662128 61256 11176 S 0.5 0.0 205:36.04 python
1 root 20 0 4504 48 0 S 0.0 0.0 0:01.03 sh
7 root 20 0 65520 420 4 S 0.0 0.0 0:07.19 sshd
...
41787 root 20 0 20060 3700 32 S 0.0 0.0 0:00.01 bash
41898 root 20 0 968200 62276 33216 S 0.0 0.0 0:14.26 node
可見,top
與ps
的靜態結果輸出不同,top
這個進程可以持續地監測整個系統的進程任務狀態。在預設的情況下更新進程資源的時間為5秒,不過可以使用-d
來執行修改。top
主要分為兩部分界面,上面的界面為整個系統的資源使用狀態,基本上總共有六行。至於top
下半部分的畫面,則是每個進程使用的資源情況。
top
預設使用CPU使用率(%CPU
)作為排序的依據,如果你想要使用記憶體使用率排序,則可以按下M
鍵,若要恢復則按下P
鍵即可。如果想要退出top
,則按下q
。
參考
- [1] 鳥哥. 鳥哥的 Linux 私房菜: 基礎學習篇(第四版)[M]. 人民郵電出版社, 2018.
- [2] 《維基百科:守護進程》
- [3] 梅拉妮·米歇爾. 複雜[M]. 湖南科學技術出版社, 2018.