程序與程式

程序與程式

  • 程式 (program):通常為 binary program ,放置在儲存媒體中 (如硬碟、光碟、軟碟、磁帶等), 為實體檔案的型態存在;
  • 程序 (process):程式被觸發後,執行者的權限與屬性、程式的程式碼與所需資料等都會被載入記憶體中, 作業系統並給予這個記憶體內的單元一個識別碼 (PID),可以說,程序就是一個正在運作中的程式

 

子程序與父程序

當我們登入系統後,會取得一個 bash shell ,然後,我們用這個 bash 提供的介面去執行另一個指令,例如 /usr/bin/passwd 或者是 touch 等等,那些另外執行的指令也會被觸發成為 PID ,呵呵!那個後來執行指令才產生的 PID 就是『子程序』了,而在我們原本的 bash 環境下,就稱為『父程序』

 

程式彼此之間是有相關性的!例如在一个bash shell里执行 bash命令 後,第二個 bash 的父程序就是前一個 bash。因為每個程序都有一個 PID ,那某個程序的父程序該如何判斷?就透過 Parent PID (PPID) 來判斷即可。此外,之前介绍的 export 內容我們也探討過環境變數的繼承問題,子程序可以取得父程序的環境變數啦

 

很多朋友常常會發現:『咦!明明我將有問題的程序關閉了,怎麼過一陣子他又自動的產生?而且新產生的那個程序的 PID 與原先的還不一樣,這是怎麼回事呢?』不要懷疑,如果不是 crontab 工作排程的影響,肯定有一支父程序存在,所以你殺掉子程序後, 父程序就會主動再生一支!那怎麼辦?正所謂這:『擒賊先擒王』,找出那支父程序,然後將他刪除就對啦

 

fork and exec:程序呼叫的流程

其實子程序與父程序之間的關係還挺複雜的,最大的複雜點在於程序互相之間的呼叫。 Linux 的程序呼叫通常稱為 fork-and-exec 的流程!程序都會藉由父程序以複製 (fork) 的方式產生一個一模一樣的子程序, 然後被複製出來的子程序再以 exec 的方式來執行實際要進行的程式,最終就成為一個子程序的存在

其流程为:

(1)系統先以 fork 的方式複製一個與父程序相同的暫存程序,這個程序與父程序唯一的差別就是 PID 不同! 但是這個暫存程序還會多一個 PPID 的參數!然後

(2)暫存程序開始以 exec 的方式載入實際要執行的程式,最終变成子程序

 

系統或網路服務:常駐在記憶體的程序

之前學到的指令(如touch, rm, chmod等)都是執行完就結束了。也就是說,該項指令被觸發後所產生的 PID 很快就會終止呢! 那有沒有一直在執行的程序啊?當然有啊!而且多的是呢!

舉個簡單的例子來說好了,我們知道系統每分鐘都會去掃瞄 /etc/crontab 以及相關的設定檔, 來進行工作排程吧?那麼那個工作排程是誰負責的?當然不是鳥哥啊!呵呵! crond 這個程式所管理的我們將他啟動在背景當中一直持續不斷的運作, 套句以前 DOS 年代常常說的一句話,那就是『常駐在記憶體當中的程序』啦!

常駐在記憶體當中的程序通常都是負責一些系統所提供的功能以服務使用者各項任務,因此這些常駐程式就會被我們稱為:服務 (daemon)。系統的服務非常的多,不過主要大致分成系統本身所需要的服務,例如剛剛提到的 crond atd ,還有 syslog 等等的。還有一些則是負責網路連線的服務,例如 Apache, named, postfix, vsftpd... 等等的。這些網路服務比較有趣的地方,在於這些程式被執行後,他會啟動一個可以負責網路監聽的埠口 (port) ,以提供外部用戶端 (client) 的連線要求。

工作管理 (job control)

工作管理 (job control) 是用在 bash 環境下的,也就是說:『當我們登入系統取得 bash shell 之後,單一終端機介面下同時進行多個工作的行為管理 』。舉例來說,我們在登入 bash 後, 想要一邊複製檔案、一邊進行資料搜尋、一邊進行編譯,還可以一邊進行 vi 程式撰寫! 當然我們可以重複登入那六個文字介面的終端機環境中,不過,能不能在一個 bash 內達成? 當然可以啊!就是使用 job control 啦!

你應該要瞭解的是:『進行工作管理的行為中, 其實每個工作都是目前 bash 的子程序,亦即彼此之間是有相關性的。 我們無法以 job control 的方式由 tty1 的環境去管理 tty2 bash !』 這個概念請你得先建立起來,後續的範例介紹之後,你就會清楚的瞭解囉!

由於假設我們只有一個終端介面,因此在可以出現提示字元讓你操作的環境就稱為前景 (foreground),至於其他工作就可以讓你放入背景 (background) 去暫停或運作。要注意的是,放入背景的工作想要運作時, 他必須不能夠與使用者互動。舉例來說, vim 絕對不可能在背景裡面執行 (running) !因為你沒有輸入資料他就不會跑啊! 而且放入背景的工作是不可以使用 [ctrl]+c 來終止的』

總之,要進行 bash job control 必須要注意到的限制是:

  • 這些工作所觸發的程序必須來自於你 shell 的子程序(只管理自己的 bash)
  • 前景:你可以控制與下達指令的這個環境稱為前景的工作 (foreground)
  • 背景:可以自行運作的工作,你無法使用 [ctrl]+c 終止他,可使用 bg/fg 呼叫該工作;
  • 背景中『執行』的程序不能等待 terminal/shell 的輸入(input)

注意:

1. bash 只能夠管理自己的工作而不能管理其他 bash 的工作,所以即使你是 root 也不能夠將別人的 bash 底下的 job 給他拿過來執行

2. job又分前景與背景,然後在背景裡面的工作狀態又可以分為『暫停 (stop)』與『運作中 (running)

 

下面介绍一些job 控制的指令

 

直接將指令丟到背景中『執行』的 &

