在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
命令時,這個程序觸發出來的其它程序也會沿用這個程序的相關許可權。
我們對程式與程序做一個總結:
程序彼此之間是有關係的。從下圖來看,連續執行兩個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的方式來執行時機要執行的程式碼,最終就成為一個子程序。整個流程有點像下面這張圖:
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]一書。
想要檢視系統上正在執行中的程序,可以利用靜態的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
顯示出來的資料有哪些?
-
;SZ代表此程序用掉多少記憶體;WCHAN表示目前進場是否執行,同樣的,若為-
表示正在執行中。pts/n
)。所以你看到的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
。