午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

Linux fork與vfork的深入分析

 張金富 2010-12-02
Linux fork與vfork的深入分析
發(fā)布者:chinaitlab  日期: 2010-10-27 00:00:00 
 

  一)fork的概述

  .操作系統(tǒng)對進程的管理,是通過進程表完成的.進程表中的每一個表項,記錄的是當前操作系統(tǒng)中一個進程的信息.

  .進程在系統(tǒng)的唯一標識是PID,PID是一個從1到32768的正整數(shù),其中1一般是特殊進程init,其它進程從2開始依次編號.當用完32768后,從2重新開始.

  .一個稱為“程序計數(shù)器(program counter, pc)”的寄存器,指出當前占用 CPU的進程要執(zhí)行的下一條指令的位置

  .當分給某個進程的 CPU時間已經(jīng)用完,操作系統(tǒng)將該進程相關(guān)的寄存器的值,保存到該進程在進程表中對應的表項里面,把將要接替這個進程占用 CPU的那個進程的上下文,從進程表中讀出,并更新相應的寄存器.

  二)fork的一個例子:

  #include <sys/types.h>

  #include <sys/types.h>

  #include <unistd.h>

  #include <stdio.h>

  int main()

  {

  pid_t pid;

  pid=fork();

  if(pid<0)

  printf("error in fork!");

  else if(pid==0)

  printf("I am the child process,ID is %d\n",getpid());

  else

  printf("I am the parent process,ID is %d\n",getpid());

  }

  gcc test1.c -o test1

  debian:/tmp# ./test1

  I am the child process,ID is 2723

  I am the parent process,ID is 2722

  程序分析:

  1)pid=fork();

  先來看看子進程的表現(xiàn):

  操作系統(tǒng)調(diào)用fork()函數(shù)創(chuàng)建一個新的進程(子進程),并且在進程表中相應為它建立一個新的表項,

  此時子進程得到CPU的調(diào)度,它的上下文被換入,占據(jù) CPU,操作系統(tǒng)對fork的實現(xiàn),使得子進程中fork調(diào)用返回0

  所以在這個進程中pid=0,這個進程繼續(xù)執(zhí)行的過程中,if語句中 pid<0不滿足,但是pid= =0是true。所以輸出i am the child process...

  父進程的表現(xiàn):

  操作系統(tǒng)對fork的實現(xiàn),使這個調(diào)用在父進程中返回剛剛創(chuàng)建的子進程的pid(一個正整數(shù)),所以下面的if語句中pid<0,

  pid==0的兩個分支都不會執(zhí)行。所以輸出i am the parent process...

  2)對子進程來說,fork返回給它0,但它的pid絕對不會是0,之所以fork返回0給它,是因為它隨時可以調(diào)用getpid()來獲取自己的pid

  3)fork之后父子進程除非采用了同步手段,否則不能確定誰先運行,也不能確定誰先結(jié)束.認為子進程結(jié)束后父進程才從fork返回的,這是不對的,fork不是這樣的,vfork才這樣。

  4)父進程執(zhí)行了所有的進程,而子進程只執(zhí)行了fork()后面的程序,這是因為子進程繼承了父進程的PC(程序計數(shù)器).

  三)fork的另一個例子:

  #include <stdio.h>

  #include <sys/types.h>

  #include <unistd.h>

  int main()

  {

  pid_t pid1;

  pid_t pid2;

  pid1 = fork();

  pid2 = fork();

  printf("pid1:%d, pid2:%d\n", pid1, pid2);

  }

  gcc test2.c -o test2

  ./test2

  pid1:18938, pid2:0

  pid1:0, pid2:0

  pid1:18938, pid2:18939

  pid1:0, pid2:18940

  程序分析:

  1)執(zhí)行test2時,啟動一個進程,設這個進程為P0,PID為xxxxx

  2)當執(zhí)行到pid1 = fork();時,P0啟動了一個進程,設這個進程為P1,它的PID為18938,暫且不管P1.

  3)P0中的fork返回18938給pid1,繼續(xù)執(zhí)行到pid2 = fork();此時啟動另一個新的進程,設為P2,P2的PID為18939 ,同樣暫且不管P2.

  4)P0的第二個fork返回18939給p2,最后P0的執(zhí)行結(jié)果為pid1:18938, pid2:18939

  5)再看P2,P2生成時,P0中的pid1=18938,所以P2中的pid1繼承P0的pid1=18938,而作為子進程pid2=0,P2從第二個fork后開始執(zhí)行,

  最后輸出pid1:18938, pid2:0.

  6)回頭看P1,P1中第一條fork返回0給pid1,然后接著執(zhí)行后面的語句.而后面接著的語句是pid2 = fork();執(zhí)行到這里,P1又產(chǎn)生了一個新進程,設為P3,先不管P3.

  7)P1中第二條fork將P3的PID返回給pid2,P3的PID為18940,所以P1的pid2=18940。P1繼續(xù)執(zhí)行后續(xù)程序,結(jié)束,輸出“pid1:0, pid2:18940”.

  8)P3作為P1的子進程,繼承P1中pid1=0,并且第二條fork將0返回給pid2,所以P3最后輸出“pid1:0, pid2:0”.

  9)所有的進程都執(zhí)行完畢.

  四)vfork與fork的區(qū)別

  vfork與fork主要有三點區(qū)別:

  .fork():子進程拷貝父進程的數(shù)據(jù)段,堆棧段

  vfork():子進程與父進程共享數(shù)據(jù)段

  .fork()父子進程的執(zhí)行次序不確定vfork 保證子進程先運行,在調(diào)用 exec 或 exit 之前與父進程數(shù)據(jù)是共享的,在它調(diào)用 exec或 exit 之后父進程才可能被調(diào)度運行。

  .vfork()保證子進程先運行,在它調(diào)用 exec 或 exit 之后父進程才可能被調(diào)度運行.如果在調(diào)用這兩個函數(shù)之前子進程依賴于父進程的進一步動作,則會導致死鎖。

  1)先用fork()進行試驗

  #include <unistd.h>

  #include <stdio.h>

  int main(void)

  {

  pid_t pid;

  int count=0;

  pid=fork();

  count++;

  printf("count= %d\n",count);

  return 0;

  }

 

 

 

  分析:

  通過上面fork()的說明,這個程序的輸出應該是:

  ./test

  count= 1

  count= 1

  2)而將fork()換成vfork()呢,程序如下

  #include <unistd.h>

  #include <stdio.h>

  int main(void)

  {

  pid_t pid;

  int count=0;

  pid=vfork();

  count++;

  printf("count= %d\n",count);

  return 0;

  }

  執(zhí)行結(jié)果:

  ./test

  count= 1

  count= 1

  Segmentation fault (core dumped)

  分析:

  通過將fork()換成vfork(),由于vfork()是共享數(shù)據(jù)段,為什么結(jié)果不是2呢,答案是:

  vfork保證子進程先運行,在它調(diào)用 exec 或 exit 之后父進程才可能被調(diào)度運行.如果在調(diào)用這兩個函數(shù)之前子進程依賴于父進程的進一步動作,則會導致死鎖.

  3)做最后的修改,在子進程執(zhí)行時,調(diào)用_exit(),程序如下:

  #include <unistd.h>

  #include <stdio.h>

  #include <sys/types.h>

  int main(void)

  {

     pid_t pid;

     int count=0;

     pid=vfork();

     if(pid==0)

     {

        count++;

        _exit(0);

     }

     else

     {

        count++;

     }

     printf("count= %d\n",count);

     return 0;

  }

  執(zhí)行結(jié)果:

  ./test

  count= 2

  分析:如果子進程中如果沒有調(diào)用_exit(0),則父進程不可能被執(zhí)行,在子進程調(diào)用exec(),exit()之后父進程才可能被調(diào)用.

  所以加上_exit(0),使子進程退出,父進程執(zhí)行.

  這樣 else 后的語句就會被父進程執(zhí)行,又因在子進程調(diào)用 exec 或 exit 之前與父進程數(shù)據(jù)是共享的,

  所以子進程退出后把父進程的數(shù)據(jù)段 count 改成1了,子進程退出后,父進程又執(zhí)行,最終就將count 變成了 2.

  五)寫拷貝技術(shù)

  寫拷貝或叫做寫時拷貝,就是子進程在創(chuàng)建后共享父進程的虛存內(nèi)存空間,只是在兩個進程中某一個進程需要向虛擬內(nèi)存寫入數(shù)據(jù)時才拷貝相應部分的虛擬內(nèi)存.

  寫拷貝的目的是通過消除不必要的復制來提高效率,當運行一個fork進程時,兩個進程將盡可能長地共享相同的物理內(nèi)存,也就是說內(nèi)核只復制頁表入口地址和標記所有寫拷貝的頁面.

  當有一個進程修改內(nèi)存時,將會引起缺頁,這時內(nèi)核將分配一個新的物理存儲頁,并在它被修改之前復制該頁.

  這樣對像init,xinetd,sshd這樣的進程將非常有用,因為他們的工作也只是調(diào)用fork和exec.

  六)clone

  .clone函數(shù)是Linux所特有的,可以用于創(chuàng)建進程和線程,所有可移植代碼從來不使用clone系統(tǒng)調(diào)用.

  .clone是一個復雜的系統(tǒng)調(diào)用,它給予應用程序很大的權(quán)限,可以控制父進程共享哪些子進程,它可以將一個線程當作一個特定進程,與其父進程共享用戶空間.

  七)最后的總結(jié):

  1)fork()系統(tǒng)調(diào)用是創(chuàng)建一個新進程的首選方式,fork的返回值要么是0,要么是非0,父進程與子進程的根本區(qū)別在于fork函數(shù)的返回值.

  2)vfork()系統(tǒng)調(diào)用除了能保證用戶空間內(nèi)存不會被復制之外,它與fork幾乎是完全相同的.vfork存在的問題是它要求子進程立即調(diào)用exec,而不用修改任何內(nèi)存,這在真正實現(xiàn)的時候要困難的多,尤其是考慮到exec調(diào)用有可能失敗.

  3)vfork()的出現(xiàn)是為了解決當初fork()浪費用戶空間內(nèi)存的問題,因為在fork()后,很有可能去執(zhí)行exec(),vfork()的思想就是取消這種復制.

  4)現(xiàn)在的所有unix變量都使用一種寫拷貝的技術(shù)(copy on write),它使得一個普通的fork調(diào)用非常類似于vfork.因此vfork變得沒有必要.

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多