只有一個 bash 的環境下,如果想要同時進行多個工作, 那麼可以將某些工作直接丟到背景環境當中,讓我們可以繼續操作前景的工作!那麼如何將工作丟到背景中?最簡單的方法就是利用『 & 這個玩意兒了

 

[root@www ~]# tar -zpcf /tmp/etc.tar.gz /etc &

[1] 8400  <== [job number] PID

[root@www ~]# tar: Removing leading `/' from member names

# 在中括號內的號碼為工作號碼 (job number),該號碼與 bash 的控制有關。

# 後續的 8400 則是這個工作在系統中的 PID。至於後續出現的資料是 tar 執行的資料流,

# 由於我們沒有加上資料流重導向,所以會影響畫面!不過不會影響前景操作!

 

如果你輸入幾個指令後,突然出現這個資料:

[1]+  Done                    tar -zpcf /tmp/etc.tar.gz /etc

就代表 [1] 這個工作已經完成 (Done)

 

在背景當中執行的指令,如果有 stdout stderr 時,他的資料依舊是輸出到螢幕上面的。所以囉,最佳的狀況就是利用資料流重導向, 將輸出資料傳送至某個檔案中。舉例來說,我可以這樣做

[root@www ~]# tar -zpcvf /tmp/etc.tar.gz /etc > /tmp/log.txt 2>&1 &
[1] 8429
 
將『目前』的工作丟到背景中『暫停』:[ctrl]-z
[root@www ~]# vi ~/.bashrc
#  vi 的一般模式下,按下 [ctrl]-z 這兩個按鍵
[1]+  Stopped                 vim ~/.bashrc
[root@www ~]#   <==順利取得了前景的操控權!
[root@www ~]# find / -print
....(輸出省略)....
# 此時螢幕會非常的忙碌!因為螢幕上會顯示所有的檔名。請按下 [ctrl]-z 暫停
[2]+  Stopped                 find / -print

 

vi 的一般模式下,按下 [ctrl] z 這兩個按鍵,螢幕上會出現 [1] ,表示這是第一個工作, 而那個 + 代表最近一個被丟進背景的工作,且目前在背景下預設會被取用的那個工作 ( fg 這個指令有關 )!而那個 Stopped 則代表目前這個工作的狀態。在預設的情況下,使用 [ctrl]-z 丟到背景當中的工作都是『暫停』的狀態喔!

 

觀察目前的背景工作狀態: jobs

[root@www ~]# jobs [-lrs]
選項與參數:
-l  :除了列出 job number 與指令串之外,同時列出 PID 的號碼;
-r  :僅列出正在背景 run 的工作;
-s  :僅列出正在背景當中暫停 (stop) 的工作。
 
範例一:觀察目前的 bash 當中,所有的工作,與對應的 PID
[root@www ~]# jobs -l
[1]- 10314 Stopped  vim ~/.bashrc
[2]+ 10833 Stopped  find / -print

在輸出的資訊當中,例如上表,仔細看到那個 + - 號喔!那個 + 代表預設的取用工作。所以說:『目前我有兩個工作在背景當中,兩個工作都是暫停的, 而如果我僅輸入 fg 時,那麼那個 [2] 會被拿到前景當中來處理』!

其實 + 代表最近被放到背景的工作號碼, - 代表最近最後第二個被放置到背景中的工作號碼。 而超過最後第三個以後的工作,就不會有 +/- 符號存在了

將背景工作拿到前景來處理:fg

如何將背景工作拿到前景來處理的? 有啊!就是那個 fg (foreground) 啦!

[root@www ~]# fg %jobnumber
選項與參數:
%jobnumber jobnumber 為工作號碼(數字)。注意,那個 % 是可有可無的!
 
範例一:先以 jobs 觀察工作,再將工作取出:
[root@www ~]# jobs
[1]- 10314 Stopped                 vim ~/.bashrc
[2]+ 10833 Stopped                 find / -print
[root@www ~]# fg   <==預設取出那個 + 的工作,亦即 [2]。立即按下[ctrl]-z
[root@www ~]# fg %1   <==直接規定取出的那個工作號碼!再按下[ctrl]-z
[root@www ~]# jobs
[1]+  Stopped                 vim ~/.bashrc
[2]-  Stopped                 find / -print

另外,如果輸入『 fg - 』則代表將 - 號的那個工作號碼拿出來

 

讓工作在背景下的狀態變成運作中: bg

[ctrl]-z 可以將目前的工作丟到背景底下去『暫停』, 那麼如何讓一個工作在背景底下『 Run 呢?

範例一:一執行 find命令後,立刻丟到背景去暫停!
[root@www ~]# find / -perm +7000 > /tmp/text.txt
# 此時,請立刻按下 [ctrl]-z 暫停!
[3]+  Stopped                 find / -perm +7000 > /tmp/text.txt
 
範例二:讓該工作在背景下進行,並且觀察他!!
[root@www ~]# jobs ; bg %3 ; jobs
[1]-  Stopped                 vim ~/.bashrc
[2]   Stopped                 find / -print
[3]+  Stopped                 find / -perm +7000 > /tmp/text.txt
[3]+ find / -perm +7000 > /tmp/text.txt &  <== bg%3 的情況!
[1]+  Stopped                 vim ~/.bashrc
[2]   Stopped                 find / -print
[3]-  Running                 find / -perm +7000 > /tmp/text.txt &

 

管理背景當中的工作: kill

如果想要將該工作直接移除呢?或者是將該工作重新啟動呢?這個時候就得需要給予該工作一個訊號 (signal) ,讓他知道該怎麼作才好啊!此時, kill 這個指令就派上用場啦

 
[root@www ~]# kill -signal %jobnumber
[root@www ~]# kill -l
選項與參數:
-l  :這個是 L 的小寫,列出目前 kill 能夠使用的訊號 (signal) 有哪些?
signal :代表給予後面接的那個工作什麼樣的指示囉!用 man 7 signal 可知:
  -1 :重新讀取一次參數的設定檔 (類似 reload)
  -2 :代表與由鍵盤輸入 [ctrl]-c 同樣的動作;
  -9 :立刻強制刪除一個工作;
  -15:以正常的程序方式終止一項工作。與 -9 是不一樣的。
 
