Linux進程學習二

來源:互聯網
上載者:User

fork()和vfork()的學習

通過上一部分的學習,我們瞭解了進程的概念以及在Linux中進程的實現,此部分我們將具體學習如何在Linux中建立一個進程。

一前言:

通過原理知識的學習,我們知道每個進程由進程ID號標識。進程被建立時系統會為其分配一個唯一的進程ID號。當一個進程向其父進程(建立該進程的進程)傳遞其終止訊息時,意味這個進程的整個生命週期結束。此時,該進程佔用的所用資源套件括進程ID被全部釋放。

那麼在Linux中如何建立一個進程呢?

建立進程有兩種方式:一是由作業系統建立,二是由父進程建立的進程(通常為子進程)。

系統調用fork是建立一個新進程的唯一方式。vfork也可建立進程,但它實際上還是調用了fork函數。

Tiger-John 說明:

1.由作業系統建立的進程它們之間是平等的不存在資源繼承關係。

2.由父進程建立的進程通常為子進程它們之間有繼承關係。

3.在系統啟動時,OS會建立一些進程,它們承擔著管理和分配系統資源的任務,即系統進程。

如0號idle進程,它是從無到有誕生的第一個線程,主要用於節能;關於idle進程,系統最初引導0號進程,對應的PCB為init_task(),要說明下它是0號進程PCB的頭,並不是1號init進程,在引導結束後即成為cpu 上的idle進程。在每個cpu上都有一個idle進程,這些進程登記在init_tasks[]數組中。idle進程不進入就緒隊列,系統穩定後,僅當就緒隊列為空白的時候idle進程才會被調度到,在沒有其它進程啟動並執行情況下,它大量時間佔用cpu。

1號進程(init進程)它是一個由核心啟動的使用者級進程,它是所有使用者進程的父進程。實際上,Linux2.6在初始化階段首先把它建立為一個核心線程kernel_init:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
參數CLONE_FS | CLONE_FILES | CLONE_SIGHAND表示0號線程和1號線程分別共用檔案系統(CLONE_FS)、開啟的檔案(CLONE_FILES)和訊號處理常式(CLONE_SIGHAND)。當發送器選擇到kernel_init核心線程時,kernel_init就開始執行核心的一些初始化函數將系統初始化。
那麼,kernel_init()核心線程是怎樣變為使用者進程的呢?

實際上,kernel_init()核心功能中調用了execve()系統調用,該系統調用裝入使用者態下的可執行程式init(/sbin/init)。注意,核心功能kernel_init()和使用者態下的可執行檔init是不同的代碼,處於不同的位置,也運行在不同的狀態,因此,init是核心線程啟動起來的一個普通的進程,這也是使用者態下的第一個進程。init進程從不終止,因為它建立和監控作業系統外層所有進程的活動。

二fork()函數和vfork()函數的學習

1.fork()函數

調用fork函數後,當前進程分裂為兩個進程,一個是原來的父進程,另一個是剛建立的子進程。父進程調用fork後傳回值是子進程的ID,子進程中傳回值是0,若進程建立失敗,只返回-1。失敗原因一般是父進程擁有的子進程個數超過了規定限制(返回EAGAIN)或者記憶體不足(返回ENOMEM)。我們可以依據傳回值判斷進程,一般情況下調用fork函數後父子進程誰先執行是未定的,取決於核心所使用的調度演算法。一般情況下os讓所有進程享有同等執行權,除非某些進程優先順序高。若有一個孤兒進程,即父進程先於子進程死去,子進程將會由init進程收養。

