Linux内核源代码分析——fork()原理多进程网络模型
?今晚和一位500強的leader喝喝小酒吃吃烤魚,生活樂無邊。這位兄弟伙才畢業(yè)2年,已經(jīng)做到管理層了,機遇和能力不可謂不好。喝酒之余,聊到Linux內核的兩個問題——fork()、exec()的原理。
??????? 兄弟伙:fork()的原理是什么呢?
??????? 我:其實一句話就概括了——copy on write。
????????兄弟伙:copy on wirte我懂,書上介紹的一抓一大把,但是沒幾本書是能說明白的。我想從你這里得到通俗的解釋。
????????我:我在《口述程序員如何意淫進程》的三篇文章里詳細介紹過進程是什么樣子的。你應該得到啟發(fā)的。
????????兄弟伙:我明白進程是什么樣子的了。但是fork()與exec()的原理還不甚明了。
????????我:從你的角度,你覺得進程需要具備哪些東西呢?
????????兄弟伙:至少具備四個東西。
????????????????????????1、task_struct結構體。這玩意兒好比是進程的身份證。(線程則沒有)
????????????????????????2、進程還必須要有一段可執(zhí)行代碼。
????????????????????????3、進程必須具備它獨立的內存空間(線程則沒有)。
??????????????????????? 4、進程必須具備獨立的內核堆棧。
?????? 我:是的。我順便補充一下。之所以必須具備內核堆棧,是因為代碼從內核態(tài)進入用戶態(tài)時(從0級切換到3級),必須保護內核態(tài)“現(xiàn)場”,使其能夠恢復。
???????兄弟伙:那fork()與這4點是什么關系呢?
???????我:理解這一點,必須分2種情況。
???????????????????????? 1、調用fork()之后立即調用exec()執(zhí)行新的程序,生成一個全新的進程。
????????????????????????? 2、調用fork()之后不調用exec(),僅僅是為將當前進程生成多個,以提升軟件并發(fā)能力——典型的是Web Server,如Apache,nginx等。
????????兄弟伙:對于第一點,有什么需要關注的嗎?
??????? 我:我們先聊第二點吧。
??????? 兄弟伙:好。
??????? 我:調用fork()之后,操作系統(tǒng)會復制一個全新的task_struct結構體,這個結構體除了id號不一樣外,其余的都完全一樣——這意味著,兩個進程的內存空間也是映射到相同的地址。
???????? 兄弟伙:這種情況應該是最簡單,也最完美的情況。
?????????我:是的。這種情況下,一般fork的進程數(shù)只要與CPU數(shù)量一致,整個server的性能就不會太差——至少不會因為context switch而變差。而且,具備一個優(yōu)點——如果每個進程都使用了IO多路復用,比如最典型的epoll,每一個進程都會因為fork而具備獨立的數(shù)據(jù)結構,這相對與多線程模型來說,實在太簡單了。
??????? 兄弟伙:啊。你不是說“兩個進程的內存空間也是映射到相同的地址”嗎?這豈不是互相矛盾?
??????? 我:這個問題提得很好。這并不矛盾。在fork時,兩個進程是共享想同的內存的。但是,當其中一個進程試圖去修改其中一個數(shù)據(jù)結構時(寫時復制),Linux內核就會產生“缺頁中斷”為該數(shù)據(jù)結構分配全新的空間。
??????? 兄弟伙:為什么Unix采用寫時復制會大幅度提升內存管理性能呢?
??????? 我:如果不采用寫時復制,那么調用fork時,就會為進程分配全新的、獨立的內存空間地址,而事實上,其中很大一部分內容可能與父進程是相同的——也就是說,大部分內存其實被重復浪費了。而采用寫時復制之后,只有當真的需要分配獨立的內存空間的時候,才會發(fā)生缺頁中斷,分配全新的內存空間,這個copy on write是基于page的,而不是基于進程的。
???????? 兄弟伙:明白了。通過代碼分析下fork()吧。
?????????我:首先,你需要明白,fork()其實做了些什么。我選一些早期的Linux內核代碼給你看看吧。fork()其實做了2步。1、找到空閑的進程號。2、從父進程拷貝進程信息。
????????? 1、
??????????
[html]?view plaincopy2、
[html]?view plaincopy??????? find_empty_process()這個函數(shù)你一看就明白了,實在太簡單。我們分析下copy_process這個函數(shù)吧。
??????? 在copy_process這個函數(shù)里,有一行比較牛逼的語句。這一句相對比較難一點,需要重點說明下。
??????? p = (struct task_struct *) get_free_page();
????????這里的新task_struct為什么會指向一個free page呢?
????????
???????? 明白了吧?
???????? task_struct結構體是按page分配的,多余的部分作為該進程的內核堆棧,從底向task_struct延伸。
???????? 之后就是對task_struct的屬性進行設置了,包括“智能”與CPU相關部分屬性。
?????????通過這部分源代碼的分析,你應該明白了吧——最初的“口述程序員如何意淫進程”這樣的吹牛B的話看似隨意,其實是理解Linux內核的基礎與根本,如果真把那些文章當成吹牛逼了,這里的源代碼分析對你來說就是天書了——如何才能輕松看懂源代碼分析?
????????? 答案是——多看幾遍吹牛逼的對話,直到你明白這其中的深意。
總結
以上是生活随笔為你收集整理的Linux内核源代码分析——fork()原理多进程网络模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于3G网络的汽车防盗报警系统视频监控设
- 下一篇: strcpy和strncpy区别 mem