Linux 进程切换

邵帅 原创作品 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 

首先是mypcb.h头文件,这个文件定义了进程控制块

#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
struct Thread//定义线程结构体
{
	unsigned long ip;//eip寄存器,cpu下一次要执行的指令的地址
	unsigned long sp;//esp寄存器,保存的是栈顶的地址
};
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);//调度程序

然后是mymain.c文件,在这个文件中有 my_start_kernel(void)函数:在这个函数中初始化MAX_TASK_NUM个进程控制块,并将第一个进程块通过嵌入式汇编,把第一个进程压入栈中。 my_process(void)函数中,主要是当i=100时,调用跳转 my_schedule()函数

#include "mypcb.h"
tPCB task[MAX_TASK_NUM];
tPCB *my_current_task = NULL;
volatile int my_need_sched = 0;
void my_prosses(void);

void __init my_start_kernel(void)//初始化函数
{
	int pid = 0;//第一个进程
	int i;
	task[pid].pid = pid;//第一个进程id为0
	task[pid].state = 0;//状态是0,0代表正在运行,-1代表不在运行
	//程序入口是my_process方法,并把地址保存到eip寄存器中
	task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_prosses;
	//栈顶指针保存在堆栈最后一个位置的地址
	task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE - 1];
  //进程的下一个进程块指向自己
	task[pid].next = &task[pid];
	for(i=1; i<MAX_TASK_NUM; i++)
	{
		memcpy(&task[i], &task[0], sizeof(tPCB));
		task[i].pid = 1;
		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];//将当前进程块指向一个进程
	asm volatile(
		"mov %1 %%esp\n\t"//将task[pid].thread.sp放入栈顶地址的寄存器中
		"pushl %1 \n\t"//将task[pid].thread.sp压栈
		"pushl %0 \n\t"//将task[pid].thread.ip压栈
		"ret \n\t"//栈顶出栈,将值赋给eip,即将%0赋给eip
		"popl %%ebp \n\t"//栈顶出栈,将值赋给ebp,即将%1赋给ebp
		:
		:"c"(task[pid].thread.ip),"d"(task[pid].thread.sp)
	);
}
void my_prosses(void){
	int i = 0;
	while (1) {
		i++;
		if(i % 100 == 0){//当为100时,输出当前进程是哪个
			printk(KERN_NOTICE "this is process %d ---\n", my_current_task);
			if(my_need_sched == 1){//进行调转到其他进程
				my_need_sched = 0;
				my_schedule();
			}//输出调转后进程
			printk(KERN_NOTICE "this is process %d +++\n", my_current_task);
		}
	}
}

最后是my_interrupt.c文件,在这个文件中主要定义个 my_schedule(void)函数,在这个函数中判断这个进程是不是新进程,如果不是则执行函数,如果是新进程则将前后两个进程中的esp,ebp放入当前进程中去。


#include <linux/types.h>
#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;//指向当前进程的pcb指针
extern volatile int my_need_sched;
volatile int time_count = 0;

void my_time_handler(void){
  #if 1//进行计时,当time_count为1000是进行调转
  if(time_count % 1000 == 0 && my_need_sched != 1){
    printk(KERN_NOTICE ">>>my_time_handler_here<<<\n");
    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<<<\n");
  next = my_current_task->next;//
  prev = my_current_task;
  if(next->state == 0){//下一个进程是运行状态时
    asm volatile(//进程上下文切换
      "pushl %%ebp \n\t"//将ebp中内容保存到esp中,压入栈中
      "movl  %%esp, %0 \n\t"//保存esp中内容到prev->thread.sp,即保存到上一个进程栈顶
      "movl  %2, %%esp \n\t"//保存next->thread.sp到esp中
      "movl  $1f, %1 \n\t"//保存eip,$1f是接下来标号1的位置
      "pushl %3 \n\t"//将next->thread.ip保存到栈中
      "ret \n\t"//return栈顶
      "1: \t"//执行函数1
      "popl %%ebp \n\t"//弹出栈到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<<<\n", prev->pid, next->pid);
  }else{//这个进程是新进程
    next->state = 0;
    my_current_task = next;
    printk(KERN_NOTICE ">>>switch %d to %d<<<\n", prev->pid, next->pid);
    /*选择一个新进程*/
    asm volatile(
      "pushl %%ebp \n\t"//保存ebp
      "movl %%esp, %0 \n\t"//保存esp中内容到prev->thread.sp,即保存到上一个进程栈顶
      "movl %2, %%esp \n\t"//保存next->thread.sp到esp中
      "movl %2, %%ebp \n\t"//保存next->thread.sp到ebp中
      "movl $1f, %1 \n\t"//保存eip,$1f是接下来标号1的位置
      "pushl %3 \n\t"//将next->thread.ip保存到栈中
      "ret \n\t"//return栈顶
      :"=m"(prev->thread.sp), "=m"(prev->thread.ip)
      :"m"(next->thread.sp), "m"(next->thread.ip)
    );
  }
  return;
}

总结:

通过学习,进程间的上下文切换主要还是要明白这个是一个堆栈,当调用的时候就依次把下一个进程的esp放入,函数的地址放入,把下一个进程的ip放入,然后在依次出栈,首先将ip出栈,因为马上要执行函数1,而函数一的地址放入的是eip中的,然后执行函数1,最后将这个进程的ebp弹出,因为当前进程的ebp保存下一个进程的esp。这样就完成的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值