函數執行個體:通過fork()函數建立一個進程:

 1 #include<sys/types.h>
  2 #include<unistd.h>
  3 #include<stdio.h>
  4
  5 main()
  6 {
  7         pid_t pid;
  8         printf("PID before fork() :%d/n",(int)getpid());
  9
 10         pid = fork();
 11         if(pid < 0){
 12                 printf("error in fork!/n");
 13         }
 14         else if(0 == pid){
 15                 printf("I'm the child process, CurpPID is %d,ParentPid is %d    /n",pid,(int)getppid());
 16                 }
 17         else{
 18                 printf("I'm the parent process,child PID is %d,ParentPID is     %d/n",pid,(int)getpid());
 19         }
 20

程式經過調試後結果如下:

think@ubuntu:~/work/process_thread/fork$ ./fork
PID before fork() :4566
I'm the parent process,child PID is 4567,ParentPID is 4566
I'm the child process, CurpPID is 0,ParentPid is 4566
從程式執行結果可以看出:調後fork()函數後返回兩個值,子進程傳回值為0,而父進程的傳回值為建立的子進程的進程ID。

Tiger-John說明:

1>Linux進程一般包括程式碼片段,資料區段和堆棧段。程式碼片段存放程式的可執行代碼;資料區段存放程式的全域變數、常量、靜態變數;堆存放動態分配的記憶體變數,棧用於函數調用,存放函數參數、函數內部定義的局部變數。

2>有人說調用fork函數後,fork()函數返回了兩個值,這是一中錯誤的說法。其實,當系統調用fork()函數後fork()會將調用進程的所有內容原封不動的拷貝到新產生的子進程中去,當前進程分裂成了兩個進程分別在執行,互不干擾。

3>.看一個函數執行個體來仔細體會一下系統調用fork()函數後是如何執行的。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4
  5 int main()
  6 {
  7         pid_t pid;
  8         int count = 0;
  9         pid = fork();
 10
 11         printf("This is first time,pid = %d/n",pid);
 12         printf("This is the second time,pid = %d/n",pid);
 13         count++;
 14         printf("count = %d/n",count);
 15
 16         if(pid > 0){
 17                 printf("This is the parent process,the child has the pid :%d    /n",pid);      
 18         }
 19         else if(!pid){
 20                 printf("This is the child process./n");
 21         } 

 22         else{
 23                 printf("fork failed./n");
 24         }
 25
 26         printf("This is third,pid = %d/n",pid);
 27         printf("This is four time,pid = %d/n",pid);
 28         return 0;
 29 }
程式經過調試後結果

think@ubuntu:~/work/process_thread/fork1$ ./fork
This is first time,pid = 4614
This is the second time,pid = 4614
count = 1
This is the parent process,the child has the pid :4614
This is third,pid = 4614
This is four time,pid = 4614
This is first time,pid = 0
This is the second time,pid = 0
count = 1
This is the child process.
This is third,pid = 0
This is four time,pid = 0
think@ubuntu:~/work/process_thread/fork1$

Tiger-John說明:

從上面的程式的執行結果我們看到一個奇怪的現象:為什麼printf的語句執行兩次,
而那句“count++;”的語句卻只執行了一次?

系統在調用fork()後分裂成了兩個函數分別執行,互不干擾。

 

2.Vfork和fork的區別:

1>vfork也可建立進程,但它實際上還是調用了fork函數。

2>在說明他們的區別之前我們先看兩個程式和執行結果

函數執行個體1:用fork()函數建立進程

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int globVar = 5;
  6
  7 int main(void)
  8 {
  9         pid_t pid;
 10         int var = 1;
 11         int i;
 12         printf("fork is different with vfork /n");
 13
 14         pid = fork();
 15         if(!pid){
 16                 i=3;
 17                 while(i-- > 0){
 18                         printf("Child process is running/n");
 19                         globVar++;
 20                         var++;
 21                         sleep(1);
 22                               }
3                 printf("Child's globVar = %d,var = %d/n",globVar,var);
 24                 }
 25         else if(pid){
 26                 i=5;
 27                 while(i-- > 0){
 28                         printf("Parent process is running/n");
 29                         globVar++;
 30                         var++;
 31                         sleep(1);
 32                                 }
 33                 printf("Parent's globVar = %d,var %d/n",globVar,var);
 34                 exit(0);
 35                 }
 36         else{
 37                 perror("Process creation failed/n");
 38                 exit(-1);
 39             }
 40 }
程式經過調試後;

think@ubuntu:~/work/process_thread/fork3$ ./fork
fork is different with vfork
Parent process is running
Child process is running
Child process is running
Parent process is running
Child process is running
Parent process is running
Child's globVar = 8,var = 4
Parent process is running
Parent process is running
Parent's globVar = 10,var = 6

函數執行個體2:用vfork()函數建立一個進程
  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 int globVar = 5;
  6 int main(void)
  7 {
  8         pid_t pid;
  9         int var = 1;
 10         int i;
 11
 12         printf("fork is different with vfork!/n");
 13
 14         pid = vfork();
 15         if(!pid){
 16                 i=3;
 17                 while(i-- > 0)
 18                 {
 19                         printf("Child process is running/n");
 20                         globVar++;
 21                         var++;
 22                         sleep(1);
23                 }
 24                 printf("Child's globVar = %d,var =%d/n",globVar,var);
 25         }
 26         else if(pid){
 27                         i = 5;
 28                         while(i-- > 0)
 29                         {
 30                                 printf("Parent process is running/n");
 31                                 globVar++;
 32                                 var++;
 33                                 sleep(1);
 34                         }
 35                 printf("Parent's globVar = %d,var %d/n",globVar,var);
 36                 exit(0);
 37                 }
 38         else {
 39                 perror("Process creation failed/n");
 40                 exit(0);
 41         }
 42
 43 }
 程式經過調試後結果為:

think@ubuntu:~/work/process_thread/fork3$ ./vfork
fork is different with vfork!
Child process is running
Child process is running
Child process is running
Child's globVar = 8,var = 4
Parent process is running
Parent process is running
Parent process is running
Parent process is running
Parent process is running
Parent's globVar = 13,var = 5

 

 

Tiger-John說明:
我們通過以上兩個函數的執行可以看到一些區別:

 

1.使用fork建立子進程時,子進程繼承了父進程的全域變數和局部變數。在子進程中,最後全域變數globVar和局部變數var的值均遞增3,分別為8和4.不管是全域變數還是局部變數,子進程與父進程對它們的修改互不影響。

2.父進程中兩者分別遞增5.最後結果為10和6

通過以上程式的運行結果可以證明fork的子進程有自己獨立的地址空間。

3.子進程和父進程的執行順序是很隨意的,沒有固定的順序。父子進程的輸出是混雜在一起的。

--------------------------------------------

1.用vfork()函數建立子進程後,父進程中globVar和var最後均遞增了8.這是因為vfork的子進程共用父進程的地址空間,子進程修改變數對父進程是可見的。
2.使用vfork()函數子進程後列印的結果是子進程在前,父進程在後,說明vfork()保證子進程先執行,在子進程調用exit獲exec之前父進程處於阻塞等待狀態。

3>那麼現在來看看fork()和vfork()函數之間的區別:

(1)fork():使用fork()建立一個子進程時,子進程只是完全複製父進程的資源。這樣得到的子進程獨立於父進程具有良好的並發性。
 vfork(): 使用 vfor建立一個子進程時,作業系統並不將父進程的地址空間完全複製到子進程。而是子進程共用父進程的地址空間,即子進程完全運行在父進程的地址空間上。子進程對該地址空間中任何資料的修改同樣為父進程所見。
(2)fork():父子進程執行順序不定;
vfork():保證子進程先運行,在調用exec或exit之前與父進程共用資料,在它調用exec或exit之後父進程才可能被調度運行。
(3)vfork保證子進程先運行,在它調用exec或exit後父進程才可能被調度運行。如果在調用這兩個函數之前子進程依賴於父進程的進一步動作,則會導致死結。


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.