久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

UNIX環(huán)境編程學(xué)習(xí)筆記(19)

 戴維圖書館 2018-06-28

lienhua34
2014-10-07

在“進(jìn)程控制三部曲”中,我們學(xué)習(xí)到了 fork 是三部曲的第一部,用于創(chuàng)建一個新進(jìn)程,。但是關(guān)于 fork 的更深入的一些的東西我們還沒有涉及到,例如,fork 創(chuàng)建的新進(jìn)程與調(diào)用進(jìn)程之間的關(guān)系,、父子進(jìn)程的數(shù)據(jù)共享問題等,。fork 是否可以無限制的調(diào)用?如果不行的話,最大限制是多少?另外,我們還將學(xué)習(xí)一個 fork 的變體 vfork,。

1 fork 創(chuàng)建的新進(jìn)程與調(diào)用進(jìn)程之間的關(guān)系

UNIX 操作系統(tǒng)中的所有進(jìn)程之間的關(guān)系呈現(xiàn)一個樹形結(jié)構(gòu),。除了進(jìn)程 ID 為 0(swapper 進(jìn)程)和 1(init 進(jìn)程)的進(jìn)程之外的其他進(jìn)程,都會存在一個父進(jìn)程,。

fork 函數(shù)調(diào)用產(chǎn)生的新進(jìn)程的父進(jìn)程默認(rèn)即為調(diào)用進(jìn)程,。fork 函數(shù)調(diào)用產(chǎn)生的父子進(jìn)程各自的運行時間是不確定的。如果子進(jìn)程先于父進(jìn)程終止,這樣沒有什么問題,。但,如果父進(jìn)程先于子進(jìn)程終止,那么子進(jìn)程是不是就沒有了父進(jìn)程,進(jìn)程樹形結(jié)構(gòu)就被破壞了?對于這個問題,UNIX 系統(tǒng)這么處理的:如果某個進(jìn)程終止了,則將該進(jìn)程的所有尚未結(jié)束的子進(jìn)程的父進(jìn)程設(shè)置為 init 進(jìn)程(init 進(jìn)程是絕不會終止的),。其操作過程大致為:在一個進(jìn)程終止時,內(nèi)核逐個檢查所有活動進(jìn)程(因為 UNIX 沒有提供一個獲取某個進(jìn)程所有子進(jìn)程的接口),如果是正在終止的進(jìn)程的子進(jìn)程,則將其父進(jìn)程設(shè)置為 init 進(jìn)程。

2 父子進(jìn)程的數(shù)據(jù)共享問題

fork 函數(shù)創(chuàng)建的子進(jìn)程會獲得父進(jìn)程的數(shù)據(jù)空間,、堆和棧的副本,。但是,大多數(shù)情況下,fork 之后都會緊接著調(diào)用 exec 執(zhí)行新程序,從而覆蓋了從父進(jìn)程拷貝的這些副本,這就造成了內(nèi)核做了很多無用功。

現(xiàn)在很多的實現(xiàn)都采用寫時復(fù)制(Copy-On-Write,COW)技術(shù),。fork函數(shù)調(diào)用之后,父子進(jìn)程共享這些區(qū)域,而且內(nèi)核將這些區(qū)域的權(quán)限改為只讀的,。如果父、子進(jìn)程中任何一個試圖修改這些區(qū)域,則內(nèi)核只為要修改的區(qū)域做一份拷貝給該進(jìn)程,。

下面我們來看一個共享數(shù)據(jù)的例子,

復(fù)制代碼
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int glob = 0;
int
main(void)
{
    int var;
    pid_t pid;
    var = 0;
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
    } else if (pid == 0) {
        var++;
        glob++;
        printf("child: glob=%d, var=%d\n", glob, var);
        exit(0);
    }
    wait(NULL);
    printf("parent: glob=%d, var=%d\n", glob, var);
    exit(0);
}
復(fù)制代碼

該程序在 fork 之后的父進(jìn)程等待子進(jìn)程結(jié)束,而子進(jìn)程將整型變量glob 和 var 都加了 1. 編譯該程序,生成并執(zhí)行 forkdemo. 從下面的運行結(jié)果,我們看到子進(jìn)程修改的 glob 和 var 變量對父進(jìn)程沒有任何影響,。

lienhua34:demo$ gcc -o forkdemo forkdemo.c
lienhua34:demo$ ./forkdemo
child: glob=1, var=1
parent: glob=0, var=0

