亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Unix > 正文

UNIX高級環境編程(9)進程控制(Process Control)- fork,vfork,僵尸進程,wait和waitpid

2024-06-28 13:21:31
字體:
來源:轉載
供稿:網友
UNIX高級環境編程(9)進程控制(PRocess Control)- fork,vfork,僵尸進程,wait和waitpid

本章包含內容有:

  • 創建新進程
  • 程序執行(program execution)
  • 進程終止(process termination)
  • 進程的各種ID

?

1 進程標識符(Process Identifiers)

每個進程都有一個唯一的標識符,進程ID(process ID)。

進程的ID是可重用的,如果一個進程被終止,那么它的進程ID會被系統回收,但是會延遲使用,防止該進程ID標識的新進程被誤認為是以前的進程。

三個特殊ID的進程:

  • Process ID 0:調度者進程,內核進程。
  • Process ID 1:init進程,內核引導程序最后啟動,負責啟動Unix系統。對應系統文件/sbin/init。
  • Process ID 2:pagedaemon,負責虛擬內存的頁管理。

獲取進程各種ID的相關函數:

函數聲明:

#include <unistd.h>

pid_t getpid(void); ? ? // Returns: process ID of calling process

pid_t getppid(void); ? ? ? ?// Returns: parent process ID of calling process

uid_t getuid(void); ? ? ? ?// Returns: real user ID of calling process

uid_t geteuid(void); ? ? ? // Returns: effective user ID of calling process

gid_t getgid(void); ? ? ? ?// Returns: real group ID of calling process

gid_t getegid(void); ? ? ? ?// Returns: effective group ID of calling process

這里的各種ID在前面第三篇中有說明,http://www.CUOXin.com/suzhou/p/4295535.html

?

2 fork函數

fork函數用于一個已存在的進程創建一個新的進程。

函數聲明:

#include <unistd.h>

pid_t fork(void);

函數細節:

  1. 創建的新進程叫做子進程,子進程是父進程的一個拷貝,拷貝數據段,堆和棧,而共享文本段。
  2. 該函數調用一次,但是返回兩次(父進程和子進程各返回一次,子進程返回0,父進程返回子進程的進程號)。這樣設置的原因是:父進程可以有多個子進程,父進程沒有方法獲取子進程的進程號,而子進程只可能有一個父進程,并且可以通過getppid方法獲取父進程的進程號。
  3. 寫時復制(copy-on-write)機制:子進程剛創建,在只讀的情況下和父進程共享數據段、堆和棧。如果子進程或者父進程試著修改這些數據,內核會進程這些數據的拷貝。
  4. 我們無法判斷子進程和父進程的執行順序,這取決于系統的調度順序。

Example:

#include "apue.h"

?

int ? ? globvar = 6;? ? ? ? /* external variable in initialized data */

char? ? buf[] = "a write to stdout/n";

?

int

main(void)

{

? ? int ? ? var;? ? ? ? /* automatic variable on the stack */

? ? pid_t ? pid;

?

? ? var = 88;

? ? if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)

? ? ? ? err_sys("write error");

? ? printf("before fork/n");? ? /* we don't flush stdout */

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } else if (pid == 0) {? ? ? /* child */

? ? ? ? globvar++;? ? ? ? ? ? ? /* modify variables */

? ? ? ? var++;

? ? } else {

? ? ? ? sleep(2); ? ? ? ? ? ? ? /* parent */

? ? }

?

? ? printf("pid = %ld, glob = %d, var = %d/n", (long)getpid(), globvar,

? ? ? var);

? ? exit(0);

}

執行結果:

NewImage

pid為12291的進程為子進程,對變量glob和var進行了加1。

當把輸出重定向到一個文件時,我們發現結果和直接輸出到終端中不太一樣:

NewImage

