1. 程式人生 > >7.5-UC-第五課:進程管理

7.5-UC-第五課:進程管理

文件名 進程組 system 不可 trac 進程終止 thread 返回值 在操作

================第五課 進程管理================
一、基本概念------------
1. 進程與程序~~~~~~~~~~~~~
1) 進程就是運行中的程序。一個運行著的程序, 可能有多個進程。進程在操作系統中執行特定的任務。
2) 程序是存儲在磁盤上, 包含可執行機器指令和數據的靜態實體。 進程或者任務是處於活動狀態的計算機程序。
2. 進程的分類~~~~~~~~~~~~~
1) 進程一般分為交互進程、批處理進程和守護進程三類。
2) 守護進程總是活躍的,一般是後臺運行。 守護進程一般是由系統在開機時通過腳本自動激活啟動, 或者由超級用戶root來啟動。
3. 查看進程~~~~~~~~~~~
1) 簡單形式
# ps
以簡略方式顯示當前用戶有控制終端的進程信息。
2) BSD風格常用選項
# ps axu
a - 所有用戶有控制終端的進程x - 包括無控制終端的進程u - 以詳盡方式顯示w - 以更大列寬顯示
3) SVR4風格常用選項
# ps -efl
-e或-A - 所有用戶的進程-a - 當前終端的進程-u 用戶名或用戶ID - 特定用戶的進程-g 組名或組ID - 特定組的進程-f - 按完整格式顯示-F - 按更完整格式顯示-l - 按長格式顯示
4) 進程信息列表
USER/UID: 進程屬主。
PID: 進程ID。
%CPU/C: CPU使用率。
%MEM: 內存使用率。
VSZ: 占用虛擬內存大小(KB)。
RSS: 占用物理內存大小(KB)。
TTY: 終端次設備號,“?”表示無控制終端,如後臺進程。
STAT/S: 進程狀態。可取如下值:
O - 就緒。等待被調度。R - 運行。Linux下沒有O狀態,就緒狀態也用R表示。S - 可喚醒睡眠。系統中斷,獲得資源,收到信號, 都可被喚醒,轉入運行狀態。D - 不可喚醒睡眠。只能被wake_up系統調用喚醒。T - 暫停。收到SIGSTOP信號轉入暫停狀態, 收到SIGCONT信號轉入運行狀態。W - 等待內存分頁(2.6內核以後被廢棄)。X - 死亡。不可見。Z - 僵屍。已停止運行,但其父進程尚未獲取其狀態。< - 高優先級。N - 低優先級。L - 有被鎖到內存中的分頁。實時進程和定制IO。s - 會話首進程。l - 多線程化的進程。+ - 在前臺進程組中。
START/STIME: 進程開始時間。
TIME: 進程運行時間。
COMMAND/CMD: 進程指令。
F: 進程標誌。可由下列值取和:
1 - 通過fork產生但是沒有exec。4 - 擁有超級用戶特權。
PPID: 父進程ID。
NI: 進程nice值,-20到19,可通過系統調用或命令修改。
PRI: 進程優先級。
靜態優先級 = 80 + nice,60到99,值越小優先級越高。內核在靜態優先級的基礎上,根據進程的交互性計算得到實際(動態)優先級,以體現對IO消耗型進程的獎勵,和對處理器消耗型進程的懲罰。
ADDR: 內核進程的內存地址。普通進程顯示“-”。
SZ: 占用虛擬內存頁數。
WCHAN: 進程正在等待的內核函數或事件。
PSR: 進程被綁定到哪個處理器。
4. 父進程、子進程、孤兒進程和僵屍進程-------------------------------------
內核進程(0) init(1) xinetd in.telnetd <- 用戶登錄 login bash vi
1) 父進程啟動子進程後, 子進程在操作系統的調度下與其父進程同時運行。
2) 子進程先於父進程結束, 子進程向父進程發送SIGCHLD(17)信號, 父進程回收子進程的相關資源。
3) 父進程先於子進程結束,子進程成為孤兒進程, 同時被init進程收養,即成為init進程的子進程。
4) 子進程先於父進程結束, 但父進程沒有回收子進程的相關資源, 該子進程即成為僵屍進程。
5. 進程標識符(進程ID)~~~~~~~~~~~~~~~~~~~~~
1) 每個進程都有一個以非負整數表示的唯一標識, 即進程ID/PID。
2) 進程ID在任何時刻都是唯一的,但可以重用, 當一個進程退出時,其進程ID就可以被其它進程使用。
3) 延遲重用。
a.out - 1000a.out - 1010a.out - 1020...
範例:delay.c
二、getxxxid------------
#include <unistd.h>
getpid
- 獲取進程IDgetppid - 獲取父進程IDgetuid - 獲取實際用戶IDgeteuid - 獲取有效用戶IDgetgid - 獲取實際組IDgetegid - 獲取有效組ID
範例:id.c
以其它用戶身份登錄並執行$ a.out輸出 進程ID:... 父進程ID:...實際用戶ID:1000 - 實際用戶ID取父進程(shell)的實際用戶ID有效用戶ID:1000 - 有效用戶ID取實際用戶ID 實際組ID:1000 - 實際組ID取父進程(shell)的實際組ID 有效組ID:1000 - 有效組ID取實際組ID
執行# ls -l a.out輸出-rwxr-xr-x ... ^ ^
為a.out的文件權限添加設置用戶ID位和設置組ID位# chmod u+s a.out# chmod g+s a.out
執行# ls -l a.out輸出-rwsr-sr-x ... ^ ^
以其它用戶身份登錄並執行$ a.out輸出 進程ID:... 父進程ID:...實際用戶ID:1000 - 實際用戶ID取父進程(shell)的實際用戶ID有效用戶ID:0 - 有效用戶ID取程序文件的屬主ID 實際組ID:1000 - 實際組ID取父進程(shell)的實際組ID 有效組ID:0 - 有效組ID取程序文件的屬組ID
進程的訪問權限由其有效用戶ID和有效組ID決定。通過此方法可以使進程獲得比登錄用戶更高的權限。比如通過passwd命令修改登錄口令。
執行ls -l /etc/passwd輸出-rw-r--r--. 1 root root 1648 Nov 9 14:05 /etc/passwd ^該文件中存放所有用戶的口令信息,僅root用戶可寫,但事實上任何用戶都可以修改自己的登錄口令,即任何用戶都可以通過/usr/bin/passwd程序寫該文件。
執行# ls -l /usr/bin/passwd輸出-rwsr-xr-x. 1 root root 28816 Feb 8 2011 /usr/bin/passwd ^ ^該程序具有設置用戶ID位,且其屬主為root。因此以任何用戶登錄系統,執行passwd命令所啟動的進程,其有效用戶ID均為root,對/etc/passwd文件有寫權限。
三、fork--------
#include <unistd.h>
pid_t fork (void);
1. 創建一個子進程,失敗返回-1。
2. 調用一次,返回兩次。 分別在父子進程中返回子進程的PID和0。 利用返回值的不同, 可以分別為父子進程編寫不同的處理分支。
範例:fork.c
3. 子進程是父進程的副本, 子進程獲得父進程數據段和堆棧段(包括I/O流緩沖區)的拷貝, 但子進程共享父進程的代碼段。
範例:mem.c、os.c、is.c
4. 函數調用後父子進程各自繼續運行, 其先後順序不確定。 某些實現可以保證子進程先被調度。
5. 函數調用後, 父進程的文件描述符表(進程級)也會被復制到子進程中, 二者共享同一個文件表(內核級)。
範例:ftab.c
6. 總進程數或實際用戶ID所擁有的進程數, 超過系統限制,該函數將失敗。
7. 一個進程如果希望創建自己的副本並執行同一份代碼, 或希望與另一個程序並發地運行,都可以使用該函數。
8. 孤兒進程與僵屍進程。
範例:orphan.c、zombie.c
註意:fork之前的代碼只有父進程執行, fork之後的代碼父子進程都有機會執行, 受代碼邏輯的控制而進入不同分支。
四、vfork---------
#include <unistd.h>
pid_t vfork (void);
該函數的功能與fork基本相同,二者的區別:
1. 調用vfork創建子進程時並不復制父進程的地址空間, 子進程可以通過exec函數族, 直接啟動另一個進程替換自身, 進而提高進程創建的效率。
2. vfork調用之後,子進程先被調度。
五、進程的正常退出------------------
1. 從main函數中return。
int main (...) { ... return x;}
等價於:
int main (...) { ... exit (x);}
2. 調用標準C語言的exit函數。
#include <stdlib.h>
void exit (int status);
1) 調用進程退出, 其父進程調用wait/waitpid函數返回status的低8位。
2) 進程退出之前, 先調用所有事先通過atexit/on_exit函數註冊的函數, 沖刷並關閉所有仍處於打開狀態的標準I/O流, 刪除所有通過tmpfile函數創建的文件。
#include <stdlib.h>
int atexit (void (*function) (void));
function - 函數指針, 指向進程退出前需要被調用的函數。 該函數既沒有返回值也沒有參數。
成功返回0,失敗返回非零。
int on_exit (void (*function) (int, void*), void* arg);
function - 函數指針, 指向進程退出前需要被調用的函數。 該函數沒有返回值但有兩個參數: 第一參數來自exit函數的status參數, 第二個參數來自on_exit函數的arg參數。
arg - 任意指針, 將作為第二個參數被傳遞給function所指向的函數。
成功返回0,失敗返回非零。
3) 用EXIT_SUCCESS/EXIT_FAILURE常量宏 (可能是0/1)作參數,調用exit()函數表示成功/失敗, 提高平臺兼容性。
4) 該函數不會返回。
5) 該函數的實現調用了_exit/_Exit函數。
3. 調用_exit/_Exit函數。
#include <unistd.h>
void _exit (int status);
1) 調用進程退出, 其父進程調用wait/waitpid函數返回status的低8位。
2) 進程退出之前, 先關閉所有仍處於打開狀態的文件描述符, 將其所有子進程托付給init進程(PID為1的進程)收養, 向父進程遞送SIGCHILD信號。
3) 該函數不會返回。
4) 該函數有一個完全等價的標準C版本:
#include <stdlib.h>
void _Exit (int status);
4. 進程的最後一個線程執行了返回語句。
5. 進程的最後一個線程調用pthread_exit函數。
範例:exit.c
六、進程的異常終止------------------
1. 調用abort函數,產生SIGABRT信號。
2. 進程接收到某些信號。
3. 最後一個線程對“取消”請求做出響應。
七、wait/waitpid----------------
等待子進程終止並獲取其終止狀態。
#include <sys/types.h>#include <sys/wait.h>
pid_t wait (int* status);
pid_t waitpid (pid_t pid, int* status, int options);
成功返回終止子進程的PID,失敗返回-1。
1. 當一個進程正常或異常終止時, 內核向其父進程發送SIGCHLD信號。 父進程可以忽略該信號, 或者提供一個針對該信號的信號處理函數,默認為忽略。
2. 父進程調用wait函數:
1) 若所有子進程都在運行,則阻塞。
2) 若有一個子進程已終止, 則返回該子進程的PID和終止狀態(通過status參數)。
3) 若沒有需要等待子進程,則返回失敗,errno為ECHILD。
3. 在任何一個子進程終止前,wait函數只能阻塞調用進程, 而waitpid函數可以有更多選擇。
4. 如果有一個子進程在wait函數被調用之前, 已經終止並處於僵屍狀態,wait函數會立即返回, 並取得該子進程的終止狀態。
5. 子進程的終止狀態通過輸出參數status返回給調用者, 若不關心終止狀態,可將此參數置空。
6. 子進程的終止狀態可借助 sys/wait.h中定義的參數宏查看:
WIFEXITED(): 子進程是否正常終止,是則通過WEXITSTATUS()宏,獲取子進程調用exit/_exit/_Exit函數,所傳遞參數的低8位。因此傳給exit/_exit/_Exit函數的參數最好不要超過255。
WIFSIGNALED(): 子進程是否異常終止,是則通過WTERMSIG()宏獲取終止子進程的信號。
WIFSTOPPED(): 子進程是否處於暫停,是則通過WSTOPSIG()宏獲取暫停子進程的信號。
WIFCONTINUED(): 子進程是否在暫停之後繼續運行
範例:wait.c、loop.c
7. 如果同時存在多個子進程,又需要等待特定的子進程, 可使用waitpid函數,其pid參數:
-1 - 等待任一子進程,此時與wait函數等價。
> 0 - 等待由該參數所標識的特定子進程。
0 - 等待其組ID等於調用進程組ID的任一子進程, 即等待與調用進程同進程組的任一子進程。
<-1 - 等待其組ID等於該參數絕對值的任一子進程, 即等待隸屬於特定進程組內的任一子進程。
範例:waitpid.c
8. waitpid函數的options參數可取0(忽略)或以下值的位或:
WNOHANG - 非阻塞模式, 若沒有可用的子進程狀態,則返回0。
WUNTRACED - 若支持作業控制,且子進程處於暫停態, 則返回其狀態。
WCONTINUED - 若支持作業控制,且子進程暫停後繼續, 則返回其狀態。
範例:nohang.c
八、exec--------
1. exec函數會用新進程完全替代調用進程, 並開始從main函數執行。
2. exec函數並非創建子進程,新進程取調用進程的PID。
3. exec函數所創建的新進程, 完全取代調用進程的代碼段、數據段和堆棧段。
4. exec函數若執行成功,則不會返回,否則返回-1。
5. exec函數包括六種形式:
#include <unistd.h>
int execl ( const char* path, const char* arg, ...);
int execv ( const char* path, char* const argv[]);
int execle ( const char* path, const char* arg, ..., char* const envp[]);
int execve ( const char* path, char* const argv[], char* const envp[]);
int execlp ( const char* file, const char* arg, ...);
int execvp (const char* file, char* const argv[]);
l: 新程序的命令參數以單獨字符串指針的形式傳入 (const char* arg, ...),參數表以空指針結束。
v: 新程序的命令參數以字符串指針數組的形式傳入 (char* const argv[]),數組以空指針結束。
e: 新程序的環境變量以字符串指針數組的形式傳入 (char* const envp[]),數組以空指針結束, 無e則從調用進程的environ變量中復制。
p: 若第一個參數中不包含“/”,則將其視為文件名, 根據PATH環境變量搜索該文件。
範例:argenv.c、exec.c
九、system----------
#include <stdlib.h>
int system (const char* command);
1. 標準C函數。執行command, 成功返回command對應進程的終止狀態,失敗返回-1。
2. 若command取NULL,返回非零表示shell可用, 返回0表示shell不可用。
3. 該函數的實現, 調用了fork、exec和waitpid等函數, 其返回值:
1) 如果調用fork或waitpid函數出錯,則返回-1。
2) 如果調用exec函數出錯,則在子進程中執行exit(127)。
3) 如果都成功,則返回command對應進程的終止狀態 (由waitpid的status輸出參數獲得)。
4. 使用system函數而不用fork+exec的好處是, system函數針對各種錯誤和信號都做了必要的處理。
範例:system.c、fexe.c

來自為知筆記(Wiz)

7.5-UC-第五課:進程管理