第一部分 学习笔记
- 计算机的“三大法宝”:程序存储计算机、函数调用堆栈以及中断机制。
- 堆栈的作用是:记录函数调用框架、传递函数参数、保存返回值地址、提供函数内部局部变量的存储空间。
- 堆栈相关的寄存器:
ESP:堆栈指针,指向堆栈栈顶
EBP:基址指针,指向堆栈栈底 - 堆栈操作
push: 栈顶地址减少4个字节,将操作数放入栈顶存储单元
**pop ** :将操作数从栈顶存储单元移出,栈顶地址增加4个字节 - 其他关键寄存器
CS:EIP 总是指向下一条指令地址。CS是代码段寄存器, EIP是指向下一条指令的地址。 - C语言中内嵌汇编语言的写法
asm volatile (
汇编语句模版;
输出部分;
输入部分;
破坏描述部分;
);
- 内嵌汇编语言的写法举例分析
#include <stdio.h>
int main()
{
unsigned int val1 = 1;
unsigned int val2 = 2;
unsigned int val3 = 0;
pritnf("val1:%d,val2:%d,val3:%d
",val1,val2,val3);
asm volatile(
"movl $0,%%eax
"
/* 将eax寄存器清零 */
"addl %1,%%eax
"
/* %1 是指下面的输入输出部分,从0开始编号,所以%1指的是val1*/
/* 这条语句的就是就是将ecx中存储的val1的值与eax寄存器中的值相加,结果为1*/
"addl %2,%%eax
"
/* %2 是指val2存在edx寄存器中*/
/*这条语句就是将val2与寄存器eax中的值相加,放回eax中*/
"movl %%eax,%0
"
/* 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
",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 <linux/types.h>#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>
#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;i<MAX_TASK_NUM;i++) //1,2,3号进程复制0号进程
{
memcpy(&task[i],&task[0],sizeof(tPCB));
task[i].pid = i;
task[i].state = -1;
task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
task[i].next = task[i-1].next;
task[i-1].next = &task[i]; //所有进程成为一个循环链表
}
pid = 0;
my_current_task = &task[pid]; //启动0号进程
asm volatile(
"movl %1,%%esp
" //esp指向stack数组的末尾
"pushl %1
" //将task[0].thread.sp压栈
"pushl %0
" //将task[0].thread.ip压栈
"ret
" //eip指向0进程起始地址,启动0号进程
"popl %%ebp
"//释放栈空间
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)
);
}
void my_process(void)
{
int i = 0;
while(1)
{
i++;
if(i%10000000 == 0)
{
printk(KERN_NOTICE "this is process %d -
",my_current_task->pid);
if(my_need_sched == 1)
{
my_need_sched = 0;
my_shcedule();
}
printk(KERN_NOTICE"this is process %d +
",my_current_task->pid);
}
}
}
3.修改myinterrupt.c,增加进程切换代码,模拟基于时间片轮转的多道程序。
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>
#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<<<
");
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
"
"movl %%esp,%0
" /
"movl %2,%%esp
"
"movl $1f,%1
"
"pushl %3
"
"ret
"
"1: "
"popl %%ebp
"
: "=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<<<
",prev->pid,next->pid);
else
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<
",prev->pid,next->pid);
/* switch to new process */
asm volatile(
"pushl %%ebp
" /* save ebp */
"movl %%esp,%0
" /* save esp */
"movl %2,%%esp
" /* restore esp */
"movl %2,%%ebp
" /* restore ebp */
"movl $1f,%1
" /* save eip */
"pushl %3
"
"ret
" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
return;
}
修改后,make 进行重新编译。