原因:?

  • 函數write不使用緩存,所以在系統調用fork之前調用write,結果直接輸出到標準輸出上;
  • 而標準輸出如果連接到終端,則是行緩沖(line buffered),否則是全緩沖(full buffered);
  • 在第一個例子中,換行導致printf寫入到標準輸出中的數據flush到終端上(行緩沖,換新行,導致前面一行被打?。?/li>
  • 在第二個例子中,我們將標準輸出重定向到文件,則使用全緩沖,printf的數據被緩存在buffer中沒有被打印,在fork時,buffer同樣被拷貝了一份,這樣父子進程都有了一個標準IO緩存(standard IO buffer);
  • 程序中的第二個printf將新的內從append到buffer中已有數據的后面,一同打印出,就看到了第二個例子中打印的結果。

?

文件共享(File Sharing)

當調用fork函數時,父進程的所有打開的文件描述符都會復制一份到子進程中,包括文件偏移量(file offset)。

所以當父子進程同時寫文件時,他們的操作都會更新同一個文件偏移量(file offset),加入子進程向文件中寫入了一部分數據,同時更新了file offset,那么父進程進行寫入操作時,會使用跟新以后的offset,從而避免了覆蓋了子進程寫入的數據。

父子進程共享文件如下圖所示:

NewImage

我們可以發現,父子進程擁有相同的文件描述符,又沒有其他的同步方式,所以他們的輸出可能會混起來(intermixed)。

fork之后,常見的處理父子進程擁有的文件描述符有兩種方式:

  • 父進程等待子進程完成。
  • 父子進程各自工作,關閉不需要的文件描述符。

除了打開的文件描述,其他的子進程會繼承自父進程的內容包括:

NewImage

父子進程不同的地方包括:

  • fork的返回值不同
  • 進程ID不同
  • 進程的父進程ID不同
  • 子進程的tms_utime, tms_stime, itms_cutime和itms_cstime值被置為0
  • 父進程的文件鎖不會被子進程繼承
  • 子進程的pending signals被置空

?

3 vfork

vfork和fork有相同的返回值。

vfork和fork的不同點:

  • 函數目的:vfork創建的子進程是為了讓子進程執行一個新的程序
  • 復制操作:不復制父進程的地址空間,而是直接運行在父進程的地址空間中,直到子進程調用exec或者exit
  • 效率:所以vfork的執行效率比fork要高,因為它沒有copy操作
  • 不確定的結果:但是如果子進程修改了數據、調用函數或者沒有調用exec和exit方法,則會造成不確定的結果
  • 子進程先運行:vfork保證子進程先運行

?Example:

#include "apue.h"

?

int ? ? globvar = 6;? ? ? ? /* external variable in initialized data */

?

int

main(void)

{

? ? int ? ? var;? ? ? ? /* automatic variable on the stack */

? ? pid_t ? pid;

?

? ? var = 88;

? ? printf("before vfork/n"); ? /* we don't flush stdio */

? ? if ((pid = vfork()) < 0) {

? ? ? ? err_sys("vfork error");

? ? } else if (pid == 0) {? ? ? /* child */

? ? ? ? globvar++;? ? ? ? ? ? ? /* modify parent's variables */

? ? ? ? var++;

? ? ? ? _exit(0); ? ? ? ? ? ? ? /* child terminates */

? ? }

?

? ? /* parent continues here */

? ? printf("pid = %ld, glob = %d, var = %d/n", (long)getpid(), globvar,

? ? ? var);

?

? ? exit(0);

}

運行結果:

NewImage

?

4 進程退出和僵尸進程

正常退出:三個函數exit,?

如果子進程不正常退出,則內核保證記錄該進程的異常退出狀態,該進程的父進程可以通過調用wait或者waitpid函數獲取該子進程的異常退出狀態。

如果父進程在子進程之前終止,則init進程成為該子進程的父進程。從而保證每個進程都有父進程。

如果子進程先終止(異常終止或者正常退出),內核會保存該子進程的部分信息,包括進程pid,進程終止時的狀態和該進程占用的CPU時間,同時內核會清除該進程占用的內存,關閉所有已經打開的文件描述符。父進程可以通過檢查該信息獲取子進程的終止情況。

如果子進程先終止,而沒有父進程調用waitpid獲取該子進程的信息,那么這種進程被成為僵尸進程。使用ps命令可以看到僵尸進程的相關信息。

如果父進程為init進程,那么子進程異常終止并不會成為僵尸進程,因為init進程會對它的所有子進程調用wait函數獲取子進程的終止狀態。

?

5 wait和waitpid函數

子進程終止,內核會向父進程發送SIGCHLD信號。父進程默認的行為是忽略該信號,父進程也可以設置一個信號處理函數,當捕捉到該信號時,調用該處理函數,在后面的相關章節會介紹信號相關的概念。

本節介紹的wait和waitpid函數的作用是:

  • 如果子進程在運行,則阻塞;
  • 如果子進程終止,并且子進程的終止狀態被父進程獲取,則該函數立刻返回該終止狀態;
  • 如果該進程沒有任何子進程,則返回錯誤。

需要注意的一點是,如果我們在接收到SIGCHLD信號后,調用wait函數,則該函數會立刻返回。在其他情況下調用wait函數,則會阻塞。

函數聲明:

#include <sys/wait.h>

pid_t wait(int *statloc);

pid_t waitpid(pid_t pid, int *statloc, int options);?

// Both return: process ID if OK, 0,or -1 on error

兩個函數之間的區別:

  • wait函數會阻塞,一直到一個子進程終止;waitpid函數的參數options可以指定不阻塞;
  • waitpid函數可以選擇不阻塞,并且可以指定等待某一個子進程終止。

函數細節:

  • 如果一個子進程終止并成為了僵尸進程,wait函數立刻返回該子進程的狀態;
  • 如果一個進程調用wait()函數并阻塞,并且有多個子進程,則當有一個子進程終止時,wait()函數返回;
  • 參數statloc是一個整型指針,如果該參數不為null,則子進程的終止狀態被保存在該參數指向的整型中;如果我們不關心進程的終止狀態,statloc傳入null就行;

返回值檢查:

使用四個宏來檢查wait和waitpid函數來獲取子進程的終止狀態(terminated status),如退出狀態,信號值等信息。

四個宏的具體說明見下表所示:

NewImage

pid的取值對waitpid函數行為的影響:

  • pid == -1:行為和wait相同,等待任意一個子進程終止
  • pid > 0:等待進程號為pid的進程終止
  • pid ==0:等待進程組號和調用進程的進程組號相同的任意一個子進程終止
  • pid < -1:等待進程組號等于pid的任意一個子進程終止

參數option的取值:

NewImage

waitpid函數提供了三個wait沒有的特性:

  • waitpid可以讓我們等待某一個特定的進程;
  • waitpid提供了不阻塞版本的wait函數;
  • option參數WCONTINUED和WUNTRACED為系統的任務控制(job control)提供了支持。

?

Example:

#include "apue.h"

#include <sys/wait.h>

?

int

main(void)

{

? ? pid_t ? pid;

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } else if (pid == 0) {? ? ? /* first child */

? ? ? ? if ((pid = fork()) < 0)

? ? ? ? ? ? err_sys("fork error");

? ? ? ? else if (pid > 0)

? ? ? ? {

? ? ? ? ? ? exit(0);? ? /* parent from second fork == first child */

? ? ? ? }

?

? ? ? ? /*

?? ? ? ? * We're the second child; our parent becomes init as soon

?? ? ? ? * as our real parent calls exit() in the statement above.

?? ? ? ? * Here's where we'd continue executing, knowing that when

?? ? ? ? * we're done, init will reap our status.

? ? ? ? ?*/

? ? ? ? sleep(2);

? ? ? ? printf("second child, parent pid = %ld/n", (long)getppid());

? ? ? ? exit(0);

? ? }

?

? ? if (waitpid(pid, NULL, 0) != pid) ? /* wait for first child */

? ? ? ? err_sys("waitpid error");

?

? ? /*

?? ? * We're the parent (the original process); we continue executing,

?? ? * knowing that we're not the parent of the second child.

?? ? */

? ? exit(0);

}

