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