linux 内核调度分析,基于一个简单内核mykernel的Linux内核时间调度的分析

第一章 环境

Ubuntu 14.10

Linux内核 3.9.4

第二章 源代码

mypcb.h

#define MAX_TASK_NUM 4

#define KERNEL_STACK_SIZE 1024*8

/* CPU-specific state of this task */

struct Thread {

unsigned longip;

unsigned longsp;

};

typedef struct PCB{

int pid;

volatile long state;/* -1 unrunnable, 0 runnable, >0 stopped */

char stack[KERNEL_STACK_SIZE];

/* CPU-specific state of this task */

struct Thread thread;

unsigned longtask_entry;

struct PCB *next;

}tPCB;

void my_schedule(void);

mymain.c

#include

#include

#include

#include

#include

#include "mypcb.h"

tPCB task[MAX_TASK_NUM];

tPCB * my_current_task = NULL;

volatile int my_need_sched = 0;

void my_process(void);

void __init my_start_kernel(void)

{

int pid = 0;

int i;

/* Initialize process 0*/

task[pid].pid = pid;

task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */

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(i=1;i

{

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];

}

/* start process 0 by task[0] */

pid = 0;

my_current_task = &task[pid];

asm volatile(

"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */

"pushl %1\n\t" /* push ebp */

"pushl %0\n\t" /* push task[pid].thread.ip */

"ret\n\t" /* pop task[pid].thread.ip to eip */

"popl %%ebp\n\t"

:

: "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)

{

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

if(my_need_sched == 1)

{

my_need_sched = 0;

my_schedule();

}

printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);

}

}

}

myinterrupt.c

#include

#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;

/*

* 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

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(my_current_task == NULL

|| my_current_task->next == NULL)

{

return;

}

printk(KERN_NOTICE ">>>my_schedule<<

/* schedule */

next = my_current_task->next;

prev = my_current_task;

if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */

{

/* switch to next process */

asm volatile(

"pushl %%ebp\n\t" /* save ebp */

"movl %%esp,%0\n\t" /* save esp */

"movl %2,%%esp\n\t" /* restore esp */

"movl $1f,%1\n\t" /* save eip */

"pushl %3\n\t"

"ret\n\t" /* restore eip */

"1:\t" /* next process start here */

"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<<pid,next->pid);

}

else

{

next->state = 0;

my_current_task = next;

printk(KERN_NOTICE ">>>switch %d to %d<<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;

}

第三章 调试

sudo apt-get install qemu # install QEMU

sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu

wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.9.4.tar.xz

wget https://raw.github.com/mengning/mykernel/master/mykernel_for_linux3.9.4sc.patch

xz -d linux-3.9.4.tar.xz

tar -xvf linux-3.9.4.tar

cd linux-3.9.4

patch -p1 < ../mykernel_for_linux3.9.4sc.patch

make allnoconfig

make

qemu -kernel arch/x86/boot/bzImage

0818b9ca8b590ca3270a3433284dd417.png

第四章 分析

在头文件mypcb.h中定义了一个结构体Thread来存储进程在CPU的状态,而PCB中,一个非常重要的是定义了一个栈,然后用Thread来表示这个进程的运行状态是如何的。然后这是一个链表,不过,根据MAX_TASK_NUM,本实例最多四个进程。

接着来分析mymain.c

首先设置4个进程,假设为A,B,C,D,然后分别设置pid、state、task_entry、thread.ip、thread.sp等信息。其中根据注释,state表示进程的运行状态。然后通过链表将其连接起来。

就如:

0818b9ca8b590ca3270a3433284dd417.png

接着下面的for循环便是快速生成其他三个进程。

然后的my_current_task是记录现在正在运行的进程,接着对task[pid]的ip和sp进行了调换。

那么,怎么进行进程切换呢?

那就是my_process函数了,i相当于计数器一样,当i满足条件后,先打印信息。然后判断是否需要改变信息,也就是my_need_sched是否等于1,如果是就执行my_shedule函数。然后返回更改my_current_task的指向,于是完成了切换。

接着分析my_shedule函数:

首先定义可以指向进程的两个指针*next,*prev。为了防止出错,先判断my_current_task是否指向了一个进程,并没有在进程的末尾,如果是一个野指针或者后面没有进程链表的元素,那么就退出。

接着执行汇编代码,如果后面的进程是活动状态,即state=0,交换两个进程的活动情况。如果不是活动状态,那就先将后面的进程设置成活动的,再进行交换。

在目前的状态下,我们无法执行my_task_handler。我想,他可能是想表示关于时间处理的函数,就是多长时间CPU对进程执行交换,然后中断。如果添加#define 1,并在my_shedule函数中加入调用语句,便可实现CPU的中断处理了。

流程图:

my_main.c

0818b9ca8b590ca3270a3433284dd417.png

my_interrupt.c

0818b9ca8b590ca3270a3433284dd417.png

附录

卢晅 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值