c++並發編程之進程創建(給那些想知道細節的人)


      關於多進程創建,此處只講解一個函數fork().

1.進程創建

先上代碼:

 1  #include"iostream"                                                                                                                                        
 2  #include<unistd.h> //unix標准文件
 3    int main()
 4    { 
 5        using namespace std;
 6        pid_t pid;
 7       cout<<"parent have!"<<endl;
 8            pid = fork();//執行fork的時候到底發生了什么?
 9        if(pid == -1)//錯誤創建
10        {
11            perror("fork error");
12              _exit(1);
13        }
14        else if(pid == 0)//子進程
15        {
16 
17            cout<<"i am child,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
18        }
19        else//父進程
20        {
21           // sleep(1);
22            cout<<"i am parent,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
23        }
24        cout<<"both have!"<<endl;
25        return 0;
26    }

運行結果:

 

 程序及結果分析:

   程序分析:

 pid = fork();//執行fork的時候到底發生了什么? 

        這行代碼到底發生了什么?我們需要清楚:在這行代碼執行之前,如果不考慮系統調用這個層次的進程,那么就只有一個進程,就是main函數所在的進程.,程序的邏輯是順序邏輯.那么這行代碼執行后,將會發生什么?

       "main"進程將會創建一個子進程,確切的講是復制一個子進程,也就是fork創建子進程,是基於當前進程,復制了其父進程的用戶空間(也就是你所看見的上面的代碼全部都會被復制).也就是,此刻,存在了兩個並行的進程(這個很重要.並不是我們認為的父進程執行完畢了再執行子進程).更確切的講,下一個時刻,父進程要執行上述代碼中的8行以后的代碼,子進程也要執行8行以后的代碼,究竟誰先執行,看誰搶到了CPU.

        上述程序中,根據fork的返回值,來確定即將要執行什么.從形式上看,明顯感覺到fork()的返回值並不是一個值,而是>0的值和等於0的值?難道一個函數可以返回兩個返回值?也顯然不是,是因為父進程創建了子進程,子進程既然復制了父進程的用戶空間,自然來看,有兩個pid.執行fork()函數后,父進程的pid為子進程的ID端口號,子進程的pid是0.所以才會根據pid的差異執行不同的動作.

 結果分析:

       從結果可以看到,"parent have!"僅僅打印了一次,而"both have!"執行了兩次.這是因為,fork創建的子進程不會執行在此之前的程序,只會執行fork()之后的,而"both have!"並沒有限定其為子進程還是父進程需要執行的.

       從結果中可以看出:父進程的部分先得到執行,表明父進程可能先得到返回值,先搶占到cpu(注意這里只是說可能).而且,父進程和子進程的內容均得到了顯示(如果是我們單進程的程序,if 和else if 不可能同時執行).

2.創建多個子進程

 1  #include"iostream"                                                                                                                                        
 2  #include<unistd.h>
 3    int main()
 4    { 
 5        using namespace std;
 6        pid_t pid;
 7       cout<<"parent have!"<<endl;
 8       for(int i = 0;i < 5;i++)
 9       {
10             pid = fork();//執行fork的時候到底發生了什么?
11             if(pid == 0)
12             {
13                // cout<<"the ID of son "<<i+1<<":"<<getpid()<<endl;
14                  break;//這個很重要,思考為什么
15             }
16       }
17          
18        if(pid == -1)//錯誤創建
19        {
20            perror("fork error");
21              _exit(1);
22        }
23        else if(pid == 0)//子進程
24        {
25            //sleep(1);
26            cout<<"i am child,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
27        }
28        else//父進程
29        {
30            sleep(1);
31            cout<<"i am parent,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
32        }
33        cout<<"both have!"<<endl;
34        return 0;
35    }

程序運行結果:

 程序及結果分析:

 程序分析:

假如我們現在想一個父進程創建多個子進程,比如創建5個子進程.我們應該怎么做?

     for(int i = 0;i < 5;i++)
      {
            pid = fork();//執行fork的時候到底發生了什么?
            if(pid == 0)
            {
               // cout<<"the ID of son "<<i+1<<":"<<getpid()<<endl;
                 break;//結束該子進程
            }
      }

注意:為什么要在for循環里加了條件判斷和break,不加會發生什么?

        我們需要注意的是:我們的父進程創建了子進程之后,子進程和父進程擁有完全一樣的用戶代碼,也就意味着,如果我們不加if判斷,那么我們的子進程也將成為新的父進程,創建新的子進程,也就是,每執行一次循環,之前創建的子進程都會成為子進程,也就會會造成指數級增長,最終進程會變成2^5-1個,共31個.當加了break之后呢?當父進程創建完子進程之后,我們說過子進程返回的pid為0,此時,會跳出循環體,阻止了fork()的執行,也就阻止了子進程創建新的子進程(但並不影響子進程執行下面的內容,只是不執行fork而已).而父進程由於得到的是子進程的ID,從而可以繼續執行循環體.而新創建的子進程自然也不能執行循環體,這樣下來,一個父進程創建了5個等級一樣的子進程.

父子進程共享

共享遵循的原則:讀時共享寫時復制原則.

       針對前文中:父進程創建子進程的例子,我們會產生這樣的疑問?子進程是完完全全copy父進程的內容嗎?  至少現在的Linux系統不是.如果某個變量在子進程是被讀的,而不是寫的,那么這個變量物理地址空間就是被共享的(注意並不是邏輯空間).如果在子進程中要被修改,那么就會產生一般性的復制行為.

       我們還是需要清楚:父子進程的用戶空間基本是完全一樣的,但是關於PCB這一塊的內核空間並不相同,畢竟每個進程的ID都不同.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM