日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核源代码分析——fork()原理多进程网络模型

發布時間:2025/3/21 linux 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核源代码分析——fork()原理多进程网络模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?今晚和一位500強的leader喝喝小酒吃吃烤魚,生活樂無邊。這位兄弟伙才畢業2年,已經做到管理層了,機遇和能力不可謂不好。喝酒之余,聊到Linux內核的兩個問題——fork()、exec()的原理。

??????? 兄弟伙:fork()的原理是什么呢?

??????? 我:其實一句話就概括了——copy on write。

????????兄弟伙:copy on wirte我懂,書上介紹的一抓一大把,但是沒幾本書是能說明白的。我想從你這里得到通俗的解釋。

????????我:我在《口述程序員如何意淫進程》的三篇文章里詳細介紹過進程是什么樣子的。你應該得到啟發的。

????????兄弟伙:我明白進程是什么樣子的了。但是fork()與exec()的原理還不甚明了。

????????我:從你的角度,你覺得進程需要具備哪些東西呢?

????????兄弟伙:至少具備四個東西。

????????????????????????1、task_struct結構體。這玩意兒好比是進程的身份證。(線程則沒有)

????????????????????????2、進程還必須要有一段可執行代碼。

????????????????????????3、進程必須具備它獨立的內存空間(線程則沒有)。

??????????????????????? 4、進程必須具備獨立的內核堆棧。

?????? 我:是的。我順便補充一下。之所以必須具備內核堆棧,是因為代碼從內核態進入用戶態時(從0級切換到3級),必須保護內核態“現場”,使其能夠恢復。

???????兄弟伙:那fork()與這4點是什么關系呢?

???????我:理解這一點,必須分2種情況。

???????????????????????? 1、調用fork()之后立即調用exec()執行新的程序,生成一個全新的進程。

????????????????????????? 2、調用fork()之后不調用exec(),僅僅是為將當前進程生成多個,以提升軟件并發能力——典型的是Web Server,如Apache,nginx等。

????????兄弟伙:對于第一點,有什么需要關注的嗎?

??????? 我:我們先聊第二點吧。

??????? 兄弟伙:好。

??????? 我:調用fork()之后,操作系統會復制一個全新的task_struct結構體,這個結構體除了id號不一樣外,其余的都完全一樣——這意味著,兩個進程的內存空間也是映射到相同的地址。

???????? 兄弟伙:這種情況應該是最簡單,也最完美的情況。

?????????我:是的。這種情況下,一般fork的進程數只要與CPU數量一致,整個server的性能就不會太差——至少不會因為context switch而變差。而且,具備一個優點——如果每個進程都使用了IO多路復用,比如最典型的epoll,每一個進程都會因為fork而具備獨立的數據結構,這相對與多線程模型來說,實在太簡單了。

??????? 兄弟伙:啊。你不是說“兩個進程的內存空間也是映射到相同的地址”嗎?這豈不是互相矛盾?

??????? 我:這個問題提得很好。這并不矛盾。在fork時,兩個進程是共享想同的內存的。但是,當其中一個進程試圖去修改其中一個數據結構時(寫時復制),Linux內核就會產生“缺頁中斷”為該數據結構分配全新的空間。

??????? 兄弟伙:為什么Unix采用寫時復制會大幅度提升內存管理性能呢?

??????? 我:如果不采用寫時復制,那么調用fork時,就會為進程分配全新的、獨立的內存空間地址,而事實上,其中很大一部分內容可能與父進程是相同的——也就是說,大部分內存其實被重復浪費了。而采用寫時復制之后,只有當真的需要分配獨立的內存空間的時候,才會發生缺頁中斷,分配全新的內存空間,這個copy on write是基于page的,而不是基于進程的。

???????? 兄弟伙:明白了。通過代碼分析下fork()吧。

?????????我:首先,你需要明白,fork()其實做了些什么。我選一些早期的Linux內核代碼給你看看吧。fork()其實做了2步。1、找到空閑的進程號。2、從父進程拷貝進程信息。

????????? 1、

??????????

