国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 操作系統(tǒng) > 正文

進程控制之fork函數(shù)

2024-06-28 13:28:36
字體:
供稿:網(wǎng)友
進程控制之fork函數(shù)

一個現(xiàn)有進程可以調(diào)用fork函數(shù)創(chuàng)建一個新進程。

#include <unistd.h>pid_t fork( void );返回值:子進程中返回0,父進程中返回子進程ID,出錯返回-1

由fork創(chuàng)建的新進程被稱為子進程(child PRocess)。fork函數(shù)被調(diào)用一次,但返回兩次。兩次返回的唯一區(qū)別是子進程的返回值是0,而父進程的返回值則是新子進程的進程ID。將子進程ID返回給父進程的理由是:因為一個進程的子進程可以有多個,并且沒有一個函數(shù)使一個進程可以獲得其所有子進程的進程ID。fork使子進程得到返回值0的理由是:一個進程只會有一個父進程,所以子進程總是可以調(diào)用getppid以獲得其父進程的進程ID(進程ID 0總是由內(nèi)核交換進程使用,所以一個子進程的進程ID不可能為0)。

子進程和父進程繼續(xù)執(zhí)行fork調(diào)用之后的指令。子進程是父進程的副本。例如,子進程獲得父進程的數(shù)據(jù)空間、堆和棧的副本注意,這是子進程所擁有的副本。父、子進程并不共享這些存儲空間部分。父、子進程共享正文段(text,代碼段)。

由于在fork之后經(jīng)常跟隨著exec,所以現(xiàn)在的很多實現(xiàn)并不執(zhí)行一個父進程數(shù)據(jù)段、棧和堆的完全復制。作為替代,使用了寫時復制(Copy-On-Write,COW)技術(shù)。這些區(qū)域由父、子進程共享,而且內(nèi)核將它們的訪問權(quán)限改變?yōu)橹蛔x的。如果父、子進程中的任一個試圖修改這些區(qū)域,則內(nèi)核只為修改區(qū)域的那塊內(nèi)存制作一個副本,通常是虛擬存儲器系統(tǒng)中的一“頁”。

linux 2.4.22提供了另一種新進程創(chuàng)建函數(shù)——clone(2)系統(tǒng)調(diào)用。這是一種fork的泛型,它允許調(diào)用者控制哪些部分由父、子進程共享。

程序清單8-1中的程序演示了fork函數(shù),從中可以看到子進程對變量所作的改變并不影響父進程中該變量的值。

程序清單8-1 fork函數(shù)示例

[root@localhost apue]# cat prog8-1.c#include "apue.h"int     glob = 6;       /* external variable in initialized data */char    buf[] = "a write to stdout/n";intmain(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 */        {                glob++;         /* modify variables */                var++;        }        else        {                sleep(2);       /* parent */        }        printf("pid = %d, glob = %d, var = %d/n", getpid(), glob, var);        exit(0);}

如果執(zhí)行此程序則得到:

[root@localhost apue]# ./prog8-1a write to stdoutbefore forkpid = 13367, glob = 7, var = 89    子進程的變量值改變了pid = 13366, glob = 6, var = 88    父進程的變量值沒有改變[root@localhost apue]# ./prog8-1 > tmp.out[root@localhost apue]# cat tmp.outa write to stdoutbefore forkpid = 13369, glob = 7, var = 89before forkpid = 13368, glob = 6, var = 88

一般來說,在fork之后是父進程先執(zhí)行還是子進程先執(zhí)行是不確定的。這取決于內(nèi)核所使用的調(diào)度算法。如果要求父、子進程之間相互同步,則要求某種形式的進程間通信。

當寫到標準輸出時,我們將buf長度減去1作為輸出字節(jié)數(shù),這是為了避免將終止null字節(jié)寫出。strlen計算不包括終止null字節(jié)的字符串長度,而sizeof則計算包括終止null字節(jié)的緩沖區(qū)長度。兩者之間的另一個差別是,使用strlen需進行一次函數(shù)調(diào)用,而對于sizeof而言,因為緩沖區(qū)已用已知字符串進行了初始化,其長度是固定的,所以sizeof在編譯時計算緩沖區(qū)長度。

