前言:本文爲我學習孟寧老師的《庖丁解牛linux 內核》課程的簡單總結,同時做爲課間做業。 linux
唐建,《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000函數
一、概述學習
本文經過一個簡單的自定義內核程序來簡單描述內核中程序的相互切換過程,給你們展現內存多進程隨着時間片輪轉而來回切換this
的過程以及原理。線程
二、代碼結構3d
咱們的例子代碼結構:主要有兩個文件mypcb.c myinterrupt.crest
mypcb.c文件內容: blog
myinteruppt.c文件內容:進程
my_start_kernel() ,系統的起點,完成系統環境的準備ip
my_process()這問進程的主體,咱們將起多個進程實例。
my_schedule() 系統進程調度器,咱們將在這個函數看到進程是怎麼切換的。
my_time_handler() 時間中斷函數,這個函數裏面產生時間片,時間片到了後就須要進行進程切換了。
三、my_start_kernel() 系統環境的準備。
咱們先看下重要的結構體 ,進程的結構體,至關於系統的task_struct
struct Thread {
unsigned long ip;
unsigned long sp;
};
typedef struct PCB{
int pid; ——pid
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ ——當前進程調狀態
unsigned long stack[KERNEL_STACK_SIZE]; ——進程的堆棧
/* CPU-specific state of this task */
struct Thread thread; ——線程,不用理會線程。主要看其內容ip=eip,sp=esp。就是進程當前執行到哪裏了。
unsigned long task_entry; ——進程的入口
struct PCB *next; ——下一個進程
}tPCB;
咱們的linux 系統進程調度大致上就是操做task_struct,因此咱們這模擬的系統就是圍繞這tpcb進行操做
回到正題,my_start_kernel以下圖這個函數分三部分
(1)、0號進程的初始化
進程的入口爲my_process,因此咱們一會主要看怎麼跳轉到進程的入口的。
這裏task_entry 賦予了進程入口my_process()。同時sp=esp = 進程的棧底,由於是剛開始嘛,棧仍是空的。
(2)、fork多個進程,這裏模擬了fork的過程,
(3)、咱們這着重講這個,就是my_start_kernel怎麼切換到0號進程的。
asm volatile(
"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */ ——將esp 賦值爲0號進程的sp,好了,這樣棧就切換到0號進程的棧了
"pushl %1\n\t" /* push ebp */ ——將將由於0號進程還沒開始跑呢,因此ebp=esp啊,因此這裏其實是將ebp壓棧
"pushl %0\n\t" /* push task[pid].thread.ip */ ——將ip=eip 將0號進程的eip入棧。
"ret\n\t" /* pop task[pid].thread.ip to eip */ ——ret= pop eip。準備切換到0號進程了,將0號進程的ip放入eip寄存器中。
"popl %%ebp\n\t" ——sp=ebp,準備棧底。到這裏,ebp、eip、esp都準備好了,注意eip=my_process,
也就是已經切換到0號進程了。
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
四、時間片
如上圖,實際上就是沒1000個時鐘就賦值my_need_sched=1,這個是調度標誌位,表示要開始調度到下一個進程了。
五、my_process
前面咱們講到0號進程已經開始跑起來了,也就是跑到了my_process這裏。
如上圖,能夠看到這裏是死循環,當發現調度標誌被設置爲1時,表示要開始調度了,這個進程不能再執行了,輪到別人了,因而調用my_schedule去切換到其餘進程。
六、my_schedule,進程切換。
如今來到咱們的核心了,實際上就保存上一個進程的現場,而後將須要調度的進程的現場恢復到寄存器中,讓寄存器按照這個進程的現場運行。
(1)、這個就是取出立刻須要進程的結構體
(2)、這個就是難點了
/* switch to next process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */ ——將本進程的ebp入棧
"movl %%esp,%0\n\t" /* save esp */ ——將esp保存到本進程中,用於下次執行
"movl %2,%%esp\n\t" /* restore esp */ ——將新進程的sp寫入esp
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
"1:\t" /* next process start here */
"popl %%ebp\n\t"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
這個實際上就是保存本進程的信息,而後將下一個進程的東西裝入寄存器,並開始執行。
七、執行結果
能夠推測,咱們的進程是按照序號順序執行的