關於多進程創建,此處只講解一個函數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都不同.