聊聊Linux下fork與執行緒池

2021-10-07 04:15:08 字數 3804 閱讀 3074

這個知識點也是老生常談了,咱們今天就把他講清楚弄明白。

執行緒池是指在乙個多執行緒程式中建立乙個執行緒集合,在執行新的任務的時候不是新建乙個執行緒,而是使用執行緒池中已建立好的執行緒,一旦任務執行完畢,執行緒就會休眠等待新的任務分配下來。

這麼看來,執行緒池的優點就很明顯了,在頻繁的執行緒切換環境中,執行緒池可以省去多次執行緒的建立與釋放所需要的時間。

這個我還是稍微了解一點的。建立乙個執行緒非常簡單。

1.向核心空間申請一段記憶體用來存放新執行緒的pcb;

2.pcb中預留指標指向核心棧;

3.pcb中存放待執行執行緒函式與函式引數;

4.加入程序排程佇列或直接ret執行(需要先將函式指標放在棧頂,然後ret會將其載入到eip暫存器上,完成立刻執行執行緒操作)

簡言之,fork就是建立了乙個父程序的拷貝。

使用fork函式得到的子程序從父程序的繼承了整個程序的位址空間,包括:程序上下文、程序堆疊、記憶體資訊、開啟的檔案描述符、訊號控制設定、程序優先順序、程序組號、當前工作目錄、根目錄、資源限制、控制終端等。

子程序與父程序的區別在於:

1、父程序設定的鎖,子程序不繼承(因為如果是排它鎖,被繼承的話,矛盾了)

2、各自的程序id和父程序id不同

記住,fork是一次呼叫兩次返回值,因為會在兩個位址空間中都執行返回。

如果成功建立乙個子程序,對於父程序來說返回子程序id

如果成功建立乙個子程序,對於子程序來說返回值為0

如果為-1表示建立失敗。

fork系統呼叫複製產生的子程序與父程序(呼叫程序)基本一樣:**段+資料段+堆疊段+pcb,當前的執行環境基本一樣,所以子程序在fork之後開始向下執行,而不會從頭開始執行。

fork系統呼叫之後,父子程序將交替執行,執行順序不定。

如果父程序先退出,子程序還沒退出那麼子程序的父程序將變為init程序(託孤給了init程序)。(注:任何乙個程序都必須有父程序)

如果子程序先退出,父程序還沒退出,那麼子程序必須等到父程序捕獲到了子程序的退出狀態才真正結束,否則這個時候子程序就成為僵程序(殭屍程序:只保留一些退出資訊供父程序查詢)。殭屍程序可以在程序列表中檢視。

首先任何程序都有父程序(init程序是所有程序的祖宗,它沒有爸爸),當父程序拋棄了自己fork出來的子程序而執行完成後,子程序就被託管給init程序。

孤兒程序其實沒啥危害。

殭屍程序就不一樣了。本來吧,父程序在fork乙個子程序的時候,接下來需要wait操作等待子程序結束,然後子程序才會全部釋放資源(子程序執行完成後並沒有全部釋放資源,還剩下pid這種東西,退出狀態這種東西,必須要父程序wait索取才會釋放)。這下好了,父程序直接沒wait就退出了,於是殭屍進城產生後,這個程序id就再也不能被後續使用了。

很簡單,第乙個是signal(可能有的朋友不了解什麼是signal)。linux的訊號機制就提供了其中一種專門用來處理子程序退出問題的,叫sigchld,寫法也非常簡單:

#include

#include

#include

#include

#include

static

void

sig_child

(int signo)

;int

main()

else

if(pid ==0)

printf

("i am father process.i will sleep two seconds\n");

//等待子程序先退出

sleep(2

);//輸出程序資訊

system

("ps -o pid,ppid,state,tty,command");

printf

("father process is exiting.\n");

return0;

}static

void

sig_child

(int signo)

是不是非常簡單?

再給出第二種解決方法,讓父子程序擺脫父子關係,於是子程序的父親變成了init程序,這個程序肯定能幫咱們處理殭屍程序的問題。這確實是一種非常棒的思路,咱們聊聊具體的實現方法:父程序fork乙個子程序,然後子程序裡面再fork乙個孫子程序。這個時候突然子程序建立完成後就退出,那麼,孫子程序就沒有爸爸了(祖安程式設計師)。不能說孫子程序沒有爸爸了,而是說,孫子程序的爸爸變為init程序了。

