c/c++:進程(PCB進程控制塊、進程狀態、exec族函數、進程控制)

目錄

一、進程概述

1、PCB(Processing Control Block)

2、進程狀態

二、進程創建

2.1 進程ID

2.2 進程的創建

2.3 父子進程

三、exec族函數

四、進程控制

4.1 結束進程

4.2 孤兒進程

4.2 殭屍進程

4.3 進程回收

wait

waitpid


 

一、進程概述

1、PCB(Processing Control Block)

Linux內核的進程控制塊是task_struct結構體。理解該結構體中包含的如下信息:


進程id。系統中每個進程有唯一的id,在C語言中用pid_t類型表示,其實就是一個非負整數。
進程的狀態,有就緒、運行、掛起、停止等狀態。
進程切換時需要保存和恢復的一些CPU寄存器。
描述虛擬地址空間的信息。
描述控制終端的信息。
當前工作目錄(Current Working Directory)。
umask掩碼。
文件描述符表,包含很多指向file結構體的指針。
和信號相關的信息。
用戶id和組id。
會話(Session)和進程組。
進程可以使用的資源上限(Resource Limit)。

 

2、進程狀態

進程有五種狀態, 分別是: 創建態, 就緒態, 運行態, 阻塞態, 退出態。

創建態  == 初始態
阻塞態  == 掛起態

創建態: 初始化系統資源
就緒態: 不能運行, 因爲沒有cpu資源, 和別的進程搶cpu
運行態: 進程搶到了cpu, 將cpu時間使用完之後, 會再次失去cpu -> 就緒態
阻塞態: 運行的進程(有cpu資源), 受到了某些條件的干擾, 失去了cpu -> sleep(10), 睡醒了之後 -> 就緒態

 

二、進程創建

 

2.1 進程ID

  • 查看進程
 ps aux / ajx
  	a: 顯示當前終端下的所有的程序, 包括所有的用戶
  	u: 顯示用戶信息
  	x: 打印和tty終端先關的信息
  	j: 顯示更多的用戶信息
  • 殺死進程
 $ kill -l
   1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
   6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
  11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
  16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
  21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
  26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
  31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
  38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
  43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
  48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
  53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
  58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
  63) SIGRTMAX-1  64) SIGRTMAX
  
  # 殺死進程
  kill -SIGKILL 進程ID
  kill -9 進程ID
  • 獲取進程ID
  #include <sys/types.h>
  #include <unistd.h>
  
  pid_t getpid(void);	// 當前進程
  pid_t getppid(void); // 當前進程的父進程

 

2.2 進程的創建

#include <unistd.h>
  // 在當前進程中創建一個子進程
  pid_t fork(void);
  返回值: 得到兩個數
  	- >0 : 代表是父進程的返回值
  	- == 0: 子進程的返回值

如何區分父子進程?

通過fork的返回值, fork調用成功之後, 會得到兩個進程
     每個進程都會返回一個值
      - 父進程: > 0
      - 子進程: == 0

 

2.3 父子進程

  • - 區別

  - fork的返回值不同
    - 父進程: >0
    - 子進程: == 0

  •   - pcb中一些數據

    - 當前進程的PID
    - 當前進程的PPID
    - 信號集

  • - 共同點

  - 某個狀態下: 子進程剛被創建出來, 還沒有執行任何操作
    - 用戶區數據
    - 文件描述符表

  • - 父子進程對變量是不是共享的?

  - 剛開始時候一樣, 如果數據被修改之後, 不共享
  - 讀時共享(子進程被創建, 兩個進程沒有做任何寫操作), 寫時複製

 

三、exec族函數

exec函數族的作用是根據指定的文件名找到可執行文件,並用它來取代調用進程的內容,換句話說,就是在調用進程內部執行一個可執行文件。

exec函數族的函數執行成功後不會返回,因爲調用進程的實體,包括[代碼段],[數據段]和[堆棧]等都已經被新的內容取代,只留下進程ID等一些表面上的信息仍保持原樣,頗有些神似"三十六計"中的"金蟬脫殼"。看上去還是舊的軀殼,卻已經注入了新的靈魂。只有調用失敗了,它們纔會返回一個-1,從原程序的調用點接着往下執行。

#include <unistd.h>

// 不是linux系統函數
int execl(const char *path, const char *arg, ...);  // 使用最多的
	參數: 
		- path: 可執行程序的路徑, 建議寫絕對路徑
		- arg: 第二個參數, 隨便寫, 一般爲了看起來舒服, 寫成和參數1相同的值
		- 從第三個參數: 可執行程序執行過程中需要的真正的參數
		- 最後一個參數: NULL(哨兵,記住必須是空即可)
   舉例: /home/itcast/test/a.out
   execl("/home/itcast/test/a.out", "a.out", "hello", "123", NULL);
   execl("/bin/ps", "ps", "a", "u", "x", NULL);

int execlp(const char *file, const char *arg, ...); // 使用最多的
	參數:
		- file: 可執行程序的名字, 在執行這個程序之前, 子動搜索系統環境變量PATH
		- arg: 第二個參數, 隨便寫, 一般爲了看起來舒服, 寫成和參數1相同的值
		- 從第三個參數: 可執行程序執行過程中需要的真正的參數
		- 最後一個參數: NULL(哨兵)

