Linux系列7:進程管理及系統資源查看

0 引言

一個程序被加載到內存當中運行,那麼在內存中的與這個程序執行相關的數據就被稱爲進程(process)。進程是操作系統上非常重要的概念。所有操作系統上跑的數據都是以進程的形態存在。那麼系統的進程有哪些狀態?不同的狀態會如何影響系統的運作?進程之間是否可以互相控管等等的,這都是我們所必須知道的內容。

1 什麼是進程

由前面一連幾個章節的數據看來,我們一直強調在 Linux 底下所有的命令與你能夠進行的動作都與權限有關, 而系統如何判定你的權限呢?當然就是第十四章帳號管理當中提到的 UID/GID 的相關概念,以及文件的屬性相關性羅!再進一步來解釋,你現在大概知道,在 Linux 系統當中:『觸發任何一個事件時,系統都會將他定義成爲一個程序,並且給予這個程序一個 ID ,稱爲 PID,同時依據啓發這個程序的使用者與相關屬性關係,給予這個 PID 一組有效的權限配置。』 從此以後,這個 PID 能夠在系統上面進行的動作,就與這個 PID 的權限有關了!

看這個定義似乎沒有什麼很奇怪的地方,不過,您得要了解什麼叫做『觸發事件』才行啊! 我們在什麼情況下會觸發一個事件?

1.1 進程與程序(process&program)

我們如何產生一個進程呢?其實很簡單啦,就是『運行一個程序或命令』就可以觸發一個事件而取得一個 PID 羅!我們說過,系統應該是僅認識 binary file 的,那麼當我們要讓系統工作的時候,當然就是需要啓動一個 binary file 羅,那個 binary file 就是程序 (program) 啦!

那我們知道,每個程序都有三組人馬的權限,每組人馬都具有 r/w/x 的權限,所以:『不同的使用者身份運行這個 program 時,系統給予的權限也都不相同!』舉例來說,我們可以利用 touch 來創建一個空的文件,當 root 運行這個 touch 命令時,他取得的是 UID/GID = 0/0 的權限,而當 dmtsai (UID/GID=501/501) 運行這個 touch 時,他的權限就跟 root 不同啦!我們將這個概念繪製成圖示來瞧瞧如下:
在這裏插入圖片描述
如上圖所示,程序一般是放置在實體磁碟中,然後透過使用者的運行來觸發。觸發後會加載到內存中成爲一個個體,那就是進程。 爲了操作系統可管理這個程序,因此程序有給予運行者的權限/屬性等參數,幷包括程序所需要的命令碼與數據或文件數據等, 最後再給予一個 PID 。系統就是透過這個 PID 來判斷該 process 是否具有權限進行工作的!他是很重要的哩!

舉個更常見的例子,我們要操作系統的時候,通常是利用連線程序或者直接在主機前面登陸,然後取得我們的 shell 對吧!那麼,我們的 shell 是 bash 對吧,這個 bash 在 /bin/bash 對吧,那麼同時間的每個人登陸都是運行 /bin/bash 對吧!不過,每個人取得的權限就是不同!也就是說,我們可以這樣看:

在這裏插入圖片描述
也就是說,當我們登陸並運行 bash 時,系統已經給我們一個 PID 了,這個 PID 就是依據登陸者的 UID/GID (/etc/passwd) 來的啦~以上面的圖 1.1.2 配合圖 1.1.1 來做說明的話,我們知道 /bin/bash 是一個程序 (program),當 dmtsai 登陸後,他取得一個 PID 號碼爲 2234 的進程,這個程序的 User/Group 都是 dmtsai ,而當這個進程進行其他作業時,例如上面提到的 touch 這個命令時, 那麼由這個進程衍生出來的其他程序在一般狀態下,也會沿用這個進程的相關權限的!

讓我們將程序與進程作個總結:

  • 程序 (program):通常爲 binary program ,放置在儲存媒體中 (如硬盤、光盤、軟盤、磁帶等), 爲實體文件的型態存在;

  • 進程 (process):程序被觸發後,運行者的權限與屬性、程序的程序碼與所需數據等都會被加載內存中, 操作系統並給予這個內存內的單元一個識別碼 (PID),可以說,進程就是一個正在運行中的程序。

1.2 子進程與父進程

在上面的說明裏面,我們有提到所謂的『衍生出來的進程』,是什麼東西?這樣說好了,當我們登陸系統後,會取得一個 bash 的 shell ,然後,我們用這個 bash 提供的界面去運行另一個命令,例如 /usr/bin/passwd 或者是 touch 等等,那些另外運行的命令也會被觸發成爲 PID。那個後來運行命令才產生的 PID 就是『子進程』了,而在我們原本的 bash 環境下,就稱爲『父進程』了!借用我們在 十一章 Bash 談到的 export 所用的圖示好了:
在這裏插入圖片描述
所以你必須要知道,進程彼此之間是有相關性的!以上面的圖示來看,連續運行兩個 bash 後,第二個 bash 的父進程就是前一個 bash。因爲每個進程都有一個 PID ,那某個進程的父進程該如何判斷?就透過 Parent PID (PPID) 來判斷即可。此外,由十一章的 export 內容我們也探討過環境變量的繼承問題,子進程可以取得父進程的環境變量啦! 讓我們來進行底下的練習,以瞭解什麼是子進程/父進程。

