【Linux】fork函數的理解


         先給大家簡單介紹下 fork 函數

1、函數原型:

       

  #include <unistd.h>

pid_t fork(void);

2、函數功能:

      創建一個子進程

3、返回值:

      若函數執行失敗,返回 - 1;若執行成功:

      (1)、父進程返回子進程的ID(非負)

      (2)、子進程返回 0

 

      注:  (1)、pid_t 類型表示進程 id ,但為了表示 -1 ,它是有符號整型(0  不是有效進程,init 最小,為 -1)

               (2)、並不是 fork 函數有兩個返回值,而是 fork 后,fork 函數變成兩個, 父子進程各自有一個返回值。

 4、父子進程的深度理解:

     (1)、子進程是父進程的副本,它將獲得父進程的數據空間、棧、堆等資源的副本。父子進程共享代碼段,但是分別擁有自己的數據段和堆棧段。

               由於在創建進程時,會消耗大量的系統資源,所以為了可以避免拷貝大量根本就不會被使用的數據(地址空間里常常包含數十兆的數據)。由於 Unix/Linxu 強調進程快速執行的能力,這里就使用了寫 實拷貝技術 ,優化系統資源的使用。


              此處先簡要介紹下COW(Copy-on-write)機制,大致原理如下:


       在復制一個對象的時候並不是真正的把原先的對象復制到內存的另外一個位置上,而是在新對象的內存映射表中設置一個指針,指向源對象的位置,並把那塊內存的
Copy-On-Write位設置為1.這樣,在對新的對象執行讀操作的時候,內存數據不發生任何變動,直接執行讀操作;而在對新的對象執行寫操作時,將真正的對象復制到新的內存地址中,並修改新對象的內存映射表指向這個新的位置,並在新的內存位置上執行寫操作。(讀時共享寫實拷貝)。

     (2)、一般來說 fork 后的父進程先執行還是子進程先執行是不確定的。(進程運行的次序是不確定的,取決於內核對的調度算法)。可以使用getpid()和getppid()兩個函數分別獲取當前進程的進程 id 和父進程的 id。


     (3)、在Linux中存在緩沖問題。當寫 printf 函,但是沒有換行的話,它是不會輸出的,而是現將要輸出的內容放入緩沖區中,當碰到換行時,將緩沖去的內容再一起輸出。所以 fork 后子進程會復制父進程的緩沖區。因此,也將待輸出內容復制到自己與父進程獨立的緩沖區中。因此,當子進程執行遇到換行,就將緩沖區的內容全都輸出來。


5、示例代碼1:

        

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
pid = fork();

if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}



   這里為什么會出現運行上面代碼,輸出會先輸出  父進程,然后輸出子進程,然后光標就在下面一行閃呢?而不是光標跟在   arrayli@ubuntu:~/sysCode/day01/fork/review$
后面呢?


   主要是因為當前終端是一個進程,而程序 test_fork1 是一個當前終端下的子進程;然而該子進程有一個子進程,這三個進程之間相互爭奪 CPU 的使用權,所以會出現這種情況。父進程獲取到 CPU 的使用權后,當前終端獲取到了 CPU 的使用權,但是 終端沒有運行完畢,時間片用完了。此時 子進程獲取到了 CPU 的使用權,打印出了子進程的信息。然后當前終端獲取到了 CPU 的使用權,終端繼續向下運行,所以光標會出現這種情況。只是,輸出格式有問題,其余還是和平常一樣。大家可以看着下圖再加以理解下哦哦。


  

                                                                                        shell 終端進程

6、示例代碼2:

    

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
pid = fork();

if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
sleep(1); // 和程序1 不同的是這里添加上了 系統休眠
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}

     

          本程序為什么輸出格式正常呢?  

 因為在這里 當父進程獲取到 CPU 的使用權后,我給它休眠了 1s 。此時 子進程就會獲取 CPU 的使用權,執行程序。然后回到終端。


7、示例代碼3:

  

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
int i;

for( i = 0; i < 2; i++ )
{
printf("-");
pid = fork();

}
return 0;
}

       輸出:--------

       8個中划線


分析:

         在整個執行周期內,一共會產生4個進程,由於是遍歷,所以主進程會產生兩個子進程。假設 i=0 時,fork 出的子進程記為: first ; i =1 時,fork 出的子進程記為 : second

first 進程在遍歷的時候會再產生一個自己的子進程,記錄為: third;而 second 進程, i 的值已經為 1 ,不會再繼續執行下去,其他進程同理。


         主進程會輸出2個中划線(總計:2個)


         first 進程會輸出1個自己的中划線,此時考慮到從主進程緩沖區里面拷貝來的 1個中划線。即,printf 的緩沖區里面 還有一個 “-”的1個中划線(總計:2個)


         second 進程沒有自己的中划線輸出,考慮到從主進程緩沖區里拷貝過來的2個中划線(總計:2個)


         third  進程沒有自己的中划線輸出,考慮到會從 first 進程緩沖區里拷貝過來的2個中划線(總計:2個)


所以,一共會輸出8個中划線。


8、示例代碼4:

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
int i;

for( i = 0; i < 2; i++ )
{
pid = fork();
printf("-");
}
return 0;
}

       輸出:--------

       8個中划線


解析:

         雖然代碼通代碼4一樣,也是輸出8個中划線,但是內部的運行機制卻相差很遠,具體原因,見如下分析。

        

         主進程會輸出2個中划線(總計:2個)


         first 進程會輸出2個自己的中划線,無主進程緩沖區拷貝輸出(總計:2個)


         second 進程會輸出1個自己的中划線,考慮到會從主進程緩沖區里拷貝過來的1個中划線(總計:2個)


         third  進程會輸出1個自己的中划線,考慮到會從first 進程的緩沖區里拷貝過來的1個中划線(總計:2個)


9、示例代碼5:

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
int i;

for( i = 0; i < 2; i++ )
{

printf("-\n");
pid = fork();
}
return 0;
}

     輸出:  -

                  -

                  -

             3個中划線


解析:

          主進程會輸出2個中划線(總計:2個)


          first 進程會輸出1個自己的中划線,無主進程緩沖區拷貝輸出(總計:1個)


          second 進程沒有自己的中划線輸出,無主進程緩沖區拷貝輸出(總計:0個)


          third 進程沒有自己的中畫西安輸出,無 first 進程緩沖區拷貝輸出(總計:0個)


9、示例代碼6:

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
int i;

for( i = 0; i < 2; i++ )
{
pid = fork();

printf("-\n");
}
return 0;
}

     輸出: -

                 -

                 -

                 -

                 -

                 -

       6個中划線


解析:

         主進程會輸出2個中划線(總計:2個)


          first 進程會輸出2個自己的中划線,無主進程緩沖區拷貝輸出(總計:2個)


          second 進程會輸出1個自己的中划線,無主進程緩沖區拷貝輸出(總計:1個)


          third 會輸出1個自己的中划線輸出,無 first 進程緩沖區拷貝輸出(總計:1個)


10、示例代碼7:

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
pid_t pid;
pid = fork();

if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
sleep(1);
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}

輸出結果:


小結:

         在這里和大家分享了 fork 函數的理解,有不足之處的話,歡迎大家找我交流技術哦哦。

关注微信公众号

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com