一起看看實現:

#include

#include

#include

#include

intmain()

//第乙個子程序

else

if(pid ==0)

//第乙個子程序退出

else

if(pid >0)

//第二個子程序

//睡眠3s保證第乙個子程序退出,這樣第二個子程序的父親就是init程序裡

sleep(3

);printf

("i am the second child process.pid: %d\tppid:%d\n"

,getpid()

,getppid()

);exit(0

);}//父程序處理第乙個子程序退出if(

waitpid

(pid,

null,0

)!= pid)

exit(0

);return0;

}

寫時複製就很能理解。各位朋友看看,fork乙個程序可是真的浪費了好多資源啊,啥啥都得複製乙份,太浪費了吧。那能不能咱們搞點棒棒的機制,解決一下這個問題?

寫入時複製(copy-on-write)是乙個被使用在程式設計領域的最佳化策略。其基礎的觀念是,如果有多個呼叫者(callers)同時要求相同資源,他們會共同取得相同的指標指向相同的資源,直到某個呼叫者(caller)嘗試修改資源時,系統才會真正複製乙個副本(private copy)給該呼叫者,以避免被修改的資源被直接察覺到,這過程對其他的呼叫只都是通透的(transparently)。此作法主要的優點是如果呼叫者並沒有修改該資源,就不會有副本(private copy)被建立。

vfork就很省事情,它不會複製乙份新的分頁表,而是依舊指向父程序的分頁表,那麼你倆可不就是共享位址空間了嘛!那不就是你倆共享物理空間了嘛!那肯定不行啊,這樣搞的話豈不是要亂了?

於是vfork有了這樣乙個規則:由vfork創造出來的子程序還會導致父程序掛起,除非子程序exit或者execve才會喚起父程序。這樣一來,不就沒那麼多煩惱了。

vfork命好啊,一定會先於父程序執行。那麼當vfork程序執行完成後,能用return退出麼?

答案肯定是不能!這其實非常好理解。return會釋放掉棧裡面的區域性變數,exit不會。本來父子程序是共享記憶體位址的,對於棧裡面的東西,最好還是vfork剛放進去的就拿出來,換言之,自己玩自己放進去的東西。vfork如果return,那麼棧直接就被糟蹋了。

事出必有因。

咱們先了解一下exec呼叫。

系統呼叫exec是以新的程序去代替原來的程序,但程序的pid保持不變。因此,可以這樣認為,exec系統呼叫並沒有建立新的程序,只是替換了原來程序上下文的內容。原程序的**段,資料段,堆疊段被新的程序所代替。

咱們想想啊,exec一般是在fork程序中執行,本來就要將fork子程序把父程序替換掉,那咱們fork還把位址空間的東西費勁巴拉的拷貝乙份幹嘛呢?

一句話,vfork是給exec用的。

這個咱們就先不深入講解。

clone可以讓你有選擇性的繼承父程序的資源,你可以選擇想vfork一樣和父程序共享乙個虛存空間,從而使創造的是執行緒,你也可以不和父程序共享,你甚至可以選擇創造出來的程序和父程序不再是父子關係,而是兄弟關係。

多執行緒與fork

多執行緒程式裡盡量不使用fork 在多執行緒程式裡,在 自身以外的執行緒存在的狀態 下一使用fork的話,就可能引起各種各樣的問題.比較典型的例子就是,fork出來的子程序可能會死鎖.請不要,在不能把握問題的原委的情況下就在多執行緒程式裡fork子程序,能引起什麼問題呢?那看看例項吧.一執行下面的 ...

12 9 執行緒與fork

當乙個執行緒呼叫函式fork的時候,整個程序位址空間會被拷貝到子程序中,在8.3節中有提到copy on write.子程序是乙個與父程序完全不同的程序,但是如果父程序和子程序都沒有對記憶體內容進行修改,那麼該記憶體頁就可以在父程序與子程序之間進行共享。通過繼承父程序的整個位址空間,子程序也會繼承父...

linux下多執行緒中的fork介紹

目錄 回想一下 當乙個程式只有主線程的時候呼叫fork,此時fowww.cppcns.comrk會建立出的子程序也會只有一條執行緒 那要是把fork放入多執行緒的程式中呢?我們來試驗下 include include include void pthread fun void arg int mai...