例題:
請在目前的 bash 環境下,再觸發一次 bash ,並以『 ps -l 』這個命令觀察程序相關的輸出資訊。
答:
直接運行 bash ,會進入到子程序的環境中,然後輸入 ps -l 後,出現:
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 8074 8072 2 76 0 - 1287 wait pts/1 00:00:00 bash
0 S 0 8102 8074 4 76 0 - 1287 wait pts/1 00:00:00 bash
4 R 0 8118 8102 0 78 0 - 1101 - pts/1 00:00:00 ps
有看到那個 PID 與 PPID 嗎?第一個 bash 的 PID 與第二個 bash 的 PPID 都是 8074 啊, 因爲第二個 bash 是來自於第一個所產生的嘛!另外,每部主機的進程啓動狀態都不一樣, 所以在你的系統上面看到的 PID 與我這裏的顯示一定不同!那是正常的!詳細的 ps 命令我們會在本章稍後介紹, 這裏你只要知道 ps -l 可以查閱到相關的程序資訊即可。

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

1.3 fork and exec:程序呼叫的流程

其實子進程與父進程之間的關係還挺複雜的,最大的複雜點在於進程互相之間的呼叫。在 Linux 的進程呼叫通常稱爲 fork-and-exec 的流程 (注1)!進程都會藉由父進程以複製 (fork) 的方式產生一個一模一樣的子進程, 然後被複製出來的子進程再以 exec 的方式來運行實際要進行的程序,最終就成爲一個子進程的存在。 整個流程有點像底下這張圖:
在這裏插入圖片描述
(1)系統先以 fork 的方式複製一個與父進程相同的緩存程序,這個進程與父進程唯一的差別就是 PID 不同! 但是這個緩存進程還會多一個 PPID 的參數,PPID 如前所述,就是父進程的進程識別碼啦!然後
(2)緩存進程開始以 exec 的方式加載實際要運行的程序,以上述圖示來講,新的程序名稱爲 qqq ,最終子進程的程序碼就會變成 qqq 了!

1.4 系統或網絡服務:常駐在內存的進程

如果就我們之前學到的一些命令數據來看,其實我們下達的命令都很簡單,包括用 ls 顯示文件啊、用 touch 創建文件啊、rm/mkdir/cp/mv 等命令管理文件啊、chmod/chown/passwd 等等的命令來管理權限等等的,不過, 這些命令都是運行完就結束了。也就是說,該項命令被觸發後所產生的 PID 很快就會終止呢! 那有沒有一直在運行的進程啊?當然有啊!而且多的是呢!

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

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

Tips:
以crontab來說,他的主要執行程序名稱應該是cron或at纔對,爲啥要加上個d在後面?而成爲crond,atd呢?就是因爲Linux希望我們可以簡單的判斷該進程是否爲daemon,所以,一般daemon類型的程序都會加上d在文件名後,包括服務器篇我們看到的httpd,vsftpd,sshd等等都是這樣的。

1.5 Linux的多人多任務環境

我們現在知道了,其實在 Linux 底下運行一個命令時,系統會將相關的權限、屬性、程序碼與數據等均加載內存, 並給予這個單元一個程序識別碼 (PID),最終該命令可以進行的任務則與這個 PID 的權限有關。根據這個說明,我們就可以簡單的瞭解,爲什麼 Linux 這麼多用戶,但是卻每個人都可以擁有自己的環境了吧!_ !底下我們來談談 Linux 多人多工環境的特色:

多人環境:
Linux 最棒的地方就在於他的多人多任務環境了!那麼什麼是『多人多任務』?在 Linux 系統上面具有多種不同的帳號, 每種帳號都有都有其特殊的權限,只有一個人具有至高無上的權力,那就是 root (系統管理員)。除了 root 之外,其他人都必須要受一些限制的!而每個人進入 Linux 的環境配置都可以隨著每個人的喜好來配置 (還記得我們在第十一章 BASH 提過的 ~/.bashrc 吧?對了!就是那個光!)!現在知道爲什麼了吧?因爲每個人登陸後取得的 shell 的 PID 不同嘛!

多任務行爲:
我們在第零章談到 CPU 的速度,目前的 CPU 速度可高達幾個 GHz。 這代表 CPU 每秒鐘可以運行 10910^9 這麼多次命令。我們的 Linux 可以讓 CPU 在各個工作間進行切換, 也就是說,其實每個工作都僅佔去 CPU 的幾個命令次數,所以 CPU 每秒就能夠在各個程序之間進行切換啦! 誰叫 CPU 可以在一秒鐘進行這麼多次的命令運行。

