天天看點

第六周

20135103王海甯

《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000

這周的實驗是通過gdb跟蹤調試Linux核心是如何fock産生一個程序并從哪開始執行這個子程序的。

下面就進入實驗樓的環境開始實驗,輸入以下指令重新下載下傳編譯,如圖:

[plain] view plain copy

  1. rm menu -rf    
  2. git  clone https://github.com/mengning/menu.git    
  3. cd menu    
  4. mv test_fork.c test.c    
  5. make rootfs    

檢視test.c裡面的代碼如下:

啟動menuOS後停止等待,開啟gdb調試:

打好斷點:

接着在menuOS界面輸入fork運作:

切換回shell指令行界面,輸入continue指令,走起:

fork執行完成後,運作結果如下圖:

分析

1.程序間的狀态轉換

2.新程序的産生

     話說,道生一(start_kernel、cpu_idle)、一生二(kernel_init和kthreadd)、二生三(即前面0,1和2三個程序)

1号程序是所有使用者态程序祖先、0号程序是所有核心線程的祖先。

程序描述符PCB的資料結構是task_struct類型,它包括了程序相關的所有資訊,task_struct結構中主要有:

-程序狀态(記錄程序等待、運作或死鎖三種狀态)

-排程資訊

-辨別符

-程序間的通訊情況

-程序連結資訊(程序連結清單的插入等操作資訊)

-時間和定時器資訊

-檔案系統資訊

-頁面管理資訊

-和處理器相關的環境上下文資訊

-...

其中:

1)state字段。用于描述程序目前的狀态,由一組标志組成,每個标志描述一種程序可能的狀态,這些狀态的是互斥的。

2)thread_info字段。Linux核心用共用體結構将一個程序的線程描述符和核心堆棧

union thread_union{

    struct thread_info thread_info;

    unsigned long stack[2048];

}

3)task字段。所有的程序的描述符組成了一個雙向連結清單,task字段的類型為list_head。

struct list_head{

    struct list_head *next,*prev;

    start_kernel中建立0号程序,由0号程序建立1号程序,1号程序是所有使用者态程序的祖先,0号程序是所有核心程序的祖先。使用者态程式通過調用 fork建立一個有父子關系的新程序。  通過copy_thread複制現有程序實作建立新程序,先複制task_struct,再為其配置設定一個核心棧,接着copy_process進行修改,如程序pid、程序連結清單等。fork、vfork和clone三個系統調用都可建立一個新程序,但都是通過do_fork實作的。

複制過程為:

1)err = arch_dup_task_struct(tsk, orig); // 複制一個task_struct資料結構的PCB

2)ti = alloc_thread_info_node(tsk, node); tsk->stack = ti; // 給新程序配置設定一個核心堆棧

3)setup_thread_stack(tsk, orig);   // 這裡隻是複制thread_info,而非複制核心堆棧

4)*childregs = *current_pt_regs(); // 複制核心堆棧 

5)childregs->ax = 0; // 子程序的fork傳回0的原因

     建立好後執行到ret_from_fork,在ret_from_fork中會跳到syscall_exit,此時父程序的核心棧中儲存了執行fork前的上下文,子程序的核心棧中也從父程序那裡複制到的自己的上下文,是以現在父子程序可以等待被排程後正常執行啦。

總結:

     在Linux中建立新程序中,用fork得到的子程序是父程序的複制,它從父程序處複制了整個程序的位址空間,包括進

程上下文、程序堆棧、記憶體資訊、打開的檔案描述符、信号控制設定、程序優先級、程序組号、目前工作目錄、根目錄、資源限制、控制終端等。子程序所獨有的隻是它的程序号、資源使用和計時器等。

        1.無論用三種系統調用clone、fork、vfork中的哪種來建立一個新程序,都是通過調用do_fork來實作的

        2.通過複制父程序PCB的task_struct建立一個新程序

        3.子程序修改複制後的PCB,如pid、程序連結清單等

        4.fork()系統調用産生的子程序從ret_from_fork處開始執行,p->thread.ip = (unsigned long)ret_from_fork

        5.可通過傳回值判斷目前程序是父程序還是子程序,父程序處傳回程序号,子程序自己傳回0