unix高級編程第5版pdf,linux進程控制(一)--unix環境高級編程讀書筆記

 2023-11-19 阅读 23 评论 0

摘要:???? 1.進程PID和特殊的3個進程 ???? 每一個進程在系統中都有一個唯一的標識,這個標識叫做進程標識符,或者叫? PID(process identity)。我們可以通過調用? getpid? 函數來獲取一個進程的PID,也可以調用getppid函數來獲取當前進程的父進程P

???? 1.進程PID和特殊的3個進程

???? 每一個進程在系統中都有一個唯一的標識,這個標識叫做進程標識符,或者叫? PID(process identity)。我們可以通過調用? getpid? 函數來獲取一個進程的PID,也可以調用getppid函數來獲取當前進程的父進程PID。在? linux? 系統中,有三個特殊的進程,它們的進程? PID? 分別為0,1,2。0號進程是系統進程,它存在與內核當中,而不是磁盤文件上。該進程常被成為交換進程,或者叫? swapper。1號進程就是? init? 進程,這個進程用來讀取一些初始化文件,并且引導系統到一個狀態,如多用戶狀態,init? 進程是一個用戶進程。2號進程是頁精靈進程,用來做一些和虛擬內存相關的工作,可是這個進程我沒有在系統中找到。如果有知道的朋友,請告訴我,這個頁精靈進程在系統中是哪一個進程,另外頁精靈進程也是系統進程。需要說明的是,這3個進程都是由特殊的方式產生的,除了這3個進程以外,linux? 系統中的其它進程都是由調用? fork? 函數產生的。

???? 2.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.out

得到輸出結果如下:
a write to stdout
before fork
pid=3131	glob=7	var=89
pid=3130	glob=6	var=88

如果將程序的輸出結果重定向到一個文件:
./a.out > /tmp/a

查看? /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

???? 看到這個結果,我們可能會感到奇怪。為什么第一次輸出了一次? before fork,而后面重定向到文件之后,就輸出了兩次呢?原因其實很簡單,我們之前說過,fork之后的子進程會復制父進程的數據空間和堆棧。因為? write? 函數是系統調用,沒有緩存,所以立即就輸出了。而printf在交互的情況下是行緩存的,它是由換行符刷新的,所以當沒有重定向時,printf? 碰到換行符就立即輸出了。而當把這個程序重定向到一個文件時,printf? 函數就變為全緩存的了。所以它并不立即輸出,而是保留在緩存中。在調用? fork? 之后,當子進程復制父進程時,把父進程緩存中的內容也復制到子進程的工作空間中。所以? before fork? 分別在父進程和子進程中各輸出一次。

???? fork? 函數創建的子進程,會把在父進程中打開的文件描述符,復制到子進程中,也就是說它們共享同一個文件表項。

???? 3.vfork函數

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

輸出結果肯定是上面的樣子,因為? vfork? 函數保證子進程先執行。在子進程中修改了? var? 和? glob? 的值之后,就調用了? _exi? t函數退出了。因為vfork函數產生的子進程并不復制父進程的工作空間,它是在父進程的工作空間中運行的,所以子進程改變了父進程工作空間中的數據,這是很正常的。

???? 4.init? 進程領養其他進程

???? 經過前面的介紹,我們已經了解了? init? 進程。它不是一個系統進程,它就是存在于? /sbin/init? 文件。它的作用是在系統啟動時用來讀取一些初始化信息,將系統引導到一個狀態。另外,它還有一個重要的角色是收養系統中的孤兒進程。那么什么進程叫做孤兒進程呢?顧名思義,孤兒進程就是其父進程在其終止之前終止的進程。

???? init? 進程收養孤兒進程的手續是怎么樣的呢?當一個進程要終止時,內核會逐個檢查系統中的各個進程,查看它們的父進程是否是正要終止的進程,如果是這樣的話,就將它們的父進程修改為? init? 進程。

???? 另外,關于init進程清除系統中的僵死進程和僵死進程的一些概念,請參考博客點擊打開鏈接。

???? 5.wait和waitpid

shell高級編程。???? wait? 和? waitpid? 函數,用來在父進程中等待子進程的結束。wait? 函數用來等待父進程的任何一個子進程,只要有子進程終止,它就立即返回。調用? wait? 函數,會發生3種情況:

1.當該進程沒有任何子進程時,會發生錯誤,返回-1
2.當該進程的子進程都在運行,尚沒有子進程結束時,該進程會發生阻塞
3.當該進程已經有子進程終止時,可期待 ?wait ?函數立即返回,返回值為終止子進程 ?PID

waitpid? 函數和? wait? 函數的不同在于,通過參數的設置,它既可以等待任何一個子進程(這時候功能相當于? wait),又可以等待指定? pid? 的子進程,并且它可以不用阻塞。

???? 6.exec函數

???? 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[]);

這六個函數的記憶方法是以? p? 結尾的? execlp? 和? execvp? 中第一個參數是文件名,而不是路徑。當執行這個文件名指定的文件時,就會在? PATH? 指定的路徑中,搜尋指定的文件。如果在文件名中出現“/”則按路徑來處理。名字中包含“l” 的函數? execl ,execlp 和? execle,說明了在這些函數中傳遞給要執行的程序的命令行參數是一個列表,這個列表以空指針NULL結尾,它們參數排列一般如下:
pathname , arg1 , arg2 , (char*)0

而函數名字中帶有v的函數? execv, execvp 和 execve,一般是將命令行參數的指針放在一個數組? argv? 中,這個數組以空指針? NULL? 結尾。函數名中包含“e”的函數? execve? 和? execle,它規定了需要向該函數傳遞環境變量。

???? 上面的6個函數中只有? execve? 是系統調用,其他的函數都是庫函數,最終都需要調用? execve? 函數。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/183712.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息