操作系统是如何工作的
《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的所有寄存器以及存储临时数据的进程堆栈。所以,正在执行的进程包括处理器当前的一切活动。进程既可以在用户态下运行,也能在内核下运行,只是内核提供了一些用户态没有的核心服务,因此进程在访问这些服务时会产生中断,必须进行用户态与内核态的切换。