linux 用户线程 切换,Linux 用户线程切换分析

Linux内核利用esp ebp实现用户栈的切换,并通过eip的跳转来实现用户级线程的切换,简单分析如下首先定义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中 __init my_start_kernel函数用来初始化我们的第0号线程,并fork了1 2 3号线程,void my_process(void)函数用来实现线程的调度工作

/*

* linux/mykernel/mymain.c

*

* Kernel internal my_start_kernel

*

* Copyright (C) 2013 Mengning

*

*/

#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;/* set entry */

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

/* 循环链表,将0 1 2 3线程连接起来 */

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) /* need schedule */

{

my_need_sched = 0;

   my_schedule();

}

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

}

}

}

关键的myinterrupt.c文件,我们定义了一个时钟中段,每1000毫秒发出一个时钟中段并根据my_need_sched来决定是否需要进行线程的切换,关键函数在于void my_schedule(void),esp ebp的切换详见注释部分

/*

* linux/mykernel/myinterrupt.c

*

* Kernel internal my_timer_handler

*

* Copyright (C) 2013 Mengning

*

*/

#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)/* 每1000毫秒发出一个时钟中段并根据my_need_sched来决定是否需要进行线程的切换 */

{

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 */

{

/*下个线程的状态为runable,当前线程让出cpu,进行线程的切换 */

asm volatile(

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

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

"movl %2,%%esp\n\t" /* restore next thread esp ,恢复下个线程的栈状态*/

"movl $1f,%1\n\t" /* $1f就是指线程标号1:的代码在内存中存储的地址 */

"pushl %3\n\t" /* save next eip */

"ret\n\t" /* restore eip,配合上条指令,模拟了1个函数的返回动作,把eip弹出,实现线程的切换 */

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

"popl %%ebp\n\t" /*next线程时间片轮转完毕,现在轮到我执行了,恢复我的栈*/

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

{

/*设置下个线程状态为runable*/

next->state = 0;

/*当前task指向下个线程*/

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;

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值