雖說子進(jìn)程享用的是父進(jìn)程的數(shù)據(jù)副本,子進(jìn)程的修改對父進(jìn)程沒有任何影響。但有個比較特殊的情況:文件 I/O,。fork 會將父進(jìn)程的所有打開文件描述符都復(fù)制到子進(jìn)程,。父子進(jìn)程中相同的文件描述符則共享同一個文件表項(關(guān)于文件描述符和文件表項的關(guān)系請參考文檔“內(nèi)核 I/O 數(shù)據(jù)結(jié)構(gòu)”)。下面我們看一個例子,

復(fù)制代碼
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int
main(void)
{
    pid_t pid;
    printf("before fork\n");
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
    } else if (pid == 0) {
        printf("in child process\n");
        exit(0);
    }
    wait(NULL);
    printf("in parent process\n");
    exit(0);
}
復(fù)制代碼

編譯該程序,生成并執(zhí)行文件 forkdemo,

復(fù)制代碼
lienhua34:demo$ gcc -o forkdemo forkdemo.c
lienhua34:demo$ ./forkdemo
before fork
in child process
in parent process
lienhua34:demo$ ./forkdemo > foo
lienhua34:demo$ cat foo
before fork
in child process
before fork
in parent process
復(fù)制代碼

在沒有對標(biāo)準(zhǔn)輸出重定向之前,運行 forkdemo 看不出啥問題,。當(dāng)重定向標(biāo)準(zhǔn)輸出到一個文件(./forkdemo > foo)時,我們可以看到父進(jìn)程打印的字符串在子進(jìn)程打印的字符串之后,。這是因為父子進(jìn)程標(biāo)準(zhǔn)輸出共享了同一個文件表項,也即共享了同一個文件偏移量。

另外,我們注意到在標(biāo)準(zhǔn)輸出沒有重定向時,字符串“before fork”只輸出一次,但是在標(biāo)準(zhǔn)輸出重定向到文件之后輸出了兩次,。這是因為標(biāo)準(zhǔn)I/O 庫函數(shù) printf 在標(biāo)準(zhǔn)輸出連接到終端設(shè)備時是行緩沖的,于是在 fork函數(shù)之后,緩沖區(qū)中的數(shù)據(jù)已經(jīng)被沖洗了,。而當(dāng)標(biāo)準(zhǔn)輸出重定向文件之后,printf 函數(shù)就變成了全緩沖了,在 fork 之前調(diào)用 printf 函數(shù)將字符串“before fork”寫到緩沖區(qū)中,fork 時該字符串還在緩沖區(qū)中,于是便拷貝一份給子進(jìn)程,。當(dāng)父子進(jìn)程都調(diào)用 exit 函數(shù)之后,緩沖區(qū)中的數(shù)據(jù)都被沖洗到文件中,于是被出現(xiàn)了兩份“before fork”。

3 fork 典型應(yīng)用場景

fork 有兩種典型的應(yīng)用場景:

· 創(chuàng)建一個新進(jìn)程執(zhí)行新的程序,。即調(diào)用 fork 之后子進(jìn)程立即調(diào)用 exec函數(shù)執(zhí)行一個新程序,例如文檔“進(jìn)程控制三部曲”中的示例 2.

· 父進(jìn)程希望復(fù)制自己,使父,、子進(jìn)程同時執(zhí)行不同的代碼段。這在網(wǎng)絡(luò)服務(wù)進(jìn)程中比較常見:父進(jìn)程等待客戶端的服務(wù)請求,當(dāng)接收到一個請求之后,父進(jìn)程調(diào)用 fork,然后讓子進(jìn)程處理該請求,而父進(jìn)程繼續(xù)等待下一個服務(wù)請求,。其代碼框架如下所示:

復(fù)制代碼
void serve(int sockfd)
{
    int clfd;
    pid_t pid;
    for (;;) {
        clfd = accept(sockfd, NULL, NULL);
        if (clfd < 0) {
            /* print error message */
            continue;
        }
        if ((pid = fork()) < 0) {
            /* fork error */
            continue;
        } else if (pid == 0) {
            /* deal with clfd in child process */
            close(clfd);
            exit(0);
        } else {
            /* in parent process,
            close the accepted socket "clfd",
            then continues to listen next socket connection. */
        } 
    }
}
復(fù)制代碼

4 fork 函數(shù)調(diào)用次數(shù)的最大限制是多少

每個實際用戶 ID 具有一個在任何時刻的最大進(jìn)程數(shù)。CHILD_MAX 規(guī)定了每個實際用戶 ID 在任一時刻可具有的最大進(jìn)程數(shù),。我們看下面一個例子,

復(fù)制代碼
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int
main(void)
{
    pid_t pid;
    int count;
    printf("CHILD_MAX: %ld\n", sysconf(_SC_CHILD_MAX));
    count = 1;
    for (;;) {
        if ((pid = fork()) < 0) {
            printf("fork error: %s\n", strerror(errno));
            break;
        } else if (pid == 0) {
            sleep(3);
            exit(0);
        }
        count++;
    }
    printf("count: %d\n", count);
    exit(0);
}
復(fù)制代碼

編譯該程序,生成并運行文件 forkdemo,

lienhua34:demo$ gcc -o forkdemo forkdemo.c
lienhua34:demo$ ./forkdemo
CHILD_MAX: 15969
fork error: Resource temporarily unavailable
count: 15737

從上面的運行結(jié)果可以看出我的系統(tǒng)規(guī)定了每個實際用戶 ID 在任一時刻可具有的最大進(jìn)程數(shù)為 15969,。而在 for 循環(huán)中 fork 創(chuàng)建了 15737 個進(jìn)程(包括調(diào)用進(jìn)程本身)之后,fork 就因為沒有可用資源而創(chuàng)建新進(jìn)程失敗。

5 fork 的變體vfork

vfork 函數(shù)是 fork 函數(shù)的一個變體,其調(diào)用序列和返回值與 fork 函數(shù)一致,不過兩者的語義不同,。維基百科上關(guān)于 vfork 的說明如下(參考fork(system_call)),。

Vfork is a variant of fork with the same calling convention and much the same semantics; it originated in the 3BSD version of Unix,[citation needed] the first Unix to support virtual memory. It was standardized by POSIX, which permitted vfork to have exactly the same behavior as fork, but marked obsolescent in the 2004 edition,[4] and has disappeared from subsequent editions.

我們看到在 POSIX 2004 版本中已經(jīng)將 vfork 函數(shù)注為過時的,而且在之后的版本中已經(jīng)不再出現(xiàn) vfork 函數(shù)了。但是,既然《APUE》中講到了這個,那我們就來看一下 vfork 函數(shù)跟 fork 函數(shù)到底有什么區(qū)別吧,。

vfork 函數(shù)和 fork 函數(shù)的區(qū)別有兩點:

1. fork 會將父進(jìn)程的地址空間拷貝給子進(jìn)程;而 vfork 沒有,子進(jìn)程在父進(jìn)程的地址空間中運行,。

2. fork 無法確保父子進(jìn)程的執(zhí)行順序;而 vfork 保證子進(jìn)程先執(zhí)行,父進(jìn)程會一直阻塞直到子進(jìn)程調(diào)用 exit 或 exec。(注:vfork 的這個特征可能會導(dǎo)致死鎖,若子進(jìn)程在調(diào)用 exit 或 exec 之前依賴于父進(jìn)程的進(jìn)一步動作,而父進(jìn)程也正在等待子進(jìn)程,于是出現(xiàn)了循環(huán)等待的問題,。)

我們來對比一下 vfork 和 fork 在處理數(shù)據(jù)方面有什么不同,

復(fù)制代碼
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int glob = 0;
int
main(void)
{
    int var;
    pid_t pid;
    var = 0;
    if ((pid = vfork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
    } else if (pid == 0) {
        var++;
        glob++;
        printf("child: glob=%d, var=%d\n", glob, var);
        exit(0);
    }
    printf("parent: glob=%d, var=%d\n", glob, var);
    exit(0);
}
復(fù)制代碼

上面程序拷貝了上面 fork 函數(shù)處理共享數(shù)據(jù)的示例程序,將 fork 改成vfork,并且去掉了 wait(NULL) 語句,。保存為 vforkdemo.c,編譯該程序,生成并執(zhí)行 vforkdemo 文件,

lienhua34:demo$ gcc -o vforkdemo vforkdemo.c
lienhua34:demo$ ./vforkdemo
child: glob=1, var=1
parent: glob=1, var=1

從上面的運行結(jié)果,我們看到 vfork 創(chuàng)建的子進(jìn)程修改了 glob 和 var變量之后,父進(jìn)程也看到了這個修改。

vfork 函數(shù)的出現(xiàn)原因可能是早期系統(tǒng)的 fork 沒有實現(xiàn)寫時復(fù)制技術(shù),導(dǎo)致每次 fork 調(diào)用做了很多無用功(大多數(shù)情況下都是 fork 之后調(diào)用 exec執(zhí)行新程序)且效率不高,于是便創(chuàng)造了 vfork 函數(shù),。而現(xiàn)在的實現(xiàn)基本都是采用寫時復(fù)制技術(shù),而且 vfork 函數(shù)使用不當(dāng)還會出現(xiàn)死鎖,于是 vfork函數(shù)也便沒有了存在的必要性,。

(done)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多