操作系統(Linux)---fork()函數解析與進程

          我在一個二本類院校,對於linux是不在課程範圍的,屬於個人愛好吧。自己學習了一些,操作系統上學期開了,老師很水,就期末最後兩天學了整本書。

上學期去圖書館借了一本《linux內核設計與實現》當時看的是第二版,但是沒有看完,其實收穫還是挺多的。

         說回正題,前兩天有個好朋友給我講他們操作系統還有實驗課,當時我就懵了,確實差距大。於是讓他把實驗課題發給我,我想看看。很多隻有做過實驗,才能深刻體會。


編制一段程序,使用系統調用fork( )創建兩個子進程,這樣在此程序運行時,在系統中就有一個父進程和兩個進程在活動。每一個進程在屏幕上顯示一個字符,其中父進程顯示字符A進程分別顯示字符 B和字符C。試觀察、記錄並分析屏幕上進程調度的情況。

設計思路:

進程的出生:

隨着一句fork,一個新進程呱呱落地,但它這時只是老進程的一個克隆

調用fork創建的子進程,將共享父進程的代碼空間,複製父進程數據空間,此時子進程會獲得父進程的所有變量的一份拷貝。

系統調用fork(),在fork函數調用完,如果創建成功就會返回兩個進程,一個是father進程和一個新的進程son1(fork的一次調用兩次返回)。

fork()返回值意義如下:

=0:在子進程中,表示當前進程是子進程。

>0:在父進程中,返回值爲子進程的id值(唯一標識號)。

-1:創建失敗。

再在返回的father進程裏面調用fork()創建一個新的進程son2.

然後分別在進程打印A,B,C,然後實驗一下。


看看運行結果:


What happend ?
這時我們就有必要了解一下進程的死亡。

死亡:
人有生老病死,進程也一樣:
自然死亡,即運行到main函數的最後一個},從容地離我們而去。
自殺(自殺有2種方式)
調用exit函數
在main函數內使用return
無論哪一種方式,它都可以留下遺書,放在返回值裏保留下來。
被謀殺,被其它進程通過另外一些方式結束他的生命。
三個進程都是在return後死亡,而他們的死亡順序是又操作系統控制。
我們再來來看看實驗結果:

發現father的pid是10982,son1的father的pid是10982,然後到了son2這裏就會發現father的id是1965。
這又發生了什麼?看看下面兩個特別的進程。

孤兒進程

     父進程在子進程結束之前死亡(return或者exit)

     在一定時間內,當系統發現孤兒進程時,init進程就收養孤兒進程,成爲它的father,child進程exit後資源回收都又init進程完成。

僵死進程

     子進程在父進程之前結束了,但是父進程沒有用wait或waitpid回收子進程


三個進程都是在return後死亡,而他們的死亡順序是又操作系統控制。所以父進程在son2結束 死亡。son2成了孤兒進程,被init進程回收。在大多數linux上面init進程的pid爲1,但是據說ubuntu爲了減輕pid爲1進程的壓力,將其內核修改過。我init進程的pid爲1965。

wait掛起調用它的進程,直到它的任一子進程退出或者收到一個不能忽略的信號爲止,如果在父進程執行wait調用前就已經有子進程退出,則立刻返回。函數成功調用後返回結束運行子進程的ID,否則返回-1。進程死掉以後,會留下一具殭屍(一些數據),wait充當了殮屍工,把僵屍推去火化,使其最終歸於無形。

因此我們修改下代碼,在父進程裏面調用wait()函數收回子進程資源避免僵死進程。



看看運行結果:



好了,終於結束了。

如果父進程忘記wait(),就會存在僵死進程。過多的僵死進程就會佔用系統資源,影響性能,爲了預防這種情況,便有兩次調用fork()。

Why調用兩次fork()

爲了避免child成爲僵死進程,可以人爲的殺死父進程,可以讓其成爲孤兒進程被init接管,但是如果父進程是服務器進程或者一些重要的進程不能立刻被殺死的話,僵死進程就一直存在,影響系統性能,所以爲了避免這種情況,要調用兩次fork(),讓孫子線程成爲工作線程,如果它成爲僵死線程的話,可以殺死它的父進程。

在實驗中,我還設置了一個數count,是爲了實驗fork創建的子進程,每個進程是父進程的一份複製,但是又有自己的空間,每個進程都有自己的變量。

son1創建時,會複製變量count=0,但是count是存在son1的數據段中,所以son1的count和father以及son2擁有不同的地址,每個count是獨立的,不是共用的。


(但是這個程序存在一個問題,就是有兩個子進程但是隻wait( )了一次·,如果有一個子進程返回,wait調用就結束。父進程可能不會回收第二個返回的子進程,所以還是存在僵死進程的問題。可以使用waitpid()函數來指定回收子進程)

#include <stdlib.h>
#include <stdio.h>
int main()
{
int count=1;
int pid1;
printf("Recently pid is %d \n",getpid());
pid1 = fork();
if(pid1<0)
{
printf("error in fork!");
}
else if(pid1==0)
{
count++;
printf("My father'pid is %d I'm son1,my pid is %d   B\n",getppid(),getpid());
}
else
{
int pid2=fork();
if(pid2<0)
{
printf("error in fork!");
}
if(pid2==0)
{
count++;
printf("My father'pid is %d I'm son2,my pid is %d  C \n",getppid(),getpid());
}
else
{
waitpid(pid1,NULL);
waitpid(pid2,NULL);
count++;
printf("I'm father,my pid is %d,   A \n",getpid());
}
}
printf("%d \n",count);
return 0;
}


(但是如果子進程多了的話應該怎樣回收子進程,避免僵死進程呢?寫循環嗎,循環可能不是一個好的解決辦法。很多人說可以用信號實現,有時間研究一下吧,也希望知道的同學給我留言,在此感激不盡,阿里嘎多)

最後,fork與vfork的區別
1. fork要拷貝父進程的數據段;而vfork則不需要完全拷貝父進程的數據段,在子進程沒有調用exec和exit之前,子進程與父進程共享數據段


2. fork不對父子進程的執行次序進行任何限制;而在vfork調用中,子進程先運行,父進程掛起,直到子進程調用了exec或exit之後,父子進程的執行次序纔不再有限制

                        

                                                                                



發佈了55 篇原創文章 · 獲贊 22 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章