Linux核心設計第六周
——程序的描述和建立
第一部分 知識點總結
一、程序描述符task_struct資料結構
1、作業系統的三大功能:
程序管理、記憶體管理、檔案系統
2、程序的作用:
将信号、程序間通信、記憶體管理和檔案系統聯系起來
3、程序控制塊PCB——task_struct資料結構
提供了核心需要了解的資訊
4、task_struct結構龐大,有400多行代碼。包含了程序狀态、核心堆棧等相關資訊的定義。 可以從下面的圖示中,清晰的看出大體的架構。
Linux核心設計第六周 ——程式的描述和建立Linux核心設計第六周
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnaugjMzYjM0cTOtMjNzAzM3MjNxkjMzAjNxAjMtEzN2QDN38CXzAjNxAjMvwVM3YDN0czLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.jpg)
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核心設計第六周
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