Linux(1):fork函數

         ps:每一篇博客不過爲了記錄學習的過程,並反思總結,若有錯誤,還望指正算法


       函數原型:extern __pid_t fork (void) __THROWNL;編程

       該函數包括於頭文件unistd.h中。安全

       源文件裏凝視:數據結構

              /* Clone the calling process, creating an exact copy.Return -1 for errors, 0 to the new process,函數

   and the process ID of the new process to the old process.  */post

            

           fork()會產生一個子進程。其子進程會複製父進程的數據與堆棧空間。並繼承父進程的用戶代碼、組代碼、環境變量、已打開的文件代碼、工做文件夾和資源限制等。學習

Linux 使用copy-on-write(COW)技術,僅僅有當當中一進程試圖改動欲複製的空間時纔會作真正的複製動做,由於這些繼承的信息是複製而來,並非指一樣的內存空間,所以子進程對這些變量的改動和父進程並不會同步。優化

此外。子進程不會繼承父進程的文件鎖定和未處理的信號。(COW並不那麼完美 COW是一種很是重要的優化手段,核心是懶惰處理實體資源請求。在多個實體資源之間僅僅是共享資源,起初是並不真正實現資源拷貝,僅僅有當實體有需要對資源進行改動時才真正爲實體分配私有資源。但COW技術亦有它的優缺點。spa

操作系統

      1.COW技術科下降分配和複製大量資源時帶來的瞬間延時,但其實是將這樣的延時附加到了興許的操做之中。 

      2.COW技術科下降沒必要要的資源分配。比方fork進程時。並不是所有的頁面都需要複製,父進程的代碼段和僅僅讀數據段都不被贊成改動,因此無需複製。

 

       注意,Linux不保證子進程會比父進程先運行或晚運行。所以編敲代碼時要留意死鎖或競爭條件的發生。


       返回值:假設fork()調用成功則在父進程會返回新創建的子進程代碼(PID)。而在新創建的子進程中則返回0。

假設fork() 失敗則直接返回-1,失敗緣由存於errno中。失敗的緣由有三個:

      1) 系統內存不夠;

      2) 進程表滿(容量通常爲200~400)。

      3) 用戶的子進程太多(通常不超過25個)。

     錯誤代碼:EAGAIN 內存不足;ENOMEM 內存不足,沒法配置核心所需的數據結構空間。

        fork建立的新進程被稱爲子進程(child process

該函數被調用一次,但返回兩次。兩次返回的差異是子進程的返回值是0,而父進程的返回值則是新進程(子進程)的進程 id

將子進程id返回給父進程的理由是:因爲一個進程的子進程可以多於一個,沒有一個函數使一個進程可以得到其所有子進程的進程id。對子進程來講,之因此fork返回0給它,是因爲它隨時可以調用getpid()來獲取本身的pid。也可以調用getppid()來獲取父進程的id(進程id 0老是由交換進程使用。因此一個子進程的進程id不可能爲0 )

    舉個樣例:

   

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
	int pid;
	pid=fork();
	printf("Hello\n");
	if(pid==0)
		printf("I'm child process.\n");
	else
		printf("I'm parent process.\n");
	return 0;
}

    運行結果爲:

   

    fork以後,操做系統會複製一個與父進程全然一樣的子進程。雖然說是父子關係。但是在操做系統看來,他們更像兄弟關係。這2個進程共享代碼空間。因爲子進程是父進程的副本。因此它擁有父進程數據空間、棧和堆的副本。它們並無共享這些存儲空間。它們僅僅共享正文段。 但是數據空間是互相獨立的,子進程數據空間中的內容是父進程的完整拷貝,指令指針也全然一樣,子進程擁有父進程當前執行到的位置(兩進程的程序計數器pc值一樣,也就是說。子進程是從fork返回處開始執行的),但有一點不一樣,假設fork成功。子進程中fork的返回值是0。父進程中fork的返回值是子進程的進程號,假設fork不成功。父進程會返回錯誤。

   

#include<unistd.h>
#include<stdio.h>
int a=5;
int main()
{
   int b=6,pid;
   pid =fork();
   if(pid<0)
   {
      printf("Error\n");
   }
   else if(pid==0)
   {
      a++;
      b++;
      printf("Child process:%d\n",getpid());
      printf("%d %d %d\n",getpid(),a,b);
   }
   else
   {
      printf("Parent process:%d\n",getpid());
      printf("%d %d %d\n",getpid(),a,b);
   }
}

    運行結果:

   

    由此看出,子進程的值發生了改變。可以說明。它們並不是共享的。

    再來看下變量的地址:

   

#include<unistd.h>
#include<stdio.h>
int a=5;
int main()
{
   int b=6,pid;
   pid =fork();
   if(pid<0)
   {
      printf("Error\n");
   }
   else if(pid==0)
   {
      a++;
      b++;
      printf("Child process:%d\n",getpid());
      printf("%d %d %d %p %p\n",getpid(),a,b,&a,&b);
   }
   else
   {
      printf("Parent process:%d\n",getpid());
      printf("%d %d %d %p %p\n",getpid(),a,b,&a,&b);
   }
}

    運行結果:

   

    由結果可以看出,變量地址是同樣的!

!內容卻不同,因爲打印的變量的地址都是邏輯空間, 對於父子進程。它們的邏輯空間同樣,都是虛擬地址,但是物理空間仍是不一樣的。每個進程都有獨立的4G的虛擬地址空間,映射到不一樣的物理空間。比方我現在同一時候執行兩個進程,他們都有可能訪問各自的虛擬地址,但是映射以後的物理內存就不會是同一個地址。

因此在多進程編程中。不要寄但願於經過地址來推斷兩個變量是否一樣。

因此,兩個進程一直同一時候執行,而且步調一致,在fork以後,他們分別做不一樣的工做。也就是分岔了。這也是fork爲何叫fork的緣由至於那一個最早執行。可能與操做系統(調度算法)有關,而且這個問題在實際應用中並不重要。假設需要父子進程協同,可以經過原語的辦法解決。

     最後看下父子進程的關係。看到網上有人說:

     他們的關係是管理和被管理的關係,當父進程終止時。子進程也隨之而終止。但子進程終止。父進程並不必定終止。比方httpdserver執行時,咱們可以殺掉其 子進程,父進程並不會因爲子進程的終止而終止。在Linux進程管理中,當咱們發現佔用資源過多,或沒法控制的進程時。應該殺死它,以保護系統的穩定安全執行。

    咱們可以經過程序來驗證一下是否正確看看:

   

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
   int pid,v=1;
   pid=fork();
   if(pid==0)
   {
      printf("Child Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);
      sleep(1);
      printf("Child Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);
      exit(0);
   }
   else
   {
      printf("Parent Process:pid=%d,ppid=%d,v=%d\n",getpid(),getppid(),v++);  
}

     運行結果:

              

     可以看出父進程首先退出,退出前childPPID6707。退出後子進程的PPID變爲了1613.經過ps 命令查看到1613爲init進程,說明父進程退出後的子進程由 init進程領養。而該子進程是不會退出的

相關文章
相關標籤/搜索