注意程序清單8-1中fork與I/O函數(shù)之間的交互關系。write函數(shù)是不帶緩沖的。因為在fork之前調(diào)用write,所以其數(shù)據(jù)寫到標準輸出一次。但是標準I/O庫是帶緩沖的(這里用到了標準I/O庫的printf函數(shù))。如果標準輸出連到終端設備,則它是行緩沖的,否則它是全緩沖的。當以交互方式運行該程序時(此時是行緩沖的),只得到該printf輸出的行一次,其原因是標準輸出緩沖區(qū)在fork之前已由換行符沖洗。但是當將標準輸出重定向到一個文件時(此時是全緩沖的),卻得到printf輸出行兩次。其原因是,在fork之前調(diào)用了printf一次,但當調(diào)用fork時,該行數(shù)據(jù)仍在緩沖區(qū)中(我們沒有用fflush沖洗緩沖區(qū)),然后在將父進程數(shù)據(jù)空間復制到子進程中時,該緩沖區(qū)也被復制到子進程中于是那時父、子進程各自有了帶該行內(nèi)容的標準I/O緩沖區(qū)。(子進程復制父進程緩沖區(qū)對程序的影響實例解析可參考:http://blog.csdn.net/lollipop_jin/article/details/8774057)在exit之前的第二個printf將其數(shù)據(jù)添加到現(xiàn)有的緩沖區(qū)中。當每個進程終止時,最終會沖洗其緩沖區(qū)中的副本

文件共享

對程序清單8-1需注意的另一點是:在重定向父進程的標準輸出時,子進程的標準輸出也被重定向。實際上,fork的一個特性是父進程的所有打開文件描述符都被復制到子進程中。父、子進程的每個相同的打開描述符共享一個文件表項

考慮下述情況,一個進程具有三個不同的打開文件,它們是標準輸入、標準輸出和標準出錯。在從fork返回時,我們有了如圖8-1所示的結(jié)構(gòu)。

8-share-file

這種共享文件的方式使父、子進程對同一文件使用了一個文件偏移量。如果父、子進程寫到同一描述符文件,但又沒有任何形式的同步(例如使父進程等待子進程),那么它們的輸出就會相互混合(假定所有的描述符是在fork之前打開的)。

在fork之后處理文件描述符有兩種常見的情況

(1)父進程等待子進程完成。在這種情況下,父進程無需對其描述符做任何處理。當子進程終止后,它曾進行過讀、寫操作的任一共享描述符的文件偏移量已執(zhí)行了相應的更新。

(2)父、子進程各自執(zhí)行不同的程序段。在這種情況下,在fork之后,父、子進程各自關閉它們不需要使用的文件描述符,這樣就不會干擾對方使用的文件描述符。這種方法是網(wǎng)絡服務進程中經(jīng)常使用的。

除了打開文件之外,父進程的很多其他屬性也由子進程繼承(可以理解為共享),包括:

  • 實際用戶ID、實際組ID、有效用戶ID、有效組ID。
  • 附加組ID。
  • 進程組ID。
  • 會話ID。
  • 控制終端。
  • 設置用戶ID標志和設置組ID標志。
  • 當前工作目錄。
  • 根目錄。
  • 文件模式創(chuàng)建屏蔽字。
  • 信號屏蔽和安排。
  • 針對任一打開文件描述符的在執(zhí)行時關閉(close-on-exec)標志。
  • 環(huán)境。
  • 連接的共享存儲段。
  • 存儲映射。
  • 資源限制。

父、子進程之間的區(qū)別是:

  • fork的返回值。
  • 進程ID不同。
  • 兩個進程具有不同的父進程ID:子進程的父進程ID是創(chuàng)建它的進程的ID,而父進程的父進程ID則不變。
  • 子進程的tms_utime、tms_stime、tms_cutime已經(jīng)tms_ustime均被設置為0.
  • 父進程設置的文件鎖不會被子進程繼承。
  • 子進程的未處理的鬧鐘(alarm)被清除。
  • 子進程的未處理信號集設置為空集。

使fork失敗的兩個主要原因是:系統(tǒng)中已經(jīng)有了太多的進程(通常意味著某個方面出了問題),或者該實際用戶ID的進程總數(shù)超過了系統(tǒng)限制(CHILD_MAX)。

fork有下面兩種用法:

(1)一個父進程希望復制自己,使父、子進程同時執(zhí)行不同的代碼段。這在網(wǎng)絡服務進程中是常見的——父進程等待客戶端的服務請求。當這種請求到達時,父進程調(diào)用fork,使子進程處理此請求。父進程則繼續(xù)等待下一個服務請求到達。

(2)一個進程要執(zhí)行一個不同的程序。這對shell是常見的情況。在這種情況下,子進程從fork返回后立即調(diào)用exec。

某些操作系統(tǒng)將(2)中的兩個操作(fork之后執(zhí)行exec)組合成一個,并稱其為spawn。UNIX將這兩個操作分開,因為在很多場合需要單獨使用fork,其后并不跟隨exec。另外,將這兩個操作分開,使得子進程在fork和exec之間可以更改自己的屬性。例如I/O重定向、用戶ID、信號安排等。

本篇博文內(nèi)容摘自《UNIX環(huán)境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 和田县| 彭泽县| 华容县| 迁安市| 孝义市| 大渡口区| 周口市| 济源市| 牟定县| 农安县| 武威市| 宁津县| 花莲市| 柳江县| 镇康县| 达孜县| 日照市| 宜宾县| 清流县| 都江堰市| 星座| 赫章县| 巩留县| 赤壁市| 华蓥市| 潼关县| 扶绥县| 商河县| 昌都县| 南昌县| 赤峰市| 广河县| 陆河县| 平陆县| 遂平县| 青冈县| 麻栗坡县| 仁怀市| 深州市| 故城县| 满洲里市|