int execle(const char *path, const char *arg, ..., char *const envp[]);
	- path: 文件名
	- char *const envp[], 從這個參數指定的路徑中所屬第一個參數對應的文件名
	char* envp[] = {"/home/robin", "/a/b", "home/zhangsan/test", NULL};

int execv(const char *path, char *const argv[]);
	- path: 例如: /bin/ps
	- argv: 參數列表
	
	char* args[] = {"xxx", "aux", NULL};
int execvp(const char *file, char *const argv[]);
// linux系統函數
int execve(const char *path, char *const argv[], char *const envp[]);

exec族函數的結尾單詞所代表的含義:

 l(list)   參數地址列表,以空指針結尾
p(path) 按 PATH 環境變量指定的目錄搜索可執行文件
e(environment) 存有環境變量字符串地址的指針數組的地址
v(vector)  存有各參數地址的指針數組的地址

 

四、進程控制

4.1 結束進程

每個進程結束之後, 都會自己釋放自己地址空間中的 用戶區數據, 內核區的pcb沒有辦法自己釋放掉, 需要父進程去釋放.

// exit -> c庫函數
#include <stdlib.h>
void exit(int status);      // status爲返回值(退出值),一般寫-1;
exit(-1)

// _exit() -> linux系統函數
#include <unistd.h>
void _exit(int status);

 

4.2 孤兒進程

爹生孩子, 爹英年早逝, 只剩下孩子, 這個進程就是孤兒進程. 
孤兒會被領養, 被pid=1的進程, pid=1的進程, 變成了孤兒進程父親

 

4.2 殭屍進程

爹生孩子,  爹還活着, 但是孩子死了, 這時候子進程的pcb需要回收, 這個爹不負責任, 不回收子進程的pcb, 這個進程變成了殭屍進程.

殭屍進程: 沒有用戶區數據, 只有一個pcb, 不能稱之爲一個存活的進程
殭屍進程不能被 kill -9 殺死

 

4.3 進程回收

  • wait() 函數用於使父進程,直到一個子進程結束或該進程接收到一個指定的信號爲止。
  • 如果該父進程沒有子進程或它的子進程已經結束,則wait()就不阻塞,會立即返回。
  • wait調用一次只能回收一個子進程
  • waitpid()的作用和wait()一樣,但它並不一定要等待一個終止的子進程,它還有若干選項,如可提供一個非阻塞版本的wait()功能,也能支持作用控制。
  • 實際上,wait()函數只是waitpid()函數的一個特例,在Linux內部實現wait()函數時直接調用的就是waitpid()函數。

 

wait

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
	- 參數: status記錄了子進程退出時候的狀態, 正常->退出值(exit(10))/非正常退出 -> 被信號幹掉了
	- 返回值: 
		>0: 被回收的子進程的進程ID
		==-1: 調用失敗


// status參數的使用
int s;
int ret = wait(&s);
if(WIFEXITED(s))	                // 判斷子進程是不是正常退出
{   
    printf("退出的狀態碼: %d\n", WEXITSTATUS(s));	// 打印子進程退出的狀態碼
}   
if(WIFSIGNALED(s))	                // 判斷子進程是不是被信號幹掉了
{   
    printf("被這個信號幹掉了: %d\n", WTERMSIG(s));	// 打印子進程是被那個信號殺死的
} 

上面使用的幾個宏記一下,WIFEXITED、WEXITSTATUS、WIFSIGNALED、WTERMSIG

參數status解釋:

如果參數status的值不是NULL,wait就會把子進程退出時的狀態取出並存入其中,這是一個整數值(int),指出了子進程是正常退出還是非正常結束的(一個進程也可以被其他進程用信號結束),以及正常結束時返回值,或被哪一個信號結束的等信息。由於這些信息被存放在一個整數的不同二進制位中,所以用常規的方法讀取會非常麻煩,人們就設計了一套專門的宏(macro)來完成這項工作,下面我們來學習一下其中最常見的兩個:

1、WIFEXITED(status):這個宏用來指出子進程是否爲正常退出的,如果是,它會返回一個非零值。

2、WEXITSTATUS(status):當WIFEXITED返回非零值時,我們可以用這個宏來提取子進程的返回值,如果子進程調用exit(5)退出,WEXITSTATUS(status)就會返回5;如果子進程調用exit(7)退出,WEXITSTATUS(status)就會返回7。請注意,如果進程不是正常退出,也就是說,WIFEXITED返回0,這個值就毫無意義了。

 

waitpid

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
    參數:
	  - pid:
		>0: 某個子進程的pid, 比較常用
		=0: 回收當前進程組的子進程
		-1: 回收所有的子進程 == wait(NULL), 用的最多的
                <0: 某個進程組的組ID, 回收指定進程組的子進程
         - options: 設置函數阻塞或者非阻塞
         	- 0: 阻塞
         	- WNOHANG: 非阻塞,若由pid指定的子進程沒有結束,則waitpid()不阻塞而立即返回。此時返回0
     返回值:
		- >0: 回收的子進程的ID
		- =0: options=WNOHANG, 還有子進程活着
		- -1: 錯誤, 已經沒有子進程了

 

 

 

 

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