作業系統是如何工作的
《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
一、函式呼叫堆疊
1、計算機工作三個法寶
儲存程式計算機、中斷機制、堆疊
- 儲存程式計算機工作模型,計算機系統最最基礎性的邏輯結構;
- 函式呼叫堆疊,高階語言得以執行的基礎,只有機器語言和組合語言的時候堆疊機制對於計算機來說並不那麼重要,但有了高階語言及函式,堆疊成為了計算機的基礎功能;
2、堆疊是C語言程式執行時必須的一個記錄呼叫路徑和引數的空間
-函式呼叫框架
-傳遞引數
-儲存返回地址
-提供區域性變數空間
3、堆疊相關的暫存器
-esp,堆疊指標,指向棧頂
-ebp,基址指標,指向棧底,在C語言中用作記錄當前函式呼叫基址。
4、其他關鍵暫存器
-cs(程式碼段暫存器) : eip:總是指向下一條的指令地址
- 順序執行:總是指向地址連續的下一條指令
- 跳轉/分支:執行這樣的指令的時候, cs : eip的值會根據程式需要被修改
- call:將當前cs:eip的值壓入棧頂,cs:eip指向被呼叫函式的入口地址。
- ret:從棧頂彈出原來儲存在這裡的cs:eip的值,放入cs:eip中
5、建立函式堆疊框架 enter
push %ebp
movl %esp,%ebp
6、拆除函式堆疊框架 leave
movl %ebp.%esp
pop %ebp
函式引數傳遞機制和區域性變數儲存
中斷,多道程式作業系統的基點,沒有中斷機制程式只能從頭一直執行結束才有可能開始執行其他程式。
二、堆疊
1、堆疊是C語言程式執行時必須的一個記錄呼叫路徑和引數的空間。
2、堆疊存在的目的:函式呼叫框架;傳遞引數;儲存返回地址;提供區域性變數空間;等等。
3、C語言編譯器對堆疊的使用有一套的規則。
4、瞭解堆疊存在的目的和編譯器對堆疊使用的規則是理解操 作系統一些關鍵性程式碼的基礎。
三、實驗
mymain.c程式碼

1 /*
2 * linux/mykernel/myinterrupt.c
3 *
4 * Kernel internal my_timer_handler
5 *
6 * Copyright (C) 2013 Mengning
7 *
8 */
9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/ctype.h>
12 #include <linux/tty.h>
13 #include <linux/vmalloc.h>
14
15 #include "mypcb.h"
16
17 extern tPCB task[MAX_TASK_NUM];
18 extern tPCB * my_current_task;
19 extern volatile int my_need_sched; //程序是否需要排程的標誌
20 volatile int time_count = 0;
21
22 /*
23 * Called by timer interrupt.
24 * it runs in the name of current running process,
25 * so it use kernel stack of current running process
26 */
27 void my_timer_handler(void)
28 {
29 #if 1
30 if(time_count%1000 == 0 && my_need_sched != 1)
31 {
32 printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
33 my_need_sched = 1;
34 }
35 time_count ++ ;
36 #endif
37 return;
38 }
39
40 void my_schedule(void)
41 {
42 tPCB * next;
43 tPCB * prev;
44
45 if(my_current_task == NULL
46 || my_current_task->next == NULL)
47 {
48 return;
49 }
50 printk(KERN_NOTICE ">>>my_schedule<<<\n");
51 /* schedule */
52 next = my_current_task->next;
53 prev = my_current_task;
54 if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
55 {
56 /* switch to next process */
57 asm volatile(
58 "pushl %%ebp\n\t"
59 "movl %%esp,%0\n\t" /*將程序的task[pid].thread.sp,就是esp賦給esp暫存器*/
60 "movl %2,%%esp\n\t"
61 "movl $1f,%1\n\t"
62 "pushl %3\n\t" /*ebp入棧:因為在這裡棧為空,esp==ebp,所以push的%3就是esp就是ebp*/
63 "ret\n\t" /*把程序入口task[pid].thread.ip賦給eip,即從這之後0號程序啟動*/
64 "1:\t" /* next process start here */
65 "popl %%ebp\n\t"
66 : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
67 : "m" (next->thread.sp),"m" (next->thread.ip)
68 );
69 my_current_task = next;
70 printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
71 }
72 else
73 {
74 next->state = 0;
75 my_current_task = next;
76 printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
77 /* switch to new process */
78 asm volatile(
79 "pushl %%ebp\n\t" /* save ebp */
80 "movl %%esp,%0\n\t" /* save esp */
81 "movl %2,%%esp\n\t" /* restore esp */
82 "movl %2,%%ebp\n\t" /* restore ebp */
83 "movl $1f,%1\n\t" /* save eip */
84 "pushl %3\n\t"
85 "ret\n\t" /* restore eip */
86 : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
87 : "m" (next->thread.sp),"m" (next->thread.ip)
88 );
89 }
90 return;
91 }
mypcb.h
1 /*
2 * linux/mykernel/mypcb.h
3 *
4 * Kernel internal PCB types
5 *
6 * Copyright (C) 2013 Mengning
7 *
8 */
9
10 #define MAX_TASK_NUM 4
11 #define KERNEL_STACK_SIZE 1024*8
12
13 /* CPU-specific state of this task */
14 struct Thread {
15 unsigned long ip;
16 unsigned long sp; /*儲存ip和sp*/
17 };
18
19 typedef struct PCB{
20 int pid; /*程序id*/
21 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped 程序狀態*/
22 char stack[KERNEL_STACK_SIZE]; /*程序堆疊*/
23 /* CPU-specific state of this task */
24 struct Thread thread;
25 unsigned long task_entry;
26 struct PCB *next;
27 }tPCB;
28
29 void my_schedule(void);
四、總結
本週的學習讓我瞭解了程序的切換機制,在時間片輪轉或者其他方式下,程序之間形成一個連結串列,一個程序執行到一定時間或者達到一定條件的時候,就切換成下一個程式執行,程序切換伴隨著堆疊切換。
程序是一個獨立的可排程的活動,不但包括程式的指令和資料,而且包括程式計數器和CPU的所有暫存器以及儲存臨時資料的程序堆疊。所以,正在執行的程序包括處理器當前的一切活動。程序既可以在使用者態下執行,也能在核心下執行,只是核心提供了一些使用者態沒有的核心服務,因此程序在訪問這些服務時會產生中斷,必須進行使用者態與核心態的切換。