???? 每一個進程在系統中都有一個唯一的標識,這個標識叫做進程標識符,或者叫? PID(process identity)。我們可以通過調用? getpid? 函數來獲取一個進程的PID,也可以調用getppid函數來獲取當前進程的父進程PID。在? linux? 系統中,有三個特殊的進程,它們的進程? PID? 分別為0,1,2。0號進程是系統進程,它存在與內核當中,而不是磁盤文件上。該進程常被成為交換進程,或者叫? swapper。1號進程就是? init? 進程,這個進程用來讀取一些初始化文件,并且引導系統到一個狀態,如多用戶狀態,init? 進程是一個用戶進程。2號進程是頁精靈進程,用來做一些和虛擬內存相關的工作,可是這個進程我沒有在系統中找到。如果有知道的朋友,請告訴我,這個頁精靈進程在系統中是哪一個進程,另外頁精靈進程也是系統進程。需要說明的是,這3個進程都是由特殊的方式產生的,除了這3個進程以外,linux? 系統中的其它進程都是由調用? fork? 函數產生的。
???? fork? 函數用來創建一個子進程,這是? linux? 系統中創建子進程的唯一方式。當調用? fork? 函數之后,我們稱調用? fork? 函數產生的進程為子進程,調用fork函數的進程為父進程。fork? 函數調用一次,但是返回兩次。這兩次返回分別是在父進程和子進程中,在父進程中返回子進程的? PID,在子進程中返回0。當調用? fork? 函數成功后,父子進程就分別從調用? fork? 的地方開始執行。子進程是父進程的一個拷貝,但是現在很多的實現,并不是調用? fork? 成功之后就立即拷貝,而是采用了一種稱為? COW(copy on write)的技術,只有當子進程需要修改一些值時,才進行拷貝。拷貝的是父進程的數據空間以及堆和棧。我們可以用一個小例子來解釋一下? fork? 函數的作用及特性:
#include <stdio.h>
#include <unistd.h>int glob = 6;
char buf[] = "a write to stdout\n";int main(void)
{pid_t pid;int var = 88;if( write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1 ){printf("write error\n");return -1;}printf("before fork\n");if( (pid=fork())<0 ){printf("fork error\n");return -1;}else if(pid==0){glob++;var++;}else{sleep(2);}printf("pid=%d\tglob=%d\tvar=%d\n",getpid(),glob,var);return 0;
}
./a.out
a write to stdout
before fork
pid=3131 glob=7 var=89
pid=3130 glob=6 var=88
./a.out > /tmp/a
vim /tmp/a
a write to stdout
before fork
pid=3077 glob=7 var=89
before fork
pid=3076 glob=6 var=88
???? fork? 函數創建的子進程,會把在父進程中打開的文件描述符,復制到子進程中,也就是說它們共享同一個文件表項。
unix高級編程第5版pdf????? vfork? 函數和? fork? 函數的基本功能是一樣的,它也是調用一次返回兩次。但是? vfork? 函數和? fork? 函數的不同在于,vfork? 的目的是創建一個新進程,該進程用來通過調用? exec? 函數來執行一個新進程,所以子進程并不復制父進程的數據空間和堆棧,而是先在父進程的工作空間中運行。vfork? 函數要求父進程等待子進程先執行,如果在調用了vfork函數之后,子進程的執行還需要依賴于父進程的進一步動作,這將導致死鎖。只有當子進程執行了? exec? 函數或者? exit? 函數之后,父進程才可以被調度執行。我們將上面的程序中的fork函數替換為vfork函數,得到的程序如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>int glob = 6;int main(void)
{int var = 88;pid_t pid;printf("before vfork\n");if( (pid=vfork())<0 ){printf("vfork error\n");return -1;}else if(pid==0){var++;glob++;_exit(0);}printf("pid=%d var=%d glob=%d\n",getpid(),var,glob);return 0;
}
before vfork
pid=3487 var=89 glob=7
???? 經過前面的介紹,我們已經了解了? init? 進程。它不是一個系統進程,它就是存在于? /sbin/init? 文件。它的作用是在系統啟動時用來讀取一些初始化信息,將系統引導到一個狀態。另外,它還有一個重要的角色是收養系統中的孤兒進程。那么什么進程叫做孤兒進程呢?顧名思義,孤兒進程就是其父進程在其終止之前終止的進程。
???? init? 進程收養孤兒進程的手續是怎么樣的呢?當一個進程要終止時,內核會逐個檢查系統中的各個進程,查看它們的父進程是否是正要終止的進程,如果是這樣的話,就將它們的父進程修改為? init? 進程。
???? 另外,關于init進程清除系統中的僵死進程和僵死進程的一些概念,請參考博客點擊打開鏈接。
shell高級編程。???? wait? 和? waitpid? 函數,用來在父進程中等待子進程的結束。wait? 函數用來等待父進程的任何一個子進程,只要有子進程終止,它就立即返回。調用? wait? 函數,會發生3種情況:
1.當該進程沒有任何子進程時,會發生錯誤,返回-1
2.當該進程的子進程都在運行,尚沒有子進程結束時,該進程會發生阻塞
3.當該進程已經有子進程終止時,可期待 ?wait ?函數立即返回,返回值為終止子進程 ?PID
???? exec? 函數族包括很多的函數,這些函數的作用都是執行一個新的進程。內核執行一個進程的唯一方法是調用? exec? 函數,但是? exec? 函數并不修改進程的PID。一般的使用方法是先調用fork函數創建一個子進程,然后在子進程中執行? exec? 函數。exec? 函數族如下:
#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg,..., char * const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *filename, char *const argv[],char *const envp[]);
pathname , arg1 , arg2 , (char*)0
???? 上面的6個函數中只有? execve? 是系統調用,其他的函數都是庫函數,最終都需要調用? execve? 函數。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态