linux内核视频 网易,网易云课堂linux内核分析(二)

前言

这是第二周的报告。本周的实验是:完成一个简单的时间片轮转多道程序内核代码,代码见视频中或从mykernel找。

老师已经为我们搭好了实验的环境——linux3.9.4下一个极其迷你的系统。我们不用去关心别的东西,只需要知道这个迷你系统从my_start_kernel函数开始,系统时钟中断会执行my_timer_handler函数。剩下的留给我们自己发挥。同时,实验要写的代码已经给出,所以完成这个实验的难度不大。实验的关键是理解所给的代码为什么要这么写,也就是理解程序如何切换。

代码与分析

mypcb.h定义进程的属性和信息。

#define MAX_TASK_NUM 10 //这个系统最多十个进程

#define KERNEL_STACK_SIZE 1024*8 //每个进程的栈的大小

//进程的各种状态

#define MY_RUNNING 1

#define MY_SLEEP 2

#define MY_DEAD 3

//用于进程调度时,保存它栈地址和代码地址

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 entry; //进程第一次执行开始的地方

struct PCB *next; //用于构造进程链表

unsigned long priority; //暂时没用到

}tPCB;

void my_schedule(void);

总的来说my_start_kernel就是创建各个进程,并且进入0号进程。

#include "mypcb.h" //PCB结构信息

tPCB my_task[MAX_TASK_NUM]; //创建若干个PCB。也就是创建若干个任务

tPCB *my_current = NULL; //用来表示当前任务的指针。

//是否要进行程序切换的标记。1为需要,0相反。

//这个变量由my_timer_handler和my_process修改

extern volatile int my_need_sched;

//创建的进程都执行这个函数

void my_process(void)

{

unsigned count = 0;

unsigned slice = sizeof(count);

while (1)

{

count += 1;

//程序执行一定时间后检查是否要进行程序切换,不需要则输出信息

if (!(count<

{

if (my_need_sched == 1)

{

my_need_sched = 0;

my_schedule();

}

else

{

printk(KERN_NOTICE "process %d is running\n", my_current->pid);

}

}

}

}

//迷你系统从这个函数开始执行。

void __init my_start_kernel(void)

{

int i;

/* init task 0 */

//初始化第一个进程,0号

my_task[0].pid = 0;

my_task[0].state = MY_RUNNING;

my_task[0].thread.sp = (unsigned long)&my_task[0].stac[KERNEL_STACK_SIZE-1];

//设置程序的入口,也就是my_process的地址

my_task[0].entry = my_task[0].thread.ip = (unsigned long)my_process;

//环形链表

my_task[0].next = &my_task[0];

/* then init other "processes" */

//初始化其他进程。

for (i = 1; i < MAX_TASK_NUM; i++)

{

memcpy(&my_task[i], &my_task[0], sizeof(tPCB));

my_task[i].pid = i;

my_task[i].state = MY_SLEEP; //只有0号醒着,其它都睡着了

my_task[i].thread.sp += (unsigned long)sizeof(tPCB);

/* to make the list a big loop */

//环形,链表最后一个元素指向第一个元素

my_task[i].next = my_task[i-1].next;

my_task[i-1].next = &my_task[i];

}

/* going to switch to task 0! */

printk(KERN_NOTICE "main going to switch to task 0\n");

//好紧张,要开始切换了。

my_current = &my_task[0];

asm volatile(

"movl %1, %%esp\n\t" //将esp和ebp设置为任务0的栈

"movl %1, %%ebp\n\t"

"pushl %0\n\t" //不能直接修改eip,所以先将任务0的地址入栈

"ret\n\t" //再通过ret赋值给eip

:

:"c"(my_task[0].thread.ip), "d"(my_task[0].thread.sp)

);

}

myinterupt.c实现了时钟中断处理和程序调度。

//记录时钟中断了多少次

volatile unsigned long time_count = 0;

volatile int my_need_sched = 0;

//当前进程的PCB

extern tPCB *my_current;

/*

* Called by timer interrupt.

*/

//每次时钟中断都会执行它

void my_timer_handler(void)

{

time_count += 1;

//如果若干次中断后需要程序调度,修改my_need_sched。

if ((!(time_count<

{

my_need_sched = 1; //my_process会检查它是否为1,若是,就执行下面的调度程序(程序主动调度)

}

}

//

void my_schedule(void)

{

tPCB *next = my_current->next; //将要执行的进程

tPCB *pre = my_current;

//错误检查

if (!next)

{

printk(KERN_NOTICE "switch to NULL\n");

while (1);

}

printk(KERN_NOTICE "task %d is going to task %d\n", my_current->pid, next->pid);

my_current = next;

if (next->state == MY_RUNNING)

{

//如果要切换执行的程序已经醒了

asm volatile (

"pushl %%ebp\n\t" //保存当前ebp,esp,eip

"movl %%esp, %0\n\t"

"movl $1f, %1\n\t"

"movl %2, %%esp\n\t" //切换到将要运行的栈

"pushl %3\n\t" //不能直接修改eip,所以通过给压栈再ret方式赋值

"ret\n\t" //切换eip。现在已经是另外一个进程了

"1:\n\t" //切换后从这里开始

"popl %%ebp\n\t" //恢复这个进程被切换之前的ebp

:"=m"(pre->thread.sp), "=m"(pre->thread.ip)

:"m"(next->thread.sp), "m"(next->thread.ip)

);

}

else if (next->state == MY_SLEEP)

{

//如果要切换执行的程序还没有运行过。过程和上面的切换差不多

next->state = MY_RUNNING; //先叫醒它

asm volatile (

"pushl %%ebp\n\t"

"movl %%esp, %0\n\t"

"movl $1f, %1\n\t"

"movl %2, %%esp\n\t"

"movl %2, %%ebp\n\t //新进程的栈是空的,所以要设置ebp

"pushl %3\n\t"

"ret\n\t"

"1:\n\t" //被切换的进程下次执行从这里开始,而刚被叫醒的进程从my_process开始。

"popl %%ebp\n\t" //这个被切换的进程需要恢复ebp

:"=m"(pre->thread.sp), "=m"(pre->thread.ip)

: "m"(next->thread.sp), "m"(next->thread.ip)

);

}

}

结果

进程从0到9,又从9到0循环执行。

bVtfEm

总结

了解了进程如何进行初始化和切换:实质就是寄存器和栈的切换。

问题:切换不需要保存当前的eax等通用寄存器吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值