第一部分 学习笔记
- 计算机的“三大法宝”:程序存储计算机、函数调用堆栈以及中断机制。
- 堆栈的作用是:记录函数调用框架、传递函数参数、保存返回值地址、提供函数内部局部变量的存储空间。
- 堆栈相关的寄存器:
ESP:堆栈指针,指向堆栈栈顶
EBP:基址指针,指向堆栈栈底 - 堆栈操作
push: 栈顶地址减少4个字节,将操作数放入栈顶存储单元
pop :将操作数从栈顶存储单元移出,栈顶地址增加4个字节 - 其他关键寄存器
CS:EIP 总是指向下一条指令地址。CS是代码段寄存器, EIP是指向下一条指令的地址。 - C语言中内嵌汇编语言的写法
asm volatile (
汇编语句模版;
输出部分;
输入部分;
破坏描述部分;
);
- 内嵌汇编语言的写法举例分析
#include
int main()
{
unsigned int val1 = 1;
unsigned int val2 = 2;
unsigned int val3 = 0;
pritnf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3);
asm volatile(
"movl $0,%%eax\n\t"
/* 将eax寄存器清零 */
"addl %1,%%eax\n\t"
/* %1 是指下面的输入输出部分,从0开始编号,所以%1指的是val1*/
/* 这条语句的就是就是将ecx中存储的val1的值与eax寄存器中的值相加,结果为1*/
"addl %2,%%eax\n\t"
/* %2 是指val2存在edx寄存器中*/
/*这条语句就是将val2与寄存器eax中的值相加,放回eax中*/
"movl %%eax,%0\n\t"
/* val1+val2的值写入到%0中去,也就是val3*/
/*输出部分 */
:"=m"(val3)
/* =m”代表内存变量,m就是memory,也就是直接把变量写到内存val3中*/
/*输入部分 */
:"c"(vall),"d"(val2)
/* c代表%二次项,d![](https://img2018.cnblogs.com/blog/1800798/201909/1800798-20190928193228032-1611440033.png)
代表%edx,就是使用这一存储器存储相应变量的值*/
);
pritnf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3);
return 0;
}
- asm 是GCC的关键字asm的宏定义,是内嵌汇编的关键字。
- _volatile_是GCC的关键字,告诉编译器不要优化代码,汇编指令保留原样。
- %作为转义字符,寄存器前面会多一个转义符号
- %加一个数字代表输入、输入和破坏描述的编号。
第二部分 实验报告
mymain.c一直在循环输出“my_start_kernel here”这句代码,而myinterrupt.c中的my_timer_hardler函数将上述函数打断,而执行自己的代码。
- 第二步:将mykernel操作系统的代码进行扩展:
1.添加mypcb.h头文件,用来定义进程控制块
//mypcb.h
#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
struct Thread {
unsigned long ip;
unsigned long sp;
};
typedef struct PCB{
int pid; //进程的编号
volatile long state; //进程的状态
char stack[KERNEL_STACK_SIZE];
struct Thread thread;
unsigned long task_entry; //进程的起始入口地址
struct PCB *next; //进程用链表连接起来
}tPCB;
void my_schedule(void); // 进程调度器
2.修改mymain.c,作为内核代码的入口,负责初始化内核的各个组成部分
//mymain.c
#include #include
#include
#include
#include
#include
#include "mypcb.h"
tPCB task[MAX_TASK_NUM];//定义4个进程
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;
void my_process(void); //每10000000 来进行进程调度,调用my_schedule
void __init my_start_kernel(void)
{
int pid = 0;
int i;
task[pid].pid = pid; //初始化0号进程
task[pid].state = 0;
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;//0号进程的ip和入口地址设为my_process();
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].next = &task[pid]; //next指针指向自己
for(i=1;ipid);
if(my_need_sched == 1)
{
my_need_sched = 0;
my_shcedule();
}
printk(KERN_NOTICE"this is process %d +\n",my_current_task->pid);
}
}
}
3.修改myinterrupt.c,增加进程切换代码,模拟基于时间片轮转的多道程序。
#include
#include
#include
#include
#include "mypcb.h"
extern tPCB task[MAX_TASK_NUM];
extern *tPCB my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;
void my_timer_handler(void)
{
#if 1
if(time_count%1000 == 0 && my_need_sched != 1)
{
printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
my_need_sched = 1;
}
time_count ++ ;
#endif
return;
}
void my_schedule(void)
{
tPCB * next;
tPCB * prev;
if(next->state == 0) //下一个进程可运行,执行进程切换
{
/* switch to next process */
asm volatile(
"pushl %%ebp\n\t"
"movl %%esp,%0\n\t" /
"movl %2,%%esp\n\t"
"movl $1f,%1\n\t"
"pushl %3\n\t"
"ret\n\t"
"1:\t"
"popl %%ebp\n\t"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
else
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
/* switch to new process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl %2,%%ebp\n\t" /* restore ebp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
return;
}
修改后,make 进行重新编译。