執行結果:

NewImage

結果分析:

在這里我們fork了兩次,原因是,當我們想fork一個子進程出來,而我們不希望父進程阻塞在wait函數,并且不希望由于父進程沒有調用wait函數先退出導致子進程成為僵尸進程,那么fork兩次,并且退出第一個子進程,可以使得父進程及時退出,并且第二個子進程的父進程變成init進程。

?

小結

本篇主要介紹了fork、vfork、僵尸進程、wait和waitpid函數,這些在unix環境中都是很重要的概念和函數,并且在面試中也經常問到。

下一篇的內容包括:

  • 解釋器文件(interpreter files)
  • 系統調用(system function)

?

參考資料:

《Advanced Programming in the UNIX Envinronment 3rd》

?


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
激情久久av一区av二区av三区| 亚洲欧美另类自拍| 欧美人与性动交a欧美精品| 久久久人成影片一区二区三区| 亚洲精品美女久久久| 欧美多人爱爱视频网站| 欧美国产高跟鞋裸体秀xxxhd| 91在线网站视频| 色偷偷噜噜噜亚洲男人的天堂| 欧美成人三级视频网站| 国产亚洲精品美女久久久久| 国内精品久久久久久中文字幕| 尤物99国产成人精品视频| 亚洲性生活视频| 午夜精品三级视频福利| 国产视频久久久久| 黑人巨大精品欧美一区二区| 亚洲精品v欧美精品v日韩精品| 欧美风情在线观看| 日韩高清a**址| 亚洲一区亚洲二区| 国产亚洲免费的视频看| 中文字幕欧美专区| 奇门遁甲1982国语版免费观看高清| 亚洲一区久久久| 日韩精品999| 久久久久久国产精品美女| 亚洲国产免费av| 疯狂蹂躏欧美一区二区精品| 日韩精品高清在线观看| 懂色av影视一区二区三区| 亚洲mm色国产网站| 国产99久久精品一区二区 夜夜躁日日躁| 91免费人成网站在线观看18| 国产日韩视频在线观看| 欧美xxxx做受欧美| 国产国语videosex另类| 一区二区三区黄色| 欧美电影第一页| 97在线视频精品| 国产欧美一区二区三区四区| 日韩在线高清视频| 国产精品极品尤物在线观看| 精品久久久久久久大神国产| 热久久美女精品天天吊色| 日韩精品在线免费观看| 亚洲一区二区在线播放| 91高清免费视频| 日韩成人激情影院| 国产丝袜一区二区| 国内精品视频一区| 日韩av一卡二卡| 91夜夜未满十八勿入爽爽影院| 伊人久久五月天| 亚洲一区二区在线| 欧美天天综合色影久久精品| 国产91露脸中文字幕在线| 久久久久久久爱| 日韩在线观看免费全| 成人欧美一区二区三区在线湿哒哒| 国产午夜精品理论片a级探花| 欧美激情中文网| 亚洲天堂av在线播放| 久久亚洲影音av资源网| 久久成人av网站| 国产欧美精品一区二区三区-老狼| 日韩最新中文字幕电影免费看| 亚洲精品国产免费| 久久精品国产精品亚洲| 国产女同一区二区| 日韩欧中文字幕| 九九热这里只有精品6| 成人性生交大片免费看小说| 日本一区二区在线免费播放| 久久精品电影一区二区| 国产精品久久久久久久7电影| 亚洲综合精品伊人久久| 国产91在线视频| 国产精品爱啪在线线免费观看| 亚洲影院高清在线| 国产日韩av在线| 久久久久久久爱| www国产91| 日韩大片在线观看视频| 欧美资源在线观看| 国产日韩在线观看av| 一本一本久久a久久精品综合小说| 欧美富婆性猛交| 日韩精品在线影院| 欧美日韩一区二区精品| 欧美一区二区.| 国产精品成人一区| 国产精品露脸自拍| 日韩美女在线观看一区| www.日韩不卡电影av| 精品国产老师黑色丝袜高跟鞋| 91在线高清视频| 一个色综合导航| 欧美日韩第一视频| 国产精品久久久久久久9999| 久久国产精彩视频| 亚洲人线精品午夜| 岛国精品视频在线播放| 国产成人精品视频| 国产精品99久久久久久久久久久久| 裸体女人亚洲精品一区| 亚洲大胆人体av| 成人激情视频在线观看| www国产精品视频| 欧美性视频在线| 国产精品高清在线观看| 亚洲男人天堂九九视频| 欧美高清激情视频| 深夜福利日韩在线看| 欧洲成人午夜免费大片| 国产视频丨精品|在线观看| 国产精品久久久久秋霞鲁丝| 插插插亚洲综合网| 国产精品高清免费在线观看| 成人福利视频在线观看| 欧美另类在线播放| 久久亚洲一区二区三区四区五区高| www.xxxx欧美| 国产精品视频最多的网站| 狠狠躁天天躁日日躁欧美| 欧美大片网站在线观看| 亚洲精品大尺度| 国自在线精品视频| 午夜精品一区二区三区在线视频| 亚洲色图欧美制服丝袜另类第一页| 亚洲成人免费网站| 成人免费高清完整版在线观看| 亚洲精品98久久久久久中文字幕| 国产在线观看一区二区三区| 在线视频亚洲欧美| 久久天天躁日日躁| 精品久久久国产| 亚洲人成在线电影| 91大神福利视频在线| 亚洲欧美三级伦理| 色综合久久精品亚洲国产| 亚洲视屏在线播放| 国产日韩精品在线观看| 国产伊人精品在线| 亚洲性猛交xxxxwww| 九色精品免费永久在线| 欧美又大又硬又粗bbbbb| 国产一区二区精品丝袜| 亚洲国产精品网站| 中文字幕少妇一区二区三区| 亚洲欧美一区二区精品久久久| 国产精品欧美日韩| 中文字幕精品一区久久久久| 国产在线视频一区| 日韩电影在线观看永久视频免费网站| 日韩欧美成人区| 亚洲免费福利视频| 亚洲娇小xxxx欧美娇小| 久久成人人人人精品欧| 国产精品久久久av久久久| 日韩欧美中文字幕在线播放| 91精品国产91久久久| 国产亚洲欧洲在线| 亚洲xxxxx|