CPU 切換程序的工作,與這些工作進入到 CPU 運行的排程 (CPU 排程,非 crontab 排程) 會影響到系統的整體效能! 目前 Linux 使用的多工切換行爲是非常棒的一個機制,幾乎可以將 PC 的性能整個壓榨出來! 由於效能非常好,因此當多人同時登陸系統時,其實會感受到整部主機好像就爲了你存在一般! 這就是多人多工的環境啦!(注2)

多重登陸環境的七個基本終端窗口:
在 Linux 當中,默認提供了六個文字界面登陸窗口,以及一個圖形界面,你可以使用 [Alt]+[F1]…[F7] 來切換不同的終端機界面,而且每個終端機界面的登陸者還可以不同人! 很炫吧!這個東西可就很有用啦!尤其是在某個程序死掉的時候!

其實,這也是多任務環境下所產生的一個情況啦!我們的 Linux 默認會啓動六個終端機登陸環境的程序,所以我們就會有六個終端機介面。 您也可以減少啊!就是減少啓動的終端機程序就好了。詳細的數據可以先查閱 /etc/inittab 這個文件,未來我們在啓動管理流程 (第二十章) 會再仔細的介紹的!

特殊的程序管理行爲:
以前的鳥哥笨笨的,總是以爲使用 Windows 98 就可以啦!後來,因爲工作的關係,需要使用 Unix 系統,想說我只要在工作機前面就好,纔不要跑來跑去的到 Unix 工作站前面去呢!所以就使用 Windows 連到我的 Unix 工作站工作!好死不死,我一個程序跑下來要 2~3 天,唉~偏偏常常到了第 2.5 天的時候, Windows 98 就給他掛點去!當初真的是給他怕死了~

後來因爲換了新計算機,用了隨機版的 Windows 2000 ,呵呵,這東西真不錯 (指對單人而言) ,在死機的時候, 他可以僅將錯誤的程序踢掉,而不干擾其他的程序進行,呵呵! 從此以後,就不用擔心會死機連連羅!不過,2000 畢竟還不夠好,因爲有的時候還是會死當!

那麼 Linux 會有這樣的問題嗎?老實說, Linux 幾乎可以說絕對不會死機的!因爲他可以在任何時候, 將某個被困住的程序殺掉,然後再重新運行該程序而不用重新啓動!夠炫吧!那麼如果我在 Linux 下以文字界面登陸,在螢幕當中顯示錯誤信息後就掛了~動都不能動,該如何是好!? 這個時候那默認的七個窗口就幫上忙啦!你可以隨意的再按 [Alt]+[F1]…[F7] 來切換到其他的終端機界面,然後以 ps -aux 找出剛剛的錯誤程序,然後給他 kill 一下,哈哈,回到剛剛的終端機界面!恩~棒!又回覆正常羅!

爲什麼可以這樣做呢?我們剛剛不是提過嗎?每個程序之間可能是獨立的,也可能有相依性, 只要到獨立的程序當中,刪除有問題的那個程序,當然他就可以被系統移除掉啦!_

bash 環境下的工作管理 (job control)
我們在上一個小節有提到所謂的『父進程、子進程』的關係,那我們登陸 bash 之後, 就是取得一個名爲 bash 的 PID 了,而在這個環境底下所運行的其他命令, 就幾乎都是所謂的子程序了。那麼,在這個單一的 bash 介面下,我可不可以進行多個工作啊? 當然可以啦!可以『同時』進行喔!舉例來說,我可以這樣做:

[root@www ~]# cp file1 file2 &

在這一串命令中,重點在那個 & 的功能,他表示將 file1 這個文件複製爲 file2 ,且放置於背景中運行, 也就是說運行這一個命令之後,在這一個終端介面仍然可以做其他的工作!而當這一個命令 (cp file1 file2) 運行完畢之後,系統將會在你的終端介面顯示完成的消息!很便利喔!

多人多任務的系統資源分配問題考慮:
多人多工確實有很多的好處,但其實也有管理上的困擾,因爲使用者越來越多, 將導致你管理上的困擾哩!另外,由於使用者日盛,當使用者達到一定的人數後, 通常你的機器便需要升級了,因爲 CPU 的運算與 RAM 的大小可能就會不敷使用!

舉個例子來說,鳥哥之前的網站管理的有點不太好,因爲使用了一個很複雜的人數統計程序, 這個程序會一直去取用 MySQL 數據庫的數據,偏偏因爲流量大,造成 MySQL 很忙碌。 在這樣的情況下,當鳥哥要登陸去寫網頁數據,或者要去使用討論區的資源時, 哇!慢的很!簡直就是『龜速』啊!後來終於將這個程序停止不用了, 以自己寫的一個小程序來取代,呵呵!這樣才讓 CPU 的負載 (loading) 整個降下來~ 用起來順暢多了! _

2 工作管理

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

2.1 什麼是工作管理

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

