第三周学习内容
庖丁解牛Linux内核分析第二章:操作系统是如何工作的
Linux内核分析实验二
学到的一些知识
-
计算机的三大法宝:存储程序计算机,函数调用堆栈,中断
-
堆栈是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间,堆栈具体的作用有:记录函数调用框架,传递函数参数,保存返回值的地址,提供函数内部局部变量的存储空间等
-
与堆栈相关的寄存器:ESP,EBP
-
堆栈操作:push,pop
-
CS:EIP总是指向下一条的指令地址
- 顺序执行:总是指向地址连续的下一条指令
- 跳转/分支:执行这样的指令时,CS:EIP的值会根据程序需要被修改
- call:将当前的CS:EIP的值压入栈顶,CS:EIP指向被调用函数的入口地址
- ret:从栈顶弹出原来保存在这里的CS:EIP的值,放入CS:EIP中
-
如果两个机器的处理器指令集不同,汇编出来的汇编代码也会有所不同
实验内容
1.虚拟一个x86的CPU硬件平台
在实验楼的环境中打开shell,输入两行代码即可启动内核:
$ cd ~/LinuxKernel/linux-3.9.4
$ qemu -kernel arch/x86/boot/bzImage
内核启动后如图:
进入mykernel查看mymain.c和myinterrupt.c,如图所示:
2.在mykernel基础上构造一个简单地操作系统内核
增加一个mypcb.h的头文件
#define MAX_TASK_NUM 10 // 最大进程数
#define KERNEL_STACK_SIZE 1024*8
#define PRIORITY_MAX 30 //从0到30的优先级
/* CPU-specific state of this task */
struct Thread {
unsigned long ip;//point to cpu run address
unsigned long sp;//point to the thread stack's top address
//todo add other attrubte of system thread
};
//PCB Struct
typedef struct PCB{
int pid; // pcb id
volatile long state; /* -1 不运行, 0 运行, >0 停止 */
char stack[KERNEL_STACK_SIZE];// each pcb stack size is 1024*8
/* CPU-specific state of this task */
struct Thread thread;
unsigned long task_entry;//the task execute entry memory address
struct PCB *next;//pcb is a circular linked list
unsigned long priority;// task priority ////////
//todo add other attrubte of process control block
}tPCB;
//void my_schedule(int pid);
void my_schedule(void);
修改mymain.c
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/smp.h>
#endif
#include "mypcb.h"
tPCB task[MAX_TASK_NUM];
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;
void my_process(void);
unsigned long get_rand(int );
void sand_priority(void)
{
int i;
for(i=0;i<MAX_TASK_NUM;i++)
task[i].priority=get_rand(PRIORITY_MAX);
}
void __init my_start_kernel(void)
{
int pid = 0;
/* Initialize process 0*/
task[pid].pid = pid;
task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
// set task 0 execute entry address to my_process
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].next = &task[pid];
/*fork more process */
for(pid=1;pid<MAX_TASK_NUM;pid++)
{
memcpy(&task[pid],&task[0],sizeof(tPCB));
task[pid].pid = pid;
task[pid].state = -1;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority
}
task[MAX_TASK_NUM-1].next=&task[0];
printk(KERN_NOTICE "
system begin :>>>process 0 running!!!<<<
");
/* start process 0 by task[0] */
pid = 0;
my_current_task = &task[pid];
asm volatile(
"movl %1,%%esp
" /* set task[pid].thread.sp to esp */
"pushl %1
" /* push ebp */
"pushl %0
" /* push task[pid].thread.ip */
"ret
" /* pop task[pid].thread.ip to eip */
"popl %%ebp
"
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
}
void my_process(void)
{
int i = 0;
while(1)
{
i++;
if(i%10000000 == 0)
{
if(my_need_sched == 1)
{
my_need_sched = 0;
sand_priority();
my_schedule();
}
}
}
}//end of my_process
//produce a random priority to a task
unsigned long get_rand(max)
{
unsigned long a;
unsigned long umax;
umax=(unsigned long)max;
get_random_bytes(&a, sizeof(unsigned long ));
a=(a+umax)%umax;
return a;
}
修改myinterrupt.c
#include "mypcb.h"
#define CREATE_TRACE_POINTS
#include <trace/events/timer.h>
extern tPCB task[MAX_TASK_NUM];
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;
/*
* Called by timer interrupt.
* it runs in the name of current running process,
* so it use kernel stack of current running process
*/
void my_timer_handler(void)
{
#if 1
// make sure need schedule after system circle 2000 times.
if(time_count%2000 == 0 && my_need_sched != 1)
{
my_need_sched = 1;
//time_count=0;
}
time_count ++ ;
#endif
return;
}
void all_task_print(void);
tPCB * get_next(void)
{
int pid,i;
tPCB * point=NULL;
tPCB * hig_pri=NULL;//points to the the hightest task
all_task_print();
hig_pri=my_current_task;
for(i=0;i<MAX_TASK_NUM;i++)
if(task[i].priority<hig_pri->priority)
hig_pri=&task[i];
printk("higst process is:%d priority is:%d
",hig_pri->pid,hig_pri->priority);
return hig_pri;
}//end of priority_schedule
void my_schedule(void)
{
tPCB * next;
tPCB * prev;
// if there no task running or only a task ,it shouldn't need schedule
if(my_current_task == NULL
|| my_current_task->next == NULL)
{
printk(KERN_NOTICE "time out!!!,but no more than 2 task,need not schedule
");
return;
}
/* schedule */
next = get_next();
prev = my_current_task;
printk(KERN_NOTICE "the next task is %d priority is %u
",next->pid,next->priority);
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
{//save current scene
/* switch to next process */
asm volatile(
"pushl %%ebp
" /* save ebp */
"movl %%esp,%0
" /* save esp */
"movl %2,%%esp
" /* restore esp */
"movl $1f,%1
" /* save eip */
"pushl %3
"
"ret
" /* restore eip */
"1: " /* next process start here */
"popl %%ebp
"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;//switch to the next task
printk(KERN_NOTICE "switch from %d process to %d process
>>>process %d running!!!<<<
",prev->pid,next->pid,next->pid);
}
else
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE "switch from %d process to %d process
>>>process %d running!!!<<<
",prev->pid,next->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;
}//end of my_schedule
void all_task_print(void)
{
int i,cnum=62;//
printk(KERN_NOTICE "
current task is:%d all task in OS are:
",my_current_task->pid);
printk("");
for(i=0;i<cnum;i++)
printk("-");
printk("
| process:");
for(i=0;i< MAX_TASK_NUM;i++)
printk("| %2d ",i);
printk("|
| priority:");
for(i=0;i<MAX_TASK_NUM;i++)
printk("| %2d ",task[i].priority);
printk("|
");
for(i=0;i<cnum;i++)
printk("-");
printk("
");
}
重新编译,输入以下代码:
$ cd ~/LinuxKernel/linux-3.9.4
$ make
$ qemu -kernel arch/x86/boot/bzImage
结果如图所示