<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          第34回 | 進程2的創(chuàng)建

          共 4557字,需瀏覽 10分鐘

           ·

          2022-04-24 10:40

          新讀者看這里,老讀者直接跳過。


          本系列會以一個讀小說的心態(tài),從開機啟動后的代碼執(zhí)行順序,帶著大家閱讀和賞析 Linux 0.11 全部核心代碼,了解操作系統(tǒng)的技術(shù)細節(jié)和設(shè)計思想。


          本系列的 GitHub 地址如下,希望給個 star 以示鼓勵(文末閱讀原文可直接跳轉(zhuǎn),也可以將下面的鏈接復制到瀏覽器里打開)

          https://github.com/sunym1993/flash-linux0.11-talk


          本回的內(nèi)容屬于第四部分。



          你會跟著我一起,看著一個操作系統(tǒng)從啥都沒有開始,一步一步最終實現(xiàn)它復雜又精巧的設(shè)計,讀完這個系列后希望你能發(fā)出感嘆,原來操作系統(tǒng)源碼就是這破玩意。


          以下是已發(fā)布文章的列表,詳細了解本系列可以先從開篇詞看起。


          開篇詞


          第一部分 進入內(nèi)核前的苦力活


          第1回 | 最開始的兩行代碼

          第2回 | 自己給自己挪個地兒

          第3回 | 做好最最基礎(chǔ)的準備工作

          第4回 | 把自己在硬盤里的其他部分也放到內(nèi)存來

          第5回 | 進入保護模式前的最后一次折騰內(nèi)存

          第6回 | 先解決段寄存器的歷史包袱問題

          第7回 | 六行代碼就進入了保護模式

          第8回 | 煩死了又要重新設(shè)置一遍 idt 和 gdt

          第9回 | Intel 內(nèi)存管理兩板斧:分段與分頁

          第10回 | 進入 main 函數(shù)前的最后一躍!

          第一部分總結(jié)與回顧


          第二部分 大戰(zhàn)前期的初始化工作


          第11回 | 整個操作系統(tǒng)就 20 幾行代碼

          第12回 | 管理內(nèi)存前先劃分出三個邊界值

          第13回 | 主內(nèi)存初始化 mem_init

          第14回 | 中斷初始化 trap_init

          第15回 | 塊設(shè)備請求項初始化 blk_dev_init

          第16回 | 控制臺初始化 tty_init

          第17回 | 時間初始化 time_init

          第18回 | 進程調(diào)度初始化 sched_init

          第19回 | 緩沖區(qū)初始化 buffer_init

          第20回 | 硬盤初始化 hd_init

          第二部分總結(jié)與回顧


          第三部分:一個新進程的誕生


          第21回 | 新進程誕生全局概述

          第22回 | 從內(nèi)核態(tài)切換到用戶態(tài)

          第23回 | 如果讓你來設(shè)計進程調(diào)度

          第24回 | 從一次定時器滴答來看進程調(diào)度

          25回 | 通過 fork 看一次系統(tǒng)調(diào)用

          第26回 | fork 中進程基本信息的復制

          第27回 | 透過 fork 來看進程的內(nèi)存規(guī)劃

          第三部分總結(jié)與回顧


          第28回 | 番外篇 - 我居然會認為權(quán)威書籍寫錯了...

          第29回 | 番外篇 - 讓我們一起來寫本書?

          第30回 | 番外篇 - 寫時復制就這么幾行代碼


          第四部分:shell 程序的到來

          第31回 | 拿到硬盤信息
          第32回 | 加載根文件系統(tǒng)
          第33回 | 打開終端設(shè)備文件
          第34回 | 進程 2 的創(chuàng)建(本文)



          ------- 正文開始?-------




          書接上回,上回書咱們說到,進程 1 通過 open 函數(shù)建立了與外設(shè)交互的能力,具體其實就是打開了 tty0 這個設(shè)備文件,并綁定了標準輸入 0,標準輸出 1 和 標準錯誤輸出 2 這三個文件描述符。


          ?

          ?

          同時我們看到源碼中用 printf 函數(shù),調(diào)用 write 函數(shù),向 1 號文件描述符輸出了字符串的效果。

          ?

          ?

          到此為止,標志著進程 1 的工作基本結(jié)束了,準確說是能力建設(shè)的工作結(jié)束了,接下來就是控制流程創(chuàng)建新的進程了,我們繼續(xù)往下看。

          void?init(void)?{
          ????...
          ????if?(!(pid=fork()))?{
          ????????close(0);
          ????????open("/etc/rc",O_RDONLY,0);
          ????????execve("/bin/sh",argv_rc,envp_rc);
          ????????_exit(2);
          ????}
          ????if?(pid>0)
          ????????while?(pid?!=?wait(&i))
          ????????????/*?nothing?*/;
          ????while?(1)?{
          ????????if?(!(pid=fork()))?{
          ????????????close(0);close(1);close(2);
          ????????????setsid();
          ????????????(void)?open("/dev/tty0",O_RDWR,0);
          ????????????(void)?dup(0);
          ????????????(void)?dup(0);
          ????????????_exit(execve("/bin/sh",argv,envp));
          ????????}
          ????????while?(1)
          ????????????if?(pid?==?wait(&i))
          ????????????????break;
          ????????printf("\n\rchild?%d?died?with?code?%04x\n\r",pid,i);
          ????????sync();
          ????}
          ????_exit(0);???/*?NOTE!?_exit,?not?exit()?*/
          }

          別急,我們一點點看,我仍然是去掉了一些錯誤校驗的旁路分支。

          void?init(void)?{
          ????...
          ????if?(!(pid=fork()))?{
          ????????close(0);
          ????????open("/etc/rc",O_RDONLY,0);
          ????????execve("/bin/sh",argv_rc,envp_rc);
          ????????_exit(2);
          ????}
          ????...
          }

          先看這個第一段,我們先嘗試口述翻譯一遍。

          ?

          1. fork 一個新的子進程,此時就是進程 2 了。
          2. 在進程 2 里關(guān)閉(close) 0 號文件描述符。
          3. 只讀形式打開(open) rc 文件。
          4. 然后執(zhí)行(execve) sh 程序。

          ?

          聽起來還蠻合邏輯的,創(chuàng)建進程(fork)、關(guān)閉(close)、打開(open)、執(zhí)行(execve)四步走,接下來我們一點點拆解。


          fork

          ?

          fork 前面講過了,就是將進程的 task_struct 結(jié)構(gòu)進行一下復制,比如進程 0 fork 出進程 1 的時候。

          ?

          ?

          之后,新進程再重寫一些基本信息,包括元信息和 tss 里的寄存器信息。再之后,用 copy_page_tables 復制了一下頁表(這里涉及到寫時復制的伏筆)。


          比如進程 0 復制出進程 1 的時候,頁表是這樣復制的。

          ?

          ?

          而這里的進程 1 fork 出進程 2,也是同樣的流程,不同之處在于兩點細節(jié):

          ?

          第一點,進程 1 打開了三個文件描述符并指向了 tty0,那這個也被復制到進程 2 了,具體說來就是進程結(jié)構(gòu) task_struct 里的 flip[] 數(shù)組被復制了一份。

          struct?task_struct?{
          ????...
          ????struct?file?*filp[NR_OPEN];
          ????...
          };

          而進程 0 fork 出進程 1 時是沒有復制這部分信息的,因為進程 0 沒有打開任何文件。這也是剛剛說的與外設(shè)交互能力的體現(xiàn),即進程 0 沒有與外設(shè)交互的能力,進程 1 有,哎,其實就是這個 flip 數(shù)組里有沒有東西而已嘛~

          ?

          第二點,進程 0 復制進程 1 時頁表的復制只有 160 項,也就是映射 640K,而之后進程的復制,統(tǒng)統(tǒng)都是復制 1024 項,也就是映射 4M 空間。

          int?copy_page_tables(unsigned?long?from,unsigned?long?to,long?size)?{
          ????...
          ????nr?=?(from==0)?0xA0:1024;
          ????...
          }

          整體看就是如圖所示。

          ?

          ?

          除此之外,就沒有別的區(qū)別了。


          close


          好了,我們繼續(xù)看。

          void?init(void)?{
          ????...
          ????if?(!(pid=fork()))?{
          ????????close(0);
          ????????open("/etc/rc",O_RDONLY,0);
          ????????execve("/bin/sh",argv_rc,envp_rc);
          ????????_exit(2);
          ????}
          ????...
          }

          fork 完之后,后面 if 里面的代碼都是進程 2 在執(zhí)行了。

          ?

          close(0) 就是關(guān)閉 0 號文件描述符,也就是進程 1 復制過來的打開了 tty0 并作為標準輸入的文件描述符,那么此時 0 號文件描述符就空出來了。


          下面是 close 對應(yīng)的系統(tǒng)調(diào)用函數(shù),很簡單。

          int?sys_close(unsigned?int?fd)?{???
          ????...
          ????current->filp[fd]?=?NULL;
          ????...
          }


          open


          接下來 open 函數(shù)以只讀形式打開了一個叫 /etc/rc 的文件,剛好占據(jù)了 0 號文件描述符的位置。

          void?init(void)?{
          ????...
          ????if?(!(pid=fork()))?{
          ????????...
          ????????open("/etc/rc",O_RDONLY,0);
          ????????...
          ????}
          ????...
          }

          這個 rc 文件表示配置文件,具體什么內(nèi)容,取決于你的硬盤里這個位置處放了什么內(nèi)容,與操作系統(tǒng)內(nèi)核無關(guān),所以我們暫且不用管。

          ?

          此時,進程 2 與進程 1 幾乎完全一樣,只不過進程 2 通過 close 和 open 操作,將原來進程 1 的指向標準輸入的 0 號文件描述符,重新指向了 /etc/rc 文件。


          到目前為止,進程?2?與進程?1?的區(qū)別,僅僅是將?0?號文件描述符重新指向了?/etc/rc?文件,其他的沒啥區(qū)別。


          而這個 rc 文件是干嘛的,現(xiàn)在還不用管,肯定是后面 sh 程序要用到的,到時候在說。


          execve

          ?

          好,接下來進程 2 就將變得不一樣了,會通過一個經(jīng)典的,也是最難理解的 execve 函數(shù)調(diào)用,使自己搖身一變,成為 /bin/sh 程序繼續(xù)運行,這就是下一章的重點!

          void?init(void)?{
          ????...
          ????if?(!(pid=fork()))?{
          ????????...
          ????????execve("/bin/sh",argv_rc,envp_rc);
          ????????...
          ????}
          ????...
          }

          這里就包含著操作系統(tǒng)究竟是如何加載并執(zhí)行一個程序的原理,包括如何從文件系統(tǒng)中找到這個文件,如何解析一個可執(zhí)行文件(在現(xiàn)代的 Linux 里稱作 ELF 可執(zhí)行文件),如何講可執(zhí)行文件中的代碼和數(shù)據(jù)加載到內(nèi)存并運行。


          加載到內(nèi)存并運行又包含著虛擬內(nèi)存等相關(guān)的知識。所以這里面的水很深,了解了這個函數(shù),再加上 fork 函數(shù),基本就可以把操作系統(tǒng)全部核心邏輯都串起來了。


          欲知后事如何,且聽下回分解。




          ------- 關(guān)于本系列?-------




          本系列的開篇詞看這,開篇詞


          本系列的番外故事看這,讓我們一起來寫本書?也可以直接無腦加入星球,共同參與這場旅行。



          最后,本系列完全免費,希望大家能多多傳播給同樣喜歡的人,同時給我的 GitHub 項目點個 star,就在閱讀原文處,這些就足夠讓我堅持寫下去了!我們下回見。

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天津人妻第二次3p | AA黄色片视频 | 2024av天堂网 | 手机在线日本A V电影 | 欧美国产一二三区小说 |