或許你會覺得很奇怪啊,既然我可以在六個終端介面登陸,那何必使用 job control 呢? 真是脫褲子放屁,多此一舉啊!不要忘記了呢,我們可以在 /etc/security/limits.conf (第十四章) 裏面配置使用者同時可以登陸的連線數,在這樣的情況下,某些使用者可能僅能以一個連線來工作呢! 所以羅,你就得要了解一下這種工作管理的模式了!此外,這個章節內容也會牽涉到很多的數據流重導向,所以,如果忘記的話, 務必回到第十一章 BASH Shell 看一看喔!

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

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

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

接下來讓我們實際來管理這些工作吧!

2.2 job control的管理

如前所述,bash 只能夠管理自己的工作而不能管理其他 bash 的工作,所以即使你是 root 也不能夠將別人的 bash 底下的 job 給他拿過來運行。此外,又分前景與背景,然後在背景裏面的工作狀態又可以分爲『暫停 (stop)』與『運行中 (running)』。那實際進行 job 控制的命令有哪些?底下就來談談。

2.2.1 直接將命令丟到背景中『運行』的 &

如同前面提到的,我們在只有一個 bash 的環境下,如果想要同時進行多個工作, 那麼可以將某些工作直接丟到背景環境當中,讓我們可以繼續操作前景的工作!那麼如何將工作丟到背景中? 最簡單的方法就是利用『 & 』這個玩意兒了!舉個簡單的例子,我們要將 /etc/ 整個備份成爲 /tmp/etc.tar.gz 且不想要等待,那麼可以這樣做:


