天天看點

Linux核心設計第六周 ——程序的描述和建立Linux核心設計第六周

Linux核心設計第六周

——程序的描述和建立

第一部分 知識點總結

一、程序描述符task_struct資料結構

1、作業系統的三大功能:

程序管理、記憶體管理、檔案系統

2、程序的作用:

将信号、程序間通信、記憶體管理和檔案系統聯系起來

3、程序控制塊PCB——task_struct資料結構

提供了核心需要了解的資訊

4、task_struct結構龐大,有400多行代碼。包含了程序狀态、核心堆棧等相關資訊的定義。 可以從下面的圖示中,清晰的看出大體的架構。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

5、Linux的程序和作業系統原理中描述的程序狀态(就緒狀态、運作狀态、阻塞狀态)有所不同,實際核心中,就緒和運作狀态都用TASK_RUNNING表示。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

6、程序标志符pid/tpid——用于辨別程序

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

7、總體浏覽task_struct結構

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

二、程序的建立概覽及fork一個程序的使用者态代碼

1、程序建立的關鍵資訊:狀态、核心堆棧、CPU上下文切換、連結、檔案系統、信号、記憶體等。

2、程序的起源

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

3、程序建立步驟:

  • 複制程序描述符
  • 修改子程序的PCB資訊

4、shell指令如何建立子程序

  • 使用fork函數在使用者态建立子程序
  • fork系統調用在父程序和子程序各傳回一次
  • 在子程序中,傳回0,在父程序中,傳回子程序ID

三、了解程序建立過程複雜代碼的方法

了解程序建立過程複雜代碼的方法:
  • 先根據對功能的了解,自己預想可以怎樣實作;
  • 再從源代碼中,找到和預想相似的證據。

1、系統調用複習

  • 從系統核心了解

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周
  • 從使用者程式了解

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

2、fork代碼

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
    int pid;
    /* fork another process */
    pid = fork();
    if (pid < 0) 
    { 
        /* error occurred */
        fprintf(stderr,"Fork Failed!");
        exit(-1);
    } 
    else if (pid == 0) //pid == 0和下面的else都會被執行到(一個是在父程序中即pid ==0的情況,一個是在子程序中,即pid不等于0)
    {
        /* child process */
        printf("This is Child Process!\n");
    } 
    else 
    {  
        /* parent process  */
        printf("This is Parent Process!\n");
        /* parent will wait for the child to complete*/
        wait(NULL);
        printf("Child Complete!\n");
    }
}      

3、fork、vork、clone都可以建立一個子程序,它們都調用了do_fork()實作程序建立。

四、浏覽程序建立過程的關鍵代碼

1、複制PCB——task_struct

err = arch_dup_task_struct(tsk, orig);      

2、給新程序建立新的核心堆棧

ti = alloc_thread_info_node(tsk, node);//建立新的核心堆棧
tsk->stack = ti;
setup_thread_stack(tsk, orig); //這裡隻是複制thread_info,而非複制核心堆棧
setup_thread_stack(tsk,orig);      

3複制copy_process

即核心堆棧中的thread.sp的内容

4、fork執行過程分析

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

5、子程序是從哪裡執行的? 

代碼部分為

*childregs = *current_pt_regs(); //複制核心堆棧
childregs->ax = 0; //為什麼子程序的fork傳回0,這裡就是原因!

p->thread.sp = (unsigned long) childregs; //排程到子程序時的核心棧頂
p->thread.ip = (unsigned long) ret_from_fork; //排程到子程序時的第一條指令位址
           
ret_from_fork就是子函數開始執行的地方,我們檢視ret_from_fork在系統檔案entry32.h中的定義可以發現,是執行了如下圖所示的過程。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

第二部分 實驗部分

1、更新menu,删除test_fork.c和test.c檔案,重新執行make rootfs

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

2、重新make rootfs之後,可以看到核心被啟動

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

3、像之前的實驗一樣,啟動gdb調試

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

4、在fork系統調用的關鍵代碼處,設定斷點。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

5、在Menu系統中輸入fork指令,可以看到隻輸出了fork功能的描述,沒有之後的部分,說明該過程在斷點處停止了。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

6、單步調試,可以看到程式停在了copy_process函數處

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

7、繼續單步執行,程式再次停在了dup_task_struct函數處

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

8、進入duptaskstruct函數,繼續單步執行,程式再次停在了duptaskstruct函數處

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

9、在copy_thread函數中,繼續單步執行,可以看到,核心空間壓棧位址被初始化了。

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

10、繼續執行單步調試,如圖所示,目前核心堆棧寄存器中的值複制到子程序中

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

11、如圖所示,标記代碼的作用是,設定子程序被排程的起點

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

12、如圖所示,我們可以看到程式停止在了ret_from_fork處

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

13、對ret_from_fork繼續執行單步調試,我們可以看,目前系統執行的是彙編代碼

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

14、當程式跳轉到syscall_exit處後,就不能再繼續gdb跟蹤調試了

Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周

總結:

子程序是從ret_ from_ fork開始執行的。子程序在進入sys_ call之前與父程序的堆棧狀态相同,ret_ from_ fork中跳轉到了syscall_ exit,繼續執行上周我們分析過的部分,注意:當子程序獲得CPU控制權的時候,它的ret_ from_ fork可以把後面堆棧從iret傳回到使用者态,這裡的使用者态是子程序的使用者态

宋宸甯+ 原創作品轉載請注明出處 + 《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000

轉載于:https://www.cnblogs.com/java-stx/p/5333556.html

繼續閱讀