[html]?view plaincopy
  • int?find_empty_process(void)??
  • {??
  • ????int?i;??
  • ??
  • ????repeat:??
  • ????????if?((++last_pid)<0)?last_pid=1;??
  • ????????for(i=0?;?i<NR_TASKS?;?i++)??
  • ????????????if?(task[i]?&&?task[i]->pid?==?last_pid)?goto?repeat;??
  • ????for(i=1?;?i<NR_TASKS?;?i++)??
  • ????????if?(!task[i])??
  • ????????????return?i;??
  • ????return?-EAGAIN;??
  • }??
  • 2、

    [html]?view plaincopy
  • int?copy_process(int?nr,long?ebp,long?edi,long?esi,long?gs,long?none,??
  • ????????long?ebx,long?ecx,long?edx,??
  • ????????long?fs,long?es,long?ds,??
  • ????????long?eip,long?cs,long?eflags,long?esp,long?ss)??
  • {??
  • ????struct?task_struct?*p;??
  • ????int?i;??
  • ????struct?file?*f;??
  • ??
  • ????p?=?(struct?task_struct?*)?get_free_page();??
  • ????if?(!p)??
  • ????????return?-EAGAIN;??
  • ????task[nr]?=?p;??
  • ????*p?=?*current;??/*?NOTE!?this?doesn't?copy?the?supervisor?stack?*/??
  • ????p->state?=?TASK_UNINTERRUPTIBLE;??
  • ????p->pid?=?last_pid;??
  • ????p->father?=?current->pid;??
  • ????p->counter?=?p->priority;??
  • ????p->signal?=?0;??
  • ????p->alarm?=?0;??
  • ????p->leader?=?0;???????/*?process?leadership?doesn't?inherit?*/??
  • ????p->utime?=?p->stime?=?0;??
  • ????p->cutime?=?p->cstime?=?0;??
  • ????p->start_time?=?jiffies;??
  • ????p->tss.back_link?=?0;??
  • ????p->tss.esp0?=?PAGE_SIZE?+?(long)?p;??
  • ????p->tss.ss0?=?0x10;??
  • ????p->tss.eip?=?eip;??
  • ????p->tss.eflags?=?eflags;??
  • ????p->tss.eax?=?0;??
  • ????p->tss.ecx?=?ecx;??
  • ????p->tss.edx?=?edx;??
  • ????p->tss.ebx?=?ebx;??
  • ????p->tss.esp?=?esp;??
  • ????p->tss.ebp?=?ebp;??
  • ????p->tss.esi?=?esi;??
  • ????p->tss.edi?=?edi;??
  • ????p->tss.es?=?es?&?0xffff;??
  • ????p->tss.cs?=?cs?&?0xffff;??
  • ????p->tss.ss?=?ss?&?0xffff;??
  • ????p->tss.ds?=?ds?&?0xffff;??
  • ????p->tss.fs?=?fs?&?0xffff;??
  • ????p->tss.gs?=?gs?&?0xffff;??
  • ????p->tss.ldt?=?_LDT(nr);??
  • ????p->tss.trace_bitmap?=?0x80000000;??
  • ????if?(last_task_used_math?==?current)??
  • ????????__asm__("clts?;?fnsave?%0"::"m"?(p->tss.i387));??
  • ????if?(copy_mem(nr,p))?{??
  • ????????task[nr]?=?NULL;??
  • ????????free_page((long)?p);??
  • ????????return?-EAGAIN;??
  • ????}??
  • ????for?(i=0;?i<NR_OPEN;i++)??
  • ????????if?(f=p->filp[i])??
  • ????????????f->f_count++;??
  • ????if?(current->pwd)??
  • ????????current->pwd->i_count++;??
  • ????if?(current->root)??
  • ????????current->root->i_count++;??
  • ????if?(current->executable)??
  • ????????current->executable->i_count++;??
  • ????set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));??
  • ????set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));??
  • ????p->state?=?TASK_RUNNING;?/*?do?this?last,?just?in?case?*/??
  • ????return?last_pid;??
  • }??
  • ??????? find_empty_process()這個函數你一看就明白了,實在太簡單。我們分析下copy_process這個函數吧。

    ??????? 在copy_process這個函數里,有一行比較牛逼的語句。這一句相對比較難一點,需要重點說明下。

    ??????? p = (struct task_struct *) get_free_page();

    ????????這里的新task_struct為什么會指向一個free page呢?

    ????????

    ???????? 明白了吧?

    ???????? task_struct結構體是按page分配的,多余的部分作為該進程的內核堆棧,從底向task_struct延伸。

    ???????? 之后就是對task_struct的屬性進行設置了,包括“智能”與CPU相關部分屬性。

    ?????????通過這部分源代碼的分析,你應該明白了吧——最初的“口述程序員如何意淫進程”這樣的吹牛B的話看似隨意,其實是理解Linux內核的基礎與根本,如果真把那些文章當成吹牛逼了,這里的源代碼分析對你來說就是天書了——如何才能輕松看懂源代碼分析?

    ????????? 答案是——多看幾遍吹牛逼的對話,直到你明白這其中的深意。

    總結

    以上是生活随笔為你收集整理的Linux内核源代码分析——fork()原理多进程网络模型的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。