[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 運行的數據流,
# 由於我們沒有加上數據流重導向,所以會影響畫面!不過不會影響前景的操作喔!

仔細的瞧一瞧,我在輸入一個命令後,在該命令的最後面加上一個『 & 』代表將該命令丟到背景中, 此時 bash 會給予這個命令一個『工作號碼(job number)』,就是那個 [1] 啦!至於後面那個 8400 則是該命令所觸發的『 PID 』了! 而且,有趣的是,我們可以繼續操作 bash 呢!很不賴吧! 不過,那麼丟到背景中的工作什麼時候完成?完成的時候會顯示什麼?如果你輸入幾個命令後,突然出現這個數據:

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

就代表 [1] 這個工作已經完成 (Done) ,該工作的命令則是接在後面那一串命令列。 這樣瞭解了吧!另外,這個 & 代表:『將工作丟到背景中去運行』喔! 注意到那個『運行』的字眼!此外,這樣的情況最大的好處是: 不怕被 [ctrl]+c 中斷的啦! 此外,將工作丟到背景當中要特別注意數據的流向喔!包括上面的信息就有出現錯誤信息,導致我的前景被影響。 雖然只要按下 [enter] 就會出現提示字節。但如果我將剛剛那個命令改成:

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

情況會怎樣?在背景當中運行的命令,如果有 stdout 及 stderr 時,他的數據依舊是輸出到螢幕上面的, 所以,我們會無法看到提示字節,當然也就無法完好的掌握前景工作。同時由於是背景工作的 tar , 此時你怎麼按下 [ctrl]+c 也無法停止螢幕被搞的花花綠綠的!所以羅,最佳的狀況就是利用數據流重導向, 將輸出數據傳送至某個文件中。舉例來說,我可以這樣做:

[root@www ~]# tar -zpcvf /tmp/etc.tar.gz /etc > /tmp/log.txt 2>&1 &
[1] 8429
[root@www ~]# 

命令中的1表示標準輸出,&1表示標準輸出的引用,2表示標準錯誤輸出。上面命令的意思是將標準輸出重定向到文件/tmp/log.txt,將標準錯誤輸出重定向到標準輸出的引用。如此一來,標準輸出和錯誤輸出都給他傳送到 /tmp/log.txt 當中,當然就不會影響到我們前景的作業了。 這樣說,您應該可以更清楚數據流重導向的重要性了吧!有關command>file 2>&1 & 命令的詳細解釋請參考Linux系列8:command>file 2>&1 & 命令詳解

Tips:
工作號碼 (job number) 只與你這個 bash 環境有關,但是他既然是個命令觸發的咚咚,所以當然一定是一個程序, 因此你會觀察到有 job number 也搭配一個 PID !

2.2.2 將『目前』的工作丟到背景中『暫停』:[ctrl]-z

想個情況:如果我正在使用 vi ,卻發現我有個文件不知道放在哪裏,需要到 bash 環境下進行搜尋,此時是否要結束 vi 呢?呵呵!當然不需要啊!只要暫時將 vi 給他丟到背景當中等待即可。 例如以下的案例:

[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 丟到背景當中的工作都是『暫停』的狀態喔!

2.2.3 觀察目前的背景工作狀態: 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

如果想要知道目前有多少的工作在背景當中,就用 jobs 這個命令吧!一般來說,直接下達 jobs 即可! 不過,如果你還想要知道該 job number 的 PID 號碼,可以加上 -l 這個參數啦! 在輸出的資訊當中,例如上表,仔細看到那個 + - 號喔!那個 + 代表默認的取用工作。 所以說:『目前我有兩個工作在背景當中,兩個工作都是暫停的, 而如果我僅輸入 fg 時,那麼那個 [2] 會被拿到前景當中來處理』!

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

2.2.4 將背景工作拿到前景來處理: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 命令就能夠將背景工作拿到前景來處理羅!不過比較有趣的是最後一個顯示的結果,我們會發現 + 出現在第一個工作後! 怎麼會這樣啊?這是因爲你剛剛利用 fg %1 將第一號工作捉到前景後又放回背景,此時最後一個被放入背景的將變成 vi 那個命令動作, 所以當然 [1] 後面就會出現 + 了!瞭解乎!另外,如果輸入『 fg - 』 則代表將 - 號的那個工作號碼拿出來,上面就是 [2]- 那個工作號碼啦!

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

我們剛剛提到,那個 [ctrl]-z 可以將目前的工作丟到背景底下去『暫停』, 那麼如何讓一個工作在背景底下『 Run 』呢?我們可以在底下這個案例當中來測試! 注意喔!底下的測試要進行的快一點!_

範例一:一運行 find / -perm +7000 > /tmp/text.txt 後,立刻丟到背景去暫停!
[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 &

看到哪裏有差異嗎?呼呼!沒錯!就是那個狀態列~以經由 Stopping 變成了 Running 羅! 看到差異點,嘿嘿!命令列最後方多了一個 & 的符號羅! 代表該工作被啓動在背景當中了啦!

2.2.6 管理背景當中的工作: kill

剛剛我們可以讓一個已經在背景當中的工作繼續工作,也可以讓該工作以 fg 拿到前景來, 那麼,如果想要將該工作直接移除呢?或者是將該工作重新啓動呢?這個時候就得需要給予該工作一個訊號 (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 就會繼續存在文件系統當中。這樣您應該可以稍微分辨一下了吧?

其實, kill 的妙用是很無窮的啦!他搭配 signal 所詳列的資訊 (用 man 7 signal 去查閱相關數據) 可以讓您有效的管理工作與進程(Process),此外,那個 killall 也是同樣的用法! 至於常用的 signal 您至少需要了解 1, 9, 15 這三個 signal 的意義纔好。 此外, signal 除了以數值來表示之外,也可以使用訊號名稱喔! 舉例來說,上面的範例二就是一個例子啦!至於 signal number 與名稱的對應, 呵呵,使用 kill -l 就知道啦(L的小寫)!

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

2.3 離線管理問題:nohup

要注意的是,我們在工作管理當中提到的『背景』指的是在終端機模式下可以避免 [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 搭配 & 是不錯的運行情境喔! 可以參考看看!

3 進程管理

本章一開始就提到所謂的『進程』的概念,包括進程的觸發、子進程與父進程的相關性等等, 此外,還有那個『進程的相依性』以及所謂的『殭屍進程』等等需要說明的呢!爲什麼進程管理這麼重要呢?這是因爲:

  • 首先,本章一開始就談到的,我們在操作系統時的各項工作其實都是經過某個 PID 來達成的 (包括你的 bash 環境), 因此,能不能進行某項工作,就與該進程的權限有關了。
  • 再來,如果您的 Linux 系統是個很忙碌的系統,那麼當整個系統資源快要被使用光時, 您是否能夠找出最耗系統的那個進程,然後刪除該進程,讓系統恢復正常呢?
  • 此外,如果由於某個進程寫的不好,導致產生一個有問題的進程在內存當中,您又該如何找出他,然後將他移除呢?
  • 如果同時有五六項工作在您的系統當中運行,但其中有一項工作纔是最重要的, 該如何讓那一項重要的工作被最優先運行呢?

所以羅,一個稱職的系統管理員,必須要熟悉進程的管理流程才行,否則當系統發生問題時,還真是很難解決問題呢! 底下我們會先介紹如何觀察進程與進程的狀態,然後再加以進程控制羅!

3.1 進程查看

既然進程這麼重要,那麼我們如何查閱系統上面正在運行當中的進程呢?很簡單啊! 利用靜態的 ps 或者是動態的 top,還能以 pstree 來查閱進程樹之間的關係喔!

3.1.1 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  :做一個更爲完整的輸出。

鳥哥個人認爲 ps 這個命令的 man page 不是很好查閱,因爲很多不同的 Unix 都使用這個 ps 來查閱進程狀態, 爲了要符合不同版本的需求,所以這個 man page 寫的非常的龐大!因此,通常鳥哥都會建議你,直接背兩個比較不同的選項, 一個是隻能查閱自己 bash 進程的『 ps -l 』一個則是可以查閱所有系統運行的進程『 ps aux 』!注意,你沒看錯,是『 ps aux 』沒有那個減號 (-) !先來看看關於自己 bash 進程狀態的觀察:

僅觀察自己的 bash 相關進程: ps -l

範例一:將目前屬於您自己這次登陸的 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

系統整體的進程運行是非常多的,但如果使用 ps -l 則僅列出與你的操作環境 (bash) 有關的進程而已, 亦即最上一級的父進程會是你自己的 bash 而沒有延伸到 init 這支進程去!那麼 ps -l 秀出來的數據有哪些呢? 我們就來觀察看看:

  • 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/NI:Priority/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 那一行代表的意義爲何呢?

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


範例二:列出目前所有的正在內存當中的進程:
[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 -l 與 ps aux 顯示的項目並不相同!在 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 !**這樣可以看的懂了嗎?其他各欄位的意義請 man ps (雖然真的很難 man 的出來!) 羅!

範例五:找出與 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 這兩個羅!就是這樣找的啦!

除此之外,我們必須要知道的是『殭屍 (zombie) 』進程是什麼? 通常,造成殭屍進程的成因是因爲該進程應該已經運行完畢,或者是因故應該要終止了, 但是該進程的父進程卻無法完整的將該進程結束掉,而造成那個進程一直存在內存當中。 如果你發現在某個進程的 CMD 後面還接上 時,就代表該進程是殭屍進程啦,例如:

apache  8683  0.0  0.9 83384 9992 ?   Z  14:33   0:00 /usr/sbin/httpd <defunct>

當系統不穩定的時候就容易造成所謂的殭屍進程,可能是因爲進程寫的不好啦,或者是使用者的操作習慣不良等等所造成。 如果你發現系統中很多殭屍進程時,記得啊!要找出該進程的父進程,然後好好的做個追蹤,好好的進行主機的環境最佳化啊! 看看有什麼地方需要改善的,不要只是直接將他 kill 掉而已呢!不然的話,萬一他一直產生,那可就麻煩了! @_@

事實上,通常殭屍進程都已經無法控管,而直接是交給 init 這支進程來負責了,偏偏 init 是系統第一支運行的進程, 他是所有進程的父進程!我們無法殺掉該進程的 (殺掉他,系統就死掉了!),所以羅,如果產生殭屍進程, 而系統過一陣子還沒有辦法透過核心非經常性的特殊處理來將該進程刪除時,那你只好透過 reboot 的方式來將該進程抹去了!

3.1.2 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 的功能非常多!可以用的按鍵也非常的多!可以參考 man top 的內部說明文件! 鳥哥這裏僅是列出一些鳥哥自己常用的選項而已。接下來讓我們實際觀察一下如何使用 top 與 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

top 也是個挺不錯的進程觀察工具!但不同於 ps 是靜態的結果輸出, top 這個進程可以持續的監測整個系統的進程工作狀態。 在默認的情況下,每次升級進程資源的時間爲 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 有關,也是越小越早被運行;

  • %CPU:CPU 的使用率;

  • %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 文件中了。

這玩意兒很有趣!可以幫助你將某個時段 top 觀察到的結果存成文件,可以用在你想要在系統背景底下運行。 由於是背景底下運行,與終端機的螢幕大小無關,因此可以得到全部的進程畫面!那如果你想要觀察的進程 CPU 與內存使用率都很低,結果老是無法在第一行顯示時,該怎辦?我們可以僅觀察單一進程喔!如下所示:

範例三:我們自己的 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

接下來你就會看到如下的顯示畫面!

top - 17:38:58 up 7 days, 16:52,  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,   126648k buffers
Swap:  1020088k total,       28k used,  1020060k free,   311276k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
13639 root      26  10  5148 1508 1220 S  0.0  0.2   0:00.18 bash

看到不同處了吧?底線的地方就是修改了之後所產生的效果!一般來說,如果鳥哥想要找出最損耗 CPU 資源的那個進程時,大多使用的就是 top 這支進程啦!然後強制以 CPU 使用資源來排序 (在 top 當中按下 P 即可), 就可以很快的知道啦!

3.1.3 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 真是好用到不行!直接輸入 pstree 可以查到進程相關性,如上表所示,還會使用線段將相關性進程連結起來哩! 一般連結符號可以使用 ASCII 碼即可,但有時因爲語系問題會主動的以 Unicode 的符號來連結, 但因爲可能終端機無法支持該編碼,或許會造成亂碼問題。因此可以加上 -A 選項來克服此類線段亂碼問題。

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

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

3.2 進程的管理

進程之間是可以互相控制的!舉例來說,你可以關閉、重新啓動服務器軟件,服務器軟件本身是個進程, 你既然可以讓她關閉或啓動,當然就是可以控制該進程啦!那麼進程是如何互相管理的呢?其實是透過給予該進程一個訊號 (signal) 去告知該進程你想要讓她作什麼!因此這個訊號就很重要啦!

我們也在本章之前的 bash 工作管理當中提到過, 要給予某個已經存在背景中的工作某些動作時,是直接給予一個訊號給該工作號碼即可。那麼到底有多少 signal 呢? 你可以使用 kill -l (小寫的 L ) 或者是 man 7 signal 都可以查詢到!主要的訊號代號與名稱對應及內容是:
在這裏插入圖片描述
上面僅是常見的 signal 而已,更多的訊號資訊請自行 man 7 signal 吧!一般來說,你只要記得『1, 9, 15』這三個號碼的意義即可。那麼我們如何傳送一個訊號給某個進程呢?就透過 kill 或 killall 吧!底下分別來看看:

3.3 關於進程的運行順序

我們知道 Linux 是多人多工的環境,由 top 的輸出結果我們也發現, 系統同時間有非常多的進程在運行中,只是絕大部分的進程都在休眠 (sleeping) 狀態而已。 想一想,如果所有的進程同時被喚醒,那麼 CPU 應該要先處理那個進程呢?也就是說,那個進程被運行的優先序比較高? 這就得要考慮到進程的優先運行序 (Priority) 與 CPU 排程羅!

Tips:
CPU 排程與前一章的例行性工作排程並不一樣。 CPU 排程指的是每支進程被 CPU 運行的演算守則, 而例行性工作排程則是將某支進程安排在某個時間再交由系統運行。 CPU 排程與操作系統較具有相關性!

3.3.1 Priority 與 Nice 值

我們知道 CPU 一秒鐘可以運行多達數 G 的微命令次數,透過核心的 CPU 排程可以讓各進程被 CPU 所切換運行, 因此每個進程在一秒鐘內或多或少都會被 CPU 運行部分的命令碼。如果進程都是集中在一個佇列中等待 CPU 的運行, 而不具有優先順序之分,也就是像我們去遊樂場玩熱門遊戲需要排隊一樣,每個人都是照順序來! 你玩過一遍後還想再玩 (沒有運行完畢),請到後面繼續排隊等待。情況有點像底下這樣:
在這裏插入圖片描述
上圖中假設 pro1, pro2 是緊急的進程, pro3, pro4 是一般的進程,在這樣的環境中,由於不具有優先順序, 唉啊!pro1, pro2 還是得要繼續等待而沒有優待呢!如果 pro3, pro4 的工作又臭又長!那麼緊急的 pro1, pro2 就得要等待個老半天才能夠完成!真麻煩啊!所以羅,我們想要將進程分優先順序啦!如果優先序較高則運行次數可以較多次, 而不需要與較慢優先的進程搶位置!我們可以將進程的優先順序與 CPU 排程進行如下圖的解釋:
在這裏插入圖片描述
如上圖所示,具高優先權的 pro1, pro2 可以被取用兩次,而較不重要的 pro3, pro4 則運行次數較少。 如此一來 pro1, pro2 就可以較快被完成啦!要注意,上圖僅是示意圖,並非較優先者一定會被運行兩次啦! 爲了要達到上述的功能,我們 Linux 給予進程一個所謂的『優先運行序 (priority, PRI)』, 這個 PRI 值越低代表越優先的意思。不過這個 PRI 值是由核心動態調整的, 使用者無法直接調整 PRI 值的。 先來瞧瞧 PRI 曾在哪裏出現?

[root@www ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 18625 18623  2  75   0 -  1514 wait   pts/1    00:00:00 bash
4 R     0 18653 18625  0  77   0 -  1102 -      pts/1    00:00:00 ps

由於 PRI 是核心動態調整的,我們使用者也無權去幹涉 PRI !那如果你想要調整進程的優先運行序時,就得要透過 Nice 值了!Nice 值就是上表的 NI 啦!一般來說, PRI 與 NI 的相關性如下:

PRI(new) = PRI(old) + nice
不過你要特別留意到,如果原本的 PRI 是 50 ,並不是我們給予一個 nice = 5 ,就會讓 PRI 變成 55 喔! 因爲 PRI 是系統『動態』決定的,所以,雖然 nice 值是可以影響 PRI ,不過, 最終的 PRI 仍是要經過系統分析後纔會決定的。另外, nice 值是有正負的喔,而既然 PRI 越小越早被運行, 所以,當 nice 值爲負值時,那麼該進程就會降低 PRI 值,亦即會變的較優先被處理。 此外,你必須要留意到:

  • nice 值可調整的範圍爲 -20 ~ 19 ;
  • root 可隨意調整自己或他人進程的 Nice 值,且範圍爲 -20 ~ 19 ;
  • 一般使用者僅可調整自己進程的 Nice 值,且範圍僅爲 0 ~ 19 (避免一般用戶搶佔系統資源);
  • 一般使用者僅可將 nice 值越調越高,例如本來 nice 爲 5 ,則未來僅能調整到大於 5;

這也就是說,要調整某個進程的優先運行序,就是『調整該進程的 nice 值』啦!那麼如何給予某個進程 nice 值呢?有兩種方式,分別是:

  • 一開始運行進程就立即給予一個特定的 nice 值:用 nice 命令;
  • 調整某個已經存在的 PID 的 nice 值:用 renice 命令。

3.3.2 nice :新運行的命令即給予新的 nice 值

[root@www ~]# nice [-n 數字] command
選項與參數:
-n  :後面接一個數值,數值的範圍 -20 ~ 19。

範例一:用 root 給一個 nice 值爲 -5 ,用於運行 vi ,並觀察該進程!
[root@www ~]# nice -n -5 vi &
[1] 18676
[root@www ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 18625 18623  0  75   0 -  1514 wait   pts/1    00:00:00 bash
4 T     0 18676 18625  0  72  -5 -  1242 finish pts/1    00:00:00 vi
4 R     0 18678 18625  0  77   0 -  1101 -      pts/1    00:00:00 ps
# 原本的 bash PRI 爲 75  ,所以 vi 默認應爲 75。不過由於給予 nice  爲 -5 ,
# 因此 vi 的 PRI 降低了!但並非降低到 70 ,因爲核心還會動態調整!

[root@www ~]# kill -9 %1 <==測試完畢將 vi 關閉

就如同前面說的, nice 是用來調整進程的運行優先順序!這裏只是一個運行的範例罷了! 通常什麼時候要將 nice 值調大呢?舉例來說,系統的背景工作中, 某些比較不重要的進程之進行:例如備份工作!由於備份工作相當的耗系統資源, 這個時候就可以將備份的命令之 nice 值調大一些,可以使系統的資源分配的更爲公平!

3.3.3 renice :已存在進程的 nice 重新調整

[root@www ~]# renice [number] PID
選項與參數:
PID :某個進程的 ID 啊!

範例一:找出自己的 bash PID ,並將該 PID 的 nice 調整到 10
[root@www ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 18625 18623  0  75   0 -  1514 wait   pts/1    00:00:00 bash
4 R     0 18712 18625  0  77   0 -  1102 -      pts/1    00:00:00 ps

[root@www ~]# renice 10 18625
18625: old priority 0, new priority 10

[root@www ~]# ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 18625 18623  0  85  10 -  1514 wait   pts/1    00:00:00 bash
4 R     0 18715 18625  0  87  10 -  1102 -      pts/1    00:00:00 ps

如果要調整的是已經存在的某個進程的話,那麼就得要使用 renice 了。使用的方法很簡單, renice 後面接上數值及 PID 即可。因爲後面接的是 PID ,所以你務必要以 ps 或者其他進程觀察的命令去找出 PID 才行啊!

由上面這個範例當中我們也看的出來,雖然修改的是 bash 那個進程,但是該進程所觸發的 ps 命令當中的 nice 也會繼承而爲 10 喔!瞭解了吧!整個 nice 值是可以在父進程 --> 子進程之間傳遞的呢! 另外,除了 renice 之外,其實那個 top 同樣的也是可以調整 nice 值的!

3.4 系統資源的查看

除了系統的進程之外,我們還必須就係統的一些資源進行檢查啊!舉例來說,我們使用 top 可以看到很多系統的資源對吧!那麼,還有沒有其他的工具可以查閱的? 當然有啊!底下這些工具命令可以玩一玩!

3.4.1 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 則是剩餘可用的量。 後面的 shared/buffers/cached 則是在已被使用的量當中,用來作爲緩衝及緩存的量。

仔細的看到範例一的輸出喔,我們的 Linux 測試用主機是很平凡的,根本沒有什麼工作, 但是,我的實體內存是幾乎被用光光的情況呢!不過,至少有 132MB 用在緩衝記憶 (buffers) 工作, 287MB 則用在緩存 (cached) 工作,也就是說,系統是『很有效率的將所有的內存用光光』, 目的是爲了讓系統的存取效能加速啦!

很多朋友都會問到這個問題『我的系統明明很輕鬆,爲何內存會被用光光?』現在了了吧? 被用光是正常的!而需要注意的反而是 swap 的量。一般來說, swap 最好不要被使用,尤其 swap 最好不要被使用超過 20% 以上, 如果您發現 swap 的用量超過 20% ,那麼,最好還是買實體內存來插吧! 因爲, Swap 的效能跟實體內存實在差很多,而系統會使用到 swap , 絕對是因爲實體內存不足了纔會這樣做的!如此,瞭解吧!

Tips:
Linux 系統爲了要加速系統效能,所以會將最常使用到的或者是最近使用到的文件數據緩存 (cache) 下來, 這樣未來系統要使用該文件時,就直接由內存中搜尋取出,而不需要重新讀取硬盤,速度上面當然就加快了! 因此,實體內存被用光是正常的喔!

3.4.2 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 類型等等的資訊。以上面範例一的狀態來說,我的 Linux 主機使用的核心名稱爲 Linux,而主機名稱爲 www.vbird.tsai,核心的版本爲 2.6.18-92.el5 ,該核心版本創建的日期爲 2008/6/10,適用的硬件平臺爲 i386 以上等級的硬件平臺喔。

3.4.3 觀察系統啓動時間與工作負載

這個命令很單純呢!就是顯示出目前系統已經啓動多久的時間,以及 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
# top 這個命令已經談過相關資訊,不再聊!

3.4.4 netstat :追蹤網絡或插槽檔

3.4.5 dmesg :分析核心產生的信息

3.4.6 vmstat :偵測系統資源變化

4 特殊文件與進程

4.1 具有 SUID/SGID 權限的命令運行狀態

4.2 /proc/* 代表的意義

4.3 查詢已開啓文件或已運行程序開啓之文件

主要內容摘自鳥哥的Linux私房菜第十七章、程序管理與 SELinux 初探

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章