文章目錄
- Linux-進程相關函數
- 父子進程關系
- 父子進程地址空間
- getpid函數 獲取本進程號
- getppid函數 獲取當前進程的進程的父進程號
- getpgid函數 獲取進程組號
- 示例
- fork函數 創建進程
- 區分父子進程
- exit函數 進程退出
- wait函數 等待子進程退出
- waitpid函數
Linux-進程相關函數
每個進程都由一個進程號來標識,其類型為 pid_t(整型),進程號的范圍:0~32767。進程號總是唯一的,但進程號可以重用。當一個進程終止后,其進程號就可以再次使用。
進程號(PID):
標識進程的一個非負整型數。
父進程號(PPID):
任何進程( 除 init 進程)都是由另一個進程創建,該進程稱為被創建進程的父進程,對應的進程號稱為父進程號(PPID)。如,A 進程創建了 B 進程,A 的進程號就是 B 進程的父進程號。
進程組號(PGID):
進程組是一個或多個進程的集合。他們之間相互關聯,進程組可以接收同一終端的各種信號,關聯的進程有一個進程組號(PGID) 。這個過程有點類似于 QQ 群,組相當于 QQ 群,各個進程相當于各個好友,把各個好友都拉入這個 QQ 群里,主要是方便管理,特別是通知某些事時,只要在群里吼一聲,所有人都收到,簡單粗暴。但是,這個進程組號和 QQ 群號是有點區別的,默認的情況下,當前的進程號會當做當前的進程組號。
父子進程關系
使用 fork() 函數得到的子進程是父進程的一個復制品,它從父進程處繼承了整個進程的地址空間:包括進程上下文(進程執行活動全過程的靜態描述)、進程堆棧、打開的文件描述符、信號控制設定、進程優先級、進程組號等。
子進程所獨有的只有它的進程號,計時器等(只有小量信息)。因此,使用 fork() 函數的代價是很大的。
簡單來說, 一個進程調用 fork() 函數后,系統先給新的進程分配資源,例如存儲數據和代碼的空間。然后把原來的進程的所有值都復制到新的新進程中,只有少數值與原來的進程的值不同。相當于克隆了一個自己。
實際上,更準確來說,Linux 的 fork() 使用是通過寫時拷貝 (copy- on-write) 實現。寫時拷貝是一種可以推遲甚至避免拷貝數據的技術。內核此時并不復制整個進程的地址空間,而是讓父子進程共享同一個地址空間。只用在需要寫入的時候才會復制地址空間,從而使各個進行擁有各自的地址空間。也就是說,資源的復制是在需要寫入的時候才會進行,在此之前,只有以只讀方式共享。
注意:fork之后父子進程共享文件,fork產生的子進程與父進程相同的文件文件描述符指向相同的文件表,引用計數增加,共享文件文件偏移指針。
- Linux 的 fork() 使用是通過寫時拷貝 (copy- on-write),讀時共享實現
父子進程地址空間
- 父子進程各自的地址空間是獨立的
getpid函數 獲取本進程號
#include <sys/types.h>
#include <unistd.h>
?
pid_t getpid(void);
功能:獲取本進程號(PID)
參數:無
返回值:本進程號
getppid函數 獲取當前進程的進程的父進程號
#include <sys/types.h>
#include <unistd.h>
?
pid_t getppid(void);
功能:獲取調用此函數的進程的父進程號(PPID)
參數:無
返回值:調用此函數的進程的父進程號(PPID)
getpgid函數 獲取進程組號
#include <sys/types.h>
#include <unistd.h>
?
pid_t getpgid(pid_t pid);
功能:獲取進程組號(PGID)
參數:pid:進程號
返回值:參數為 0 時返回當前進程組號,否則返回參數指定的進程的進程組號
示例
int main()
{pid_t pid, ppid, pgid;
?pid = getpid();printf("pid = %d\n", pid);
?ppid = getppid();printf("ppid = %d\n", ppid);
?pgid = getpgid(pid);printf("pgid = %d\n", pgid);
?return 0;
}
fork函數 創建進程
#include <sys/types.h>
#include <unistd.h>
?
pid_t fork(void);
功能:用于從一個已存在的進程中創建一個新進程,新進程稱為子進程,原進程稱為父進程。
參數:無
返回值:成功:子進程中返回 0,父進程中返回子進程 ID。pid_t,為整型。失敗:返回-1。失敗的兩個主要原因是:1)當前的進程數已經達到了系統規定的上限,這時 errno 的值被設置為 EAGAIN。2)系統內存不足,這時 errno 的值被設置為 ENOMEM。
區分父子進程
int main()
{pid_t pid;pid = fork();if (pid < 0){ // 沒有創建成功 perror("fork");return 0;}
?if (0 == pid){ // 子進程 while (1){printf("I am son\n");sleep(1);}}else if (pid > 0){ // 父進程 while (1){printf("I am father\n");sleep(1);}}
?return 0;
}
exit函數 進程退出
#include <stdlib.h>
void exit(int status);
?
#include <unistd.h>
void _exit(int status);
功能:結束調用此函數的進程。
參數:status:返回給父進程的參數(低 8 位有效),至于這個參數是多少根據需要來填寫。
返回值:無
exit() 和 _exit() 函數功能和用法是一樣的,無非時所包含的頭文件不一樣,還有的區別就是:exit()屬于標準庫函數,exit()屬于系統調用函數。
- exit() 函數會處理后續的刷新緩沖區的動作
- _exit() 是直接退出進程,不做任何的后續處理操作
wait函數 等待子進程退出
在每個進程退出的時候,內核釋放該進程所有的資源、包括打開的文件、占用的內存等。但是仍然為其保留一定的信息,這些信息主要主要指進程控制塊PCB的信息(包括進程號、退出狀態、運行時間等)。
父進程可以通過調用wait或waitpid得到它的退出狀態同時徹底清除掉這個進程。
wait() 和 waitpid() 函數的功能一樣,區別在于,wait() 函數會阻塞,waitpid() 可以設置不阻塞,waitpid() 還可以指定等待哪個子進程結束。
注意:一次wait或waitpid調用只能清理一個子進程,清理多個子進程應使用循環。
#include <sys/types.h>
#include <sys/wait.h>
?
pid_t wait(int *status);
功能:等待任意一個子進程結束,如果任意一個子進程結束了,此函數會回收該子進程的資源。
參數:status : 進程退出時的狀態信息。
返回值:成功:已經結束子進程的進程號失敗: -1
調用 wait() 函數的進程會掛起(阻塞),直到它的一個子進程退出或收到一個不能被忽視的信號時才被喚醒(相當于繼續往下執行)。
若調用進程沒有子進程,該函數立即返回;若它的子進程已經結束,該函數同樣會立即返回,并且會回收那個早已結束進程的資源。
所以,wait()函數的主要功能為回收已經結束子進程的資源。
如果參數 status 的值不是 NULL,wait() 就會把子進程退出時的狀態取出并存入其中,這是一個整數值(int),指出了子進程是正常退出還是被非正常結束的。
這個退出信息在一個 int 中包含了多個字段,直接使用這個值是沒有意義的,我們需要用宏定義取出其中的每個字段。
宏函數可分為如下三組:
- WIFEXITED(status)
為非0 → 進程正常結束
WEXITSTATUS(status)
如上宏為真,使用此宏 → 獲取進程退出狀態 (exit的參數)
- WIFSIGNALED(status)
為非0 → 進程異常終止
WTERMSIG(status)
如上宏為真,使用此宏 → 取得使進程終止的那個信號的編號。
- WIFSTOPPED(status)
為非0 → 進程處于暫停狀態
WSTOPSIG(status)
如上宏為真,使用此宏 → 取得使進程暫停的那個信號的編號。
WIFCONTINUED(status)
為真 → 進程暫停后已經繼續運行
waitpid函數
#include <sys/types.h>
#include <sys/wait.h>
?
pid_t waitpid(pid_t pid, int *status, int options);
功能:等待子進程終止,如果子進程終止了,此函數會回收子進程的資源。
?
參數:pid : 參數 pid 的值有以下幾種類型:pid > 0 等待進程 ID 等于 pid 的子進程。pid = 0 等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程組,waitpid 不會等待它。pid = -1 等待任一子進程,此時 waitpid 和 wait 作用一樣。pid < -1 等待指定進程組中的任何子進程,這個進程組的 ID 等于 pid 的絕對值。
?status : 進程退出時的狀態信息。和 wait() 用法一樣。
?options : options 提供了一些額外的選項來控制 waitpid()。0:同 wait(),阻塞父進程,等待子進程退出。WNOHANG:沒有任何已經結束的子進程,則立即返回。WUNTRACED:如果子進程暫停了則此函數馬上返回,并且不予以理會子進程的結束狀態。(由于涉及到一些跟蹤調試方面的知識,加之極少用到)返回值:waitpid() 的返回值比 wait() 稍微復雜一些,一共有 3 種情況:1) 當正常返回的時候,waitpid() 返回收集到的已經回收子進程的進程號;2) 如果設置了選項 WNOHANG,而調用中 waitpid() 發現沒有已退出的子進程可等待,則返回 0;3) 如果調用中出錯,則返回-1,這時 errno 會被設置成相應的值以指示錯誤所在,如:當 pid 所對應的子進程不存在,或此進程存在,但不是調用進程的子進程,waitpid() 就會出錯返回,這時 errno 被設置為 ECHILD;