範例一:找出目前的 bash 環境下的背景工作,並將該工作『強制刪除』。
[root@www ~]# jobs
[1]+  Stopped                 vim ~/.bashrc
[2]   Stopped                 find / -print
[root@www ~]# kill -9 %2; jobs
[1]+  Stopped                 vim ~/.bashrc
[2]   Killed                  find / -print
# 再過幾秒你再下達 jobs 一次,就會發現 2 號工作不見了!因為被移除了!
 
範例:找出目前的 bash 環境下的背景工作,並將該工作『正常終止』掉。
[root@www ~]# jobs
[1]+  Stopped                 vim ~/.bashrc
[root@www ~]# kill -SIGTERM %1
# -SIGTERM  -15 是一樣的!您可以使用 kill -l 來查閱!

 

特別留意一下, -9 這個 signal 通常是用在『強制刪除一個不正常的工作』時所使用的, -15 則是以正常步驟結束一項工作(15也是預設值),兩者之間並不相同呦!舉上面的例子來說,我 vi 的時候,不是會產生一個 .filename.swp 的檔案嗎? 那麼,當使用 -15 這個 signal 時, vi 會嘗試以正常的步驟來結束掉該 vi 的工作, 所以 .filename.swp 會主動的被移除。但若是使用 -9 這個 signal 時,由於該 vi 工作會被強制移除掉,因此, .filename.swp 就會繼續存在檔案系統當中。這樣您應該可以稍微分辨一下了吧?

此外,那個 killall 也是同樣的用法! 至於常用的 signal 您至少需要瞭解 1, 9, 15 這三個 signal 的意義才好。 此外, signal 除了以數值來表示之外,也可以使用訊號名稱喔! 舉例來說,上面的範例二就是一個例子啦!至於 signal number 與名稱的對應, 呵呵,使用 kill -l 就知道啦(L的小寫)

另外, kill 後面接的數字預設會是 PID ,如果想要管理 bash 的工作控制,就得要加上 %數字 了, 這點也得特別留意才行喔!

 

離線管理問題

要注意的是,我們在工作管理當中提到的『背景』指的是在終端機模式下可以避免 [crtl]-c 中斷的一個情境, 並不是放到系統的背景去喔!所以,工作管理的背景依舊與終端機有關啦!在這樣的情況下,如果你是以遠端連線方式連接到你的 Linux 主機,並且將工作以 & 的方式放到背景去, 請問,在工作尚未結束的情況下你離線了,該工作還會繼續進行嗎?答案是『否』!不會繼續進行,而是會被中斷掉

那怎麼辦?如果我的工作需要進行一大段時間,我又不能放置在背景底下,那該如何處理呢?首先,你可以參考前一章的 at 來處理即可!因為 at 是將工作放置到系統背景, 而與終端機無關。如果不想要使用 at 的話,那你也可以嘗試使用 nohup 這個指令來處理喔!這個 nohup 可以讓你在離線或登出系統後,還能夠讓工作繼續進行。他的語法有點像這樣:

[root@www ~]# nohup [指令與參數]   <==在終端機前景中工作
[root@www ~]# nohup [指令與參數] & <==在終端機背景中工作

需要注意的是, nohup 並不支援 bash 內建的指令,因此你的指令必須要是外部指令才行。

# 1. 先編輯一支會『睡著 500 秒』的程式:
[root@www ~]# vim sleep500.sh
#!/bin/bash
/bin/sleep 500s
/bin/echo "I have slept 500 seconds."
 
# 2. 丟到背景中去執行,並且立刻登出系統:
[root@www ~]# chmod a+x sleep500.sh
[root@www ~]# nohup ./sleep500.sh &
[1] 5074
[root@www ~]# nohup: appending output to ‘nohup.out’ <==會告知這個訊息!
[root@www ~]# exit

 

如果你再次登入的話,再使用 pstree 去查閱你的程序,會發現 sleep500.sh 還在執行中喔!並不會被中斷掉!這樣瞭解意思了嗎?由於我們的程式最後會輸出一個訊息,但是 nohup 與終端機其實無關了, 因此這個訊息的輸出就會被導向『 ~/nohup.out ,所以你才會看到上述指令中,當你輸入 nohup 後, 會出現那個提示訊息囉。

如果你想要讓在背景的工作在你登出後還能夠繼續的執行,那麼使用 nohup 搭配 & 是不錯的運作情境喔! 可以參考看看!

 

下面的章节讲解的是程序(process),注意理解与job的区别

程序的觀察 (ps, top, pstree)

 

* ps :將某個時間點的程序運作情況擷取下來

[root@www ~]# ps aux  <==觀察系統所有的程序資料
[root@www ~]# ps -lA  <==也是能夠觀察所有系統的資料
[root@www ~]# ps axjf <==連同部分程序樹狀態
選項與參數:
-A  :所有的 process 均顯示出來,與 -e 具有同樣的效用;
-a  :不與 terminal 有關的所有 process 
-u  :有效使用者 (effective user) 相關的 process 
x   :通常與 a 這個參數一起使用,可列出較完整資訊。
輸出格式規劃:
l   :較長、較詳細的將該 PID 的的資訊列出;
j   :工作的格式 (jobs format)
-f  :做一個更為完整的輸出。

 

通常建議你直接背兩個比較不同的選項, 一個是只能查閱自己 bash 程序的『 ps -l 』一個則是可以查閱所有系統運作的程序『 ps aux !注意,你沒看錯,是『 ps aux 』沒有那個減號 (-)

 
使用 ps -l 則僅列出與你的操作環境 (bash) 有關的程序而已, 亦即最上層的父程序會是你自己的 bash 而沒有延伸到 init 這支程序去
 
