Linux第六周学习总结——进程额管理和进程的创建
Linux第六周學習總結——進程額管理和進程的創建
作者:劉浩晨
【原創作品轉載請注明出處】 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
一、 進程的描述
操作系統內核三大功能:進程管理(核心)、內存管理和文件系統。
進程控制快PCB——進程描述符task_struct數據結構
進程狀態(五種狀態)轉化:
進程標識符pid_t pid唯一地標識進程
雙向循環鏈表鏈接起了所有的進程,也表示了父子、兄弟等進程關系程序創建的進程具有父子關系,在編程時往往需要引用這樣的父子關系。
struct files_struct *files; //打開文件描述符列表
二、 進程的創建
1. 進程的創建概覽及fork一個進程的源代碼
進程的起源回顧:
start_ kernel創建了cpu_ idle,即0號進程。0號進程又創建了兩個線程,一個是kernel_ init,即1號進程,這個進程最終啟動了用戶態;另一個是kthreadd。0號進程是固定的代碼,1號進程是通過復制0號進程PCB之后在此基礎上做修改得到的。
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. 系統調用回顧
- iret與int 0x80指令對應,一個是彈出寄存器值,一個是壓入寄存器的值。
- 如果將系統調用類比于fork();那么就相當于系統調用創建了一個子進程,然后子進程返回之后將在內核態運行,而返回到父進程后仍然在用戶態運行。
4.創建一個新進程在內核中的執行過程
fork、vork和clone三個系統調用都可以創建一個新進程,都通過調用do_fork()實現進程創建。
Linux通過復制父進程創建新進程:
? 復制一個PCB——task_struct
err = arch_dup_task_struct(tsk, orig);
? 給新進程分配一個新的內核堆棧
ti = alloc_ thread_ info_ node(tsk, node);
tsk->stack = ti;
setup_ thread_ stack(tsk, orig); //這里只是復制thread_ info,而非復制內核堆棧
? 從用戶態的代碼看fork(),函數返回了兩次,即在父子進程中各返回一次。這就涉及子進程的內核堆棧數據狀態和task_struct中thread記錄的sp和ip的一致性問題,這是在哪里設定的——copy_thread in copy_process
*childregs = *current_pt_regs(); //復制內核堆棧,并不是全部,只是regs結構體(內核堆棧棧底的程序)
childregs->ax = 0; //為什么子進程的fork返回0,這里就是原因!
p->thread.sp = (unsigned long) childregs; //調度到子進程時的內核棧頂
p->thread.ip = (unsigned long) ret_from_fork; //調度到子進程時的第一條指令地址,也就是說返回的就是子進程的空間了
5.創建的新進程從哪里開始執行?
一個新創建的子進程,獲得CPU之后,從哪一行代碼進程執行:
? 與之前寫過的my_ kernel相比較,kernel中是可以指定新進程開始的位置(也就是通過eip寄存器指定代碼行)。fork中也有相似的機制
? 這涉及子進程的內核堆棧數據狀態和task_ struct中thread記錄的sp和ip的一致性問題,這是在copy_ thread in copy_ process設定的
*childregs = *current_pt_regs(); //復制內核堆棧,并不是全部,只是regs結構體(內核堆棧棧底的程序)
childregs->ax = 0; //為什么子進程的fork返回0,這里就是原因!
p->thread.sp = (unsigned long) childregs; //調度到子進程時的內核棧頂
p->thread.ip = (unsigned long) ret_from_fork; //調度到子進程時的第一條指令地址,也就是說返回的就是子進程的空間了
三、 實驗——分析Linux內核創建一個新進程的過程
1.更新menu內核,刪除test_fork.c以及test.cc,并重新執行make rootfs
2.比原先多出fork命令,編譯內核查看:
3.啟動gdb跟蹤調試內核,在一些重要函數處設置斷點:
4.在MenuOS中執行fork,停在父進程中。繼續執行后,停在do_fork的位置:
5.n命令進行單步執行,依次進入copy_process、dup_task_struct。此時父進程的PCB(task_struct數據結構)已經復制過來。s命令進入函數,可以看到dst = src:
6.copy_thread函數中,把task_pg_regs(p)也就是內核堆棧特定的地址找到并初始化
7.當前進程的內核堆棧寄存器中的值復制到子進程中
8.164行:p->thread.ip = (unsigned long) ret_from_fork; //確定返回地址
9.當程序跳轉到syscall_exit,就不能再繼續gdb跟蹤調試,輸入finish使得進程運行完。
總結:
1.Linux通過復制父進程來創建一個新進程,通過調用do_fork來實現。
2.Linux為每個新創建的進程動態地分配一個task_struct結構。
3.為了把內核中的所有進程組織起來,Linux提供了幾種組織方式,其中哈希表和雙向循環鏈表方式是針對系統中的所有進程(包括內核線程),而運行隊列和等待隊列是把處于同一狀態的進程組織起來。
4.fork()函數被調用一次,但返回兩次。
轉載于:https://www.cnblogs.com/lhc-java/p/5340414.html
總結
以上是生活随笔為你收集整理的Linux第六周学习总结——进程额管理和进程的创建的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 回归本心
- 下一篇: (王道408考研操作系统)第一章计算机系