範例一:將目前屬於您自己這次登入的 PID 與相關資訊列示出來(只與自己的 bash 有關)
[root@www ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 13639 13637  0  75   0 -  1287 wait   pts/1    00:00:00 bash
4 R     0 13700 13639  0  77   0 -  1101 -      pts/1    00:00:00 ps

其中

  • F:代表這個程序旗標 (process flags),說明這個程序的總結權限,常見號碼有:
    • 若為 4 表示此程序的權限為 root
    • 若為 1 則表示此子程序僅進行複製(fork)而沒有實際執行(exec)
  • S:代表這個程序的狀態 (STAT),主要的狀態有:
    • R (Running):該程式正在運作中;
    • S (Sleep):該程式目前正在睡眠狀態(idle),但可以被喚醒(signal)
    • D :不可被喚醒的睡眠狀態,通常這支程式可能在等待 I/O 的情況(ex>列印)
    • T :停止狀態(stop),可能是在工作控制(背景暫停)或除錯 (traced) 狀態;
    • Z (Zombie):僵屍狀態,程序已經終止但卻無法被移除至記憶體外。
  • UID/PID/PPID:代表『此程序被該 UID 所擁有/程序的 PID 號碼/此程序的父程序 PID 號碼』
  • C:代表 CPU 使用率,單位為百分比;
  • PRI/NIPriority/Nice 的縮寫,代表此程序被 CPU 所執行的優先順序,數值越小代表該程序越快被 CPU 執行。詳細的 PRI NI 將在下一小節說明。
  • ADDR/SZ/WCHAN:都與記憶體有關,ADDR kernel function,指出該程序在記憶體的哪個部分,如果是個 running 的程序,一般就會顯示『 - / SZ 代表此程序用掉多少記憶體 / WCHAN 表示目前程序是否運作中,同樣的, 若為 - 表示正在運作中。
  • TTY:登入者的終端機位置,若為遠端登入則使用動態終端介面 (pts/n)
  • TIME:使用掉的 CPU 時間,注意,是此程序實際花費 CPU 運作的時間,而不是系統時間;
  • CMD:就是 command 的縮寫,造成此程序的觸發程式之指令為何。

所以你看到的 ps -l 輸出訊息中,他說明的是:『bash 的程式屬於 UID 0 的使用者,狀態為睡眠 (sleep), 之所以為睡眠因為他觸發了 ps (狀態為 run) 之故此程序的 PID 13639,優先執行順序為 75 , 下達 bash 所取得的終端介面為 pts/1 ,運作狀態為等待 (wait) 。』

接下來讓我們使用 ps 來觀察一下系統內所有的程序狀態吧!

範例二:列出目前所有的正在記憶體當中的程序:
[root@www ~]# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   2064   616 ?        Ss   Mar11   0:01 init [5]
root         2  0.0  0.0      0     0 ?        S<   Mar11   0:00 [migration/0]
root         3  0.0  0.0      0     0 ?        SN   Mar11   0:00 [ksoftirqd/0]
.....(中間省略).....
root     13639  0.0  0.2   5148  1508 pts/1    Ss   11:44   0:00 -bash
root     14232  0.0  0.1   4452   876 pts/1    R+   15:52   0:00 ps aux
root     18593  0.0  0.0   2240   476 ?        Ss   Mar14   0:00 /usr/sbin/atd

ps aux 顯示的項目中,各欄位的意義為:

  • USER:該 process 屬於那個使用者帳號的?
  • PID :該 process 的程序識別碼。
  • %CPU:該 process 使用掉的 CPU 資源百分比;
  • %MEM:該 process 所佔用的實體記憶體百分比;
  • VSZ :該 process 使用掉的虛擬記憶體量 (Kbytes)
  • RSS :該 process 佔用的固定的記憶體量 (Kbytes)
  • TTY :該 process 是在那個終端機上面運作,若與終端機無關則顯示 ?,另外, tty1-tty6 是本機上面的登入者程序,若為 pts/0 等等的,則表示為由網路連接進主機的程序。
  • STAT:該程序目前的狀態,狀態顯示與 ps -l S 旗標相同 (R/S/T/Z)
  • START:該 process 被觸發啟動的時間;
  • TIME :該 process 實際使用 CPU 運作的時間。
  • COMMAND:該程序的實際指令為何?

一般來說,ps aux 會依照 PID 的順序來排序顯示,我們還是以 13639 那個 PID 那行來說明!該行的意義為『 root 執行的 bash PID 13639,佔用了 0.2% 的記憶體容量百分比,狀態為休眠 (S),該程序啟動的時間為 11:44 , 且取得的終端機環境為 pts/1 。』與 ps aux 看到的其實是同一個程序啦!這樣可以理解嗎?讓我們繼續使用 ps 來觀察一下其他的資訊吧!

範例三:以範例一的顯示內容,顯示出所有的程序:

[root@www ~]# ps -lA

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

4 S     0     1     0  0  76   0 -   435 -      ?        00:00:01 init

1 S     0     2     1  0  94  19 -     0 ksofti ?        00:00:00 ksoftirqd/0

1 S     0     3     1  0  70  -5 -     0 worker ?        00:00:00 events/0

....(以下省略)....

# 你會發現每個欄位與 ps -l 的輸出情況相同,但顯示的程序則包括系統所有的程序。

 

範例四:列出類似程序樹的程序顯示:

[root@www ~]# ps axjf

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND

    0     1     1     1 ?           -1 Ss       0   0:01 init [5]

.....(中間省略).....

    1  4586  4586  4586 ?           -1 Ss       0   0:00 /usr/sbin/sshd

 4586 13637 13637 13637 ?           -1 Ss       0   0:00  /_ sshd: root@pts/1

13637 13639 13639 13639 pts/1    14266 Ss       0   0:00      /_ -bash

13639 14266 14266 13639 pts/1    14266 R+       0   0:00          /_ ps axjf

.....(後面省略).....

上述例子你會發現其實程序之間是有相關性的啦! 不過,其實還可以使用 pstree 來達成這個程序樹喔!以上面的例子來看,鳥哥是透過 sshd 提供的網路服務取得一個程序, 該程序提供 bash 給我使用,而我透過 bash 再去執行 ps axjf

 

範例五:找出與 cron syslog 這兩個服務有關的 PID 號碼?

[root@www ~]# ps aux | egrep '(cron|syslog)'

root   4286  0.0  0.0  1720   572 ?      Ss  Mar11   0:00 syslogd -m 0

root   4661  0.0  0.1  5500  1192 ?      Ss  Mar11   0:00 crond

root  14286  0.0  0.0  4116   592 pts/1  R+  16:15   0:00 egrep (cron|syslog)

# 所以號碼是 4286 4661 這兩個囉!就是這樣找的啦!

 

通常,造成僵屍程序的成因是因為該程序應該已經執行完畢,或者是因故應該要終止了,但是該程序的父程序卻無法完整的將該程序結束掉,而造成那個程序一直存在記憶體當中如果你發現在某個程序的 CMD 後面還接上 時,就代表該程序是僵屍程序啦

例如

apache 8683 0.0  0.9 884 992 ?  Z  14:33  0:00 /usr/sbin/httpd

对于僵尸程序可以通过kill命令来删除。

 

top:動態觀察程序的變化

相對於 ps 是擷取一個時間點的程序狀態, top 則可以持續偵測程序運作的狀態!使用方式如下:

[root@www ~]# top [-d 數字] | top [-bnp]

選項與參數:

-d  :後面可以接秒數,就是整個程序畫面更新的秒數。預設是 5 秒;

-b  :以批次的方式執行 top ,還有更多的參數可以使用喔!

      通常會搭配資料流重導向來將批次的結果輸出成為檔案。

-n  :與 -b 搭配,意義是,需要進行幾次 top 的輸出結果。

-p  :指定某些個 PID 來進行觀察監測而已。

top 執行過程當中可以使用的按鍵指令:

        ? :顯示在 top 當中可以輸入的按鍵指令;

        P :以 CPU 的使用資源排序顯示;

        M :以 Memory 的使用資源排序顯示;

        N :以 PID 來排序喔!

        T :由該 Process 使用的 CPU 時間累積 (TIME+) 排序。

        k :給予某個 PID 一個訊號  (signal)

        r :給予某個 PID 重新制訂一個 nice 值。

        q :離開 top 軟體的按鍵。

範例一:每兩秒鐘更新一次 top ,觀察整體資訊:

[root@www ~]# top -d 2

top - 17:03:09 up 7 days, 16:16,  1 user,  load average: 0.00, 0.00, 0.00

Tasks:  80 total,   1 running,  79 sleeping,   0 stopped,   0 zombie

Cpu(s):  0.5%us,  0.5%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

Mem:    742664k total,   681672k used,    60992k free,   125336k buffers

Swap:  1020088k total,       28k used,  1020060k free,   311156k cached

    <==如果加入 k r 時,就會有相關的字樣出現在這裡喔!

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND    

14398 root      15   0  2188 1012  816 R  0.5  0.1   0:00.05 top

    1 root      15   0  2064  616  528 S  0.0  0.1   0:01.38 init

    2 root      RT  -5     0    0    0 S  0.0  0.0   0:00.00 migration/0

    3 root      34  19     0    0    0 S  0.0  0.0   0:00.00 ksoftirqd/0

在預設的情況下,每次更新程序資源的時間為 5 秒,不過,可以使用 -d 來進行修改。 top 主要分為兩個畫面,上面的畫面為整個系統的資源使用狀態,基本上總共有六行,顯示的內容依序是

  • 第一行(top...):這一行顯示的資訊分別為:
    • 目前的時間,亦即是 17:03:09 那個項目;
    • 開機到目前為止所經過的時間,亦即是 up 7days, 16:16 那個項目;
    • 已經登入系統的使用者人數,亦即是 1 user項目;
    • 系統在 1, 5, 15 分鐘的平均工作負載。batch工作方式為負載小於 0.8 就是這個負載囉!代表的是 1, 5, 15 分鐘,系統平均要負責運作幾個程序(工作)的意思。 越小代表系統越閒置,若高於 1 得要注意你的系統程序是否太過繁複了!
  • 第二行(Tasks...):顯示的是目前程序的總量與個別程序在什麼狀態(running, sleeping, stopped, zombie)。 比較需要注意的是最後的 zombie 那個數值,如果不是 0 !好好看看到底是那個 process 變成僵屍了吧?
  • 第三行(Cpus...):顯示的是 CPU 的整體負載,每個項目可使用 ? 查閱。需要特別注意的是 %wa ,那個項目代表的是 I/O wait, 通常你的系統會變慢都是 I/O 產生的問題比較大!因此這裡得要注意這個項目耗用 CPU 的資源喔! 另外,如果是多核心的設備,可以按下數字鍵『1』來切換成不同 CPU 的負載率。
  • 第四行與第五行:表示目前的實體記憶體與虛擬記憶體 (Mem/Swap) 的使用情況。 再次重申,要注意的是 swap 的使用量要盡量的少!如果 swap 被用的很大量,表示系統的實體記憶體實在不足!
  • 第六行:這個是當在 top 程式當中輸入指令時,顯示狀態的地方。

至於 top 下半部分的畫面,則是每個 process 使用的資源情況。比較需要注意的是:

  • PID :每個 process ID 啦!
  • USER:該 process 所屬的使用者;
  • PR Priority 的簡寫,程序的優先執行順序,越小越早被執行;
  • NI Nice 的簡寫,與 Priority 有關,也是越小越早被執行;
  • %CPUCPU 的使用率;
  • %MEM:記憶體的使用率;
  • TIME+CPU 使用時間的累加;

top 預設使用 CPU 使用率 (%CPU) 作為排序的重點,如果你想要使用記憶體使用率排序,則可以按下『M』, 若要回復則按下『P』即可。如果想要離開 top 則按下『 q 』吧!如果你想要將 top 的結果輸出成為檔案時, 可以這樣做:

範例二:將 top 的資訊進行 2 次,然後將結果輸出到 /tmp/top.txt

[root@www ~]# top -b -n 2 > /tmp/top.txt

# 這樣一來,嘿嘿!就可以將 top 的資訊存到 /tmp/top.txt 檔案中了。

 

範例三:我們自己的 bash PID 可由 $$ 變數取得,請使用 top 持續觀察該 PID

[root@www ~]# echo $$

13639  <==就是這個數字!他是我們 bash PID

[root@www ~]# top -d 2 -p 13639

top - 17:31:56 up 7 days, 16:45,  1 user,  load average: 0.00, 0.00, 0.00

Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie

Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

Mem:    742664k total,   682540k used,    60124k free,   126548k buffers

Swap:  1020088k total,       28k used,  1020060k free,   311276k cached

 

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND

13639 root      15   0  5148 1508 1220 S  0.0  0.2   0:00.18 bash

看到沒!就只會有一支程序給你看!很容易觀察吧!好,那麼如果我想要在 top 底下進行一些動作呢? 比方說,修改 NI 這個數值呢?可以這樣做:

範例四:承上題,上面的 NI 值是 0 ,想要改成 10 的話?

# 在範例三的 top 畫面當中直接按下 r 之後,會出現如下的圖樣!

top - 17:34:24 up 7 days, 16:47,  1 user,  load average: 0.00, 0.00, 0.00

Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie

Cpu(s):  0.0%us,  0.0%sy,  0.0%ni, 99.5%id,  0.0%wa,  0.0%hi,  0.5%si,  0.0%st

Mem:    742664k total,   682540k used,    60124k free,   126636k buffers

Swap:  1020088k total,       28k used,  1020060k free,   311276k cached

PID to renice: 13639  <==按下 r 然後輸入這個 PID 號碼

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND

13639 root      15   0  5148 1508 1220 S  0.0  0.2   0:00.18 bash

#在你完成上面的動作後,在狀態列會出現如下的資訊:

Renice PID 13639 to value: 10   <==這是 nice

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND

一般來說,如果想要找出最損耗 CPU 資源的那個程序時,大多使用的就是 top 這支程式啦!然後強制以 CPU 使用資源來排序 ( top 當中按下 P 即可), 就可以很快的知道啦

 

pstree

[root@www ~]# pstree [-A|U] [-up]

選項與參數:

-A  :各程序樹之間的連接以 ASCII 字元來連接;

-U  :各程序樹之間的連接以萬國碼的字元來連接。在某些終端介面下可能會有錯誤;

-p  :並同時列出每個 process PID

-u  :並同時列出每個 process 的所屬帳號名稱。

 

範例一:列出目前系統上面所有的程序樹的相關性:

[root@www ~]# pstree -A

init-+-acpid

     |-atd

     |-auditd-+-audispd---{audispd}  <==這行與底下一行為 auditd 分出來的子程序

     |        `-{auditd}

     |-automount---4*[{automount}]   <==預設情況下,相似的程序會以數字顯示

....(中間省略)....

     |-sshd---sshd---bash---pstree   <==就是我們指令執行的那個相依性!

....(底下省略)....

 

範例二:承上題,同時秀出 PID users

[root@www ~]# pstree -Aup

init(1)-+-acpid(4555)

        |-atd(18593)

        |-auditd(4256)-+-audispd(4258)---{audispd}(4261)

        |              `-{auditd}(4257)

        |-automount(4536)-+-{automount}(4537) <==程序相似但 PID 不同!

        |                 |-{automount}(4538)

        |                 |-{automount}(4541)

        |                 `-{automount}(4544)

....(中間省略)....

        |-sshd(4586)---sshd(16903)---bash(16905)---pstree(16967)

....(中間省略)....

        |-xfs(4692,xfs)   <==因為此程序擁有者並非執行 pstree 者!所以列出帳號

....(底下省略)....

# 在括號 () 內的即是 PID 以及該程序的 owner 喔!不過,由於我是使用

# root 的身份執行此一指令,所以屬於 root 的程序就不會顯示出來啦!

 

如果要找程序之間的相關性,這個 pstree 真是好用到不行! 一般連結符號可以使用 ASCII 碼即可,但有時因為語系問題會主動的以 Unicode 的符號來連結,但因為可能終端機無法支援該編碼,或許會造成亂碼問題。因此可以加上 -A 選項來克服此類線段亂碼問題

pstree 的輸出我們也可以很清楚的知道,所有的程序都是依附在 init 這支程序底下的仔細看一下,這支程序的 PID 是一號喔!因為他是由 Linux 核心所主動呼叫的第一支程式!所以 PID 就是一號了。 這也是我們剛剛提到僵屍程序時有提到,為啥發生僵屍程序需要重新開機? 因為 init 要重新啟動,而重新啟動 init 就是 reboot 囉!

如果還想要知道 PID 與所屬使用者,加上 -u -p 兩個參數即可。我們前面不是一直提到, 如果子程序掛點或者是老是砍不掉子程序時,該如何找到父程序嗎?呵呵!用這個 pstree 就對了

程序的管理

程序是如何互相管理的呢?是透過給予該程序一個訊號 (signal) 去告知該程序你想要讓她作什麼!

要給予某個已經存在背景中的工作某些動作時,是直接給予一個訊號給該工作號碼即可。那麼到底有多少 signal 呢? 你可以使用 kill -l (小寫的 L ) 或者是 man 7 signal 都可以查詢到

一般來說,你只要記得『1, 9, 15』這三個號碼的意義即可。那麼我們如何傳送一個訊號給某個程序呢?就透過 kill killall

 

* kill -signal PID

Kill的语法见上面章节。

kill 可以幫我們將這個 signal 傳送給某個工作 (%jobnumber) 或者是某個 PID (直接輸入數字)。 要再次強調的是: kill 後面直接加數字與加上 %number 的情況是不同的! 這個很重要喔!因為工作控制中有 1 號工作,但是 PID 1 號則是專指『 init 』這支程式!你怎麼可以將 init 關閉呢?關閉 init ,你的系統就當掉了啊!所以記得那個 % 是專門用在工作控制的喔!再次提醒“工作控制”与“程序控制”的区别

 

* killall -signal 指令名稱

由於 kill 後面必須要加上 PID (或者是 job number),所以,通常 kill 都會配合 ps, pstree 等指令,因為我們必須要找到相對應的那個程序的 ID 嘛!但是,如此一來,很麻煩~有沒有可以利用『下達指令的名稱』來給予訊號的舉例來說,能不能直接將 syslog 這個程序給予一個 SIGHUP 的訊號呢?可以的! killall 吧!

 

[root@www ~]# killall [-iIe] [command name]

選項與參數:

-i  interactive 的意思,互動式的,若需要刪除時,會出現提示字元給使用者;

-e  exact 的意思,表示『後面接的 command name 要一致』,但整個完整的指令

      不能超過 15 個字元。

-I  :指令名稱(可能含參數)忽略大小寫。

範例一:給予 syslogd 這個指令啟動的 PID 一個 SIGHUP 的訊號

[root@www ~]# killall -1 syslogd

# 如果用 ps aux 仔細看一下,syslogd 才是完整的指令名稱。若包含整個參數,

# syslogd -m 0 才是完整的呢!

 

範例二:強制終止所有以 httpd 啟動的程序

[root@www ~]# killall -9 httpd

 

範例三:依次詢問每個 bash 程式是否需要被終止運作!

[root@www ~]# killall -i -9 bash

Kill bash(16905) ? (y/N) n <==這個不殺!

Kill bash(17351) ? (y/N) y <==這個殺掉!

# 具有互動的功能!詢問你是否要刪除 bash 程式。注意,若沒有-i的參數,

# 所有的 bash 都會被這個 root 給殺掉!包括 root 自己的 bash 喔!

 

總之,要刪除某個程序,我們可以使用 PID 或者是啟動該程序的指令名稱,而如果要刪除某個服務呢?呵呵!最簡單的方法就是利用 killall , 因為他可以將系統當中所有以某個指令名稱啟動的程序全部刪除。舉例來說,上面的範例二當中,系統內所有以 httpd 啟動的程序,就會通通的被刪除啦!

 

關於程序的執行順序

系統同時間有非常多的程序在運行中,只是絕大部分的程序都在休眠 (sleeping) 狀態而已。 想一想,如果所有的程序同時被喚醒,那麼 CPU 應該要先處理那個程序呢?也就是說,那個程序被執行的優先序比較高? 這就得要考慮到程序的優先執行序 (Priority) CPU 排程囉!

 

对于Priority Nice renice的详解(略),详见http://linux.vbird.org/linux_basic/0440processcontrol.php#priority

 

系統資源的觀察

 

* free :觀察記憶體使用情況

[root@www ~]# free [-b|-k|-m|-g] [-t]

選項與參數:

-b  :直接輸入 free 時,顯示的單位是 Kbytes,我們可以使用 b(bytes), m(Mbytes)

      k(Kbytes), g(Gbytes) 來顯示單位喔!

-t  :在輸出的最終結果,顯示實體記憶體與 swap 的總量。

範例一:顯示目前系統的記憶體容量

[root@www ~]# free -m

          total       used    free   shared   buffers    cached

Mem:        725        666      59        0       132       287

-/+ buffers/cache:     245     479

Swap:       996          0     996

仔細看看,我的系統當中有 725MB 左右的實體記憶體,我的 swap 1GB 左右, 那我使用 free -m MBytes 來顯示時,就會出現上面的資訊。Mem 那一行顯示的是實體記憶體的量, Swap 則是虛擬記憶體的量。 total 是總量, used 是已被使用的量, free 則是剩餘可用的量

 

* uname:查閱系統與核心相關資訊

[root@www ~]# uname [-asrmpi]

選項與參數:

-a  :所有系統相關的資訊,包括底下的資料都會被列出來;

-s  :系統核心名稱

-r  :核心的版本

-m  :本系統的硬體名稱,例如 i686 x86_64 等;

-p  CPU 的類型,與 -m 類似,只是顯示的是 CPU 的類型!

-i  :硬體的平台 (ix86)

範例一:輸出系統的基本資訊

[root@www ~]# uname -a

Linux www.vbird.tsai 2.6.18-92.el5 #1 SMP Tue Jun 10 18:49:47 EDT 2008 i686

i686 i386 GNU/Linux

uname 可以列出目前系統的核心版本、 主要硬體平台以及 CPU 類型等等的資訊。

 

* uptime:觀察系統啟動時間與工作負載

顯示出目前系統已經開機多久的時間,以及 1, 5, 15 分鐘的平均負載就是了。還記得 top 吧?沒錯啦!這個 uptime 可以顯示出 top 畫面的最上面一行

[root@www ~]# uptime
 15:39:13 up 8 days, 14:52,  1 user,  load average: 0.00, 0.00, 0.00

 

* netstat :追蹤網路或插槽檔

這個指令比較常被用在網路的監控方面,不過,在程序管理方面也是需要瞭解的啦! 這個指令的執行如下所示:基本上, netstat 的輸出分為兩大部分,分別是網路與系統自己的程序相關性部分

[root@www ~]# netstat -[atunlp]

選項與參數:

-a  :將目前系統上所有的連線、監聽、Socket 資料都列出來

-t  :列出 tcp 網路封包的資料

-u  :列出 udp 網路封包的資料

-n  :不以程序的服務名稱,以埠號 (port number) 來顯示;

-l  :列出目前正在網路監聽 (listen) 的服務;

-p  :列出該網路服務的程序 PID

 

範例一:列出目前系統已經建立的網路連線與 unix socket 狀態

[root@www ~]# netstat

Active Internet connections (w/o servers) <==與網路較相關的部分

Proto Recv-Q Send-Q Local Address        Foreign Address      State

tcp        0    132 192.168.201.110:ssh  192.168.:vrtl-vmf-sa ESTABLISHED

Active UNIX domain sockets (w/o servers)  <==與本機的程序自己的相關性(非網路)

Proto RefCnt Flags       Type       State         I-Node Path

unix  20     [ ]         DGRAM                    9153   /dev/log

unix  3      [ ]         STREAM     CONNECTED     13317  /tmp/.X11-unix/X0

unix  3      [ ]         STREAM     CONNECTED     13233  /tmp/.X11-unix/X0

unix  3      [ ]         STREAM     CONNECTED     13208  /tmp/.font-unix/fs7100

....(中間省略)....

在上面的結果當中,顯示了兩個部分,分別是網路的連線以及 linux 上面的 socket 程序相關性部分。 我們先來看看網際網路連線情況的部分

  • Proto :網路的封包協定,主要分為 TCP UDP 封包;
  • Recv-Q:非由使用者程式連結到此 socket 的複製的總 bytes 數;
  • Send-Q:非由遠端主機傳送過來的 acknowledged bytes 數;
  • Local Address :本地端的 IP:port 情況
  • Foreign Address:遠端主機的 IP:port 情況
  • State :連線狀態,主要有建立(ESTABLISED)及監聽(LISTEN)

我們看上面僅有一條連線的資料,他的意義是:『透過 TCP 封包的連線,遠端的 192.168.:vrtl.. 連線到本地端的 192.168.201.110:ssh ,這條連線狀態是建立 (ESTABLISHED) 的狀態!

除了網路上的連線之外,其實 Linux 系統上面的程序是可以接收不同程序所發送來的資訊,那就是 Linux 上頭的插槽檔 (socket file)socket file 可以溝通兩個程序之間的資訊,因此程序可以取得對方傳送過來的資料。由於有 socket file,因此類似 X Window 這種需要透過網路連接的軟體,目前新版的 distributions 就以 socket 來進行視窗介面的連線溝通了。上表中 socket file 的輸出欄位有:

  • Proto :一般就是 unix 啦;
  • RefCnt:連接到此 socket 的程序數量;
  • Flags :連線的旗標;
  • Type socket 存取的類型。主要有確認連線的 STREAM 與不需確認的 DGRAM 兩種;
  • State :若為 CONNECTED 表示多個程序之間已經連線建立。
  • Path :連接到此 socket 的相關程式的路徑!或者是相關資料輸出的路徑。

以上表的輸出為例,最後那三行在 /tmp/.xx 底下的資料,就是 X Window 視窗介面的相關程序啦!而 PATH 指向的就是這些程序要交換資料的插槽檔案囉!好!那麼 netstat 可以幫我們進行什麼任務呢? 很多喔!我們先來看看,利用 netstat 去看看我們的哪些程序有啟動哪些網路的『後門』呢

範例二:找出目前系統上已在監聽的網路連線及其 PID

[root@www ~]# netstat -tlnp

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address    Foreign Address  State   PID/Program name

tcp        0      0 127.0.0.1:2208   0.0.0.0:*        LISTEN  4566/hpiod

tcp        0      0 0.0.0.0:111      0.0.0.0:*        LISTEN  4328/portmap

tcp        0      0 127.0.0.1:631    0.0.0.0:*        LISTEN  4597/cupsd

tcp        0      0 0.0.0.0:728      0.0.0.0:*        LISTEN  4362/rpc.statd

tcp        0      0 127.0.0.1:25     0.0.0.0:*        LISTEN  4629/sendmail:

tcp        0      0 127.0.0.1:2207   0.0.0.0:*        LISTEN  4571/python

tcp        0      0 :::22            :::*             LISTEN  4586/sshd

# 除了可以列出監聽網路的介面與狀態之外,最後一個欄位還能夠顯示此服務的

# PID 號碼以及程序的指令名稱喔!例如最後一行的 4586 就是該 PID

 

範例三:將上述的本地端 127.0.0.1:631 那個網路服務關閉的話?

[root@www ~]# kill -9 4597

[root@www ~]# killall -9 cupsd

 

我的主機目前到底開了幾個門(ports)!其實,不論主機提供什麼樣的服務, 一定必須要有相對應的 program 在主機上面執行才行啊!舉例來說,我們鳥園的 Linux 主機提供的就是 WWW 服務,那麼我的主機當然有一個程式在提供 WWW 的服務啊!那就是 Apache 這個軟體所提供的啦! ^_^。 所以,當我執行了這個程式之後,我的系統自然就可以提供 WWW 的服務了。那如何關閉啊? 就關掉該程式所觸發的那個程序就好了

 

* dmesg :分析核心產生的訊息

系統在開機的時候,核心會去偵測系統的硬體,你的某些硬體到底有沒有被捉到,那就與這個時候的偵測有關。 但是這些偵測的過程要不是沒有顯示在螢幕上,就是很飛快的在螢幕上一閃而逝!能不能把核心偵測的訊息捉出來瞧瞧? 可以的,那就使用 dmesg

所有核心偵測的訊息,不管是開機時候還是系統運作過程中,反正只要是核心產生的訊息,都會被記錄到記憶體中的某個保護區段。 dmesg 這個指令就能夠將該區段的訊息讀出來的!因為訊息實在太多了,所以執行時可以加入這個管線指令『 | more 』來使畫面暫停

範例一:輸出所有的核心開機時的資訊

[root@www ~]# dmesg | more

 

範例二:搜尋開機的時候,硬碟的相關資訊為何?

[root@www ~]# dmesg | grep -i hd

    ide0: BM-DMA at 0xd800-0xd807, BIOS settings: hda:DMA, hdb:DMA

    ide1: BM-DMA at 0xd808-0xd80f, BIOS settings: hdc:pio, hdd:pio

hda: IC35L040AVER07-0, ATA DISK drive

hdb: ASUS DRW-2014S1, ATAPI CD/DVD-ROM drive

hda: max request size: 128KiB

....(底下省略)....

 

範例三:網卡的硬件资讯

[root@www ~]# dmesg | grep -i eth

 

* vmstat :偵測系統資源變化

如果你想要動態的瞭解一下系統資源的運作,那麼這個 vmstat 確實可以玩一玩!vmstat 可以偵測『 CPU / 記憶體 / 磁碟輸入輸出狀態』等等,如果你想要瞭解一部繁忙的系統到底是哪個環節最累人, 可以使用 vmstat 分析看看。

详解略。

 

 

你可能感兴趣的:(Linux)