linux内核进程调度分析总结
2016-03-06 17:29
549 查看
作者:王鹤楼
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
在孟老师给出的mykernel的基础上增加mypcb.h
mymain.c文件:
myinterrupt.c
make 重新编译
运行程序:
qemu -kernel arch/x86/boot/bzImage
运行效果图
![](http://img.blog.csdn.net/20160306154154693)
![](http://img.blog.csdn.net/20160306155806403)
总结:
进程切换的关键是保存现场和恢复现场,当切换到下一个进程时要把当前进程用的变量,栈,和相关的寄存器保存到栈中,这一步叫保存现场。当切换回这个进程时再把这些变量,寄存器恢复,这一步叫恢复现场。
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
在孟老师给出的mykernel的基础上增加mypcb.h
#define MAX_TASK_NUM 4 //定义总有4个任务/进程 #define KERNEL_STACK_SIZE 1024*8 //每个进程的堆栈大小为8KB /*用于保有存当前进程的esp和eip*/ struct Thread { unsigned long ip; //eip unsigned long sp; //esp }; /*定义进程结构体*/ typedef struct PCB { int pid;//进程ID volatile long state; //进程状态 char stack[KERNEL_STACK_SIZE];//进程堆栈 struct Thread thread;//保存esp、eip unsigned long task_entry;//f进程入口 struct PCB * next;//在进程链表中指向下一个进程 struct PCB * prev;//在进程链表中指向上一个进程 }tPCB; /*声明调度函数*/ void my_schedule(void);
mymain.c文件:
#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 = 0; /*初始化0号进程*/ task[pid].pid = pid; task[pid].state = 0; /*进程入口指向进程处理函数*/ task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process; /*指向进程栈进址*/ task[pid].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE -1]; /*进程链表中,next指向指向自己*/ task[pid].next = &task[pid]; /*创建其他进程*/ for(i=1; i< MAX_TASK_NUM; i++) { /*把0号进程的状态拷贝给当前进程,不同成员值再单独赋值*/ 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]; } /* 运行0号进程*/ pid = 0; /*把0号进程的地址赋给当前进程指针*/ my_current_task = &task[pid]; /*嵌入式汇编中%0,%1,%2,%3表示下面的参数,编号从0开始*/ asm volatile( "movl %1, %%esp\n\t" /*把当前进程的栈指针赋给esp*/ "pushl %1\n\t"/*把当前进程的栈指保存到栈中*/ "pushl %0\n\t"/*保存当前进程的eip到栈中*/ "ret\n\t"/*返回操作*/ "popl %%ebp\n\t"/*弹出栈顶内容赋给ebp*/ : : "c" (task[pid].thread.ip), "d" (task[pid].thread.sp)/*ecx=当前进程的eip, edx=当前进程的栈指针*/ ); //到此为止,0号进程就启动起来了 //end } void my_process(void) { int i=0; while(1) { i++; if(i%100 == 0) { printk(KERN_NOTICE "this is process %d- \n", my_current_task->pid); /*判断是否需要切换进程,1需要切换,0不需要切换*/ 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 "mypcb.h" /*引入外部变量*/ extern tPCB task[MAX_TASK_NUM]; extern tPCB * my_current_task; extern volatile int my_need_sched; volatile int time_count = 0; /* * 定时器中断处理函数 */ void my_timer_handler(void) { /*判断是否需要切换进程*/ if(time_count % 100 == 0 && my_need_sched != 1) { printk(KERN_NOTICE ">>>my_timer_handler here<<<\n"); /*将进程切换标记为需要切换*/ my_need_sched = 1; } /* 计数器自加 */ time_count ++; 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; //state:-1表示不可用,0表示正在执行,>0表示没有运行 if(next->state == 0) { /*切换进程*/ asm volatile ( "pushl %%ebp\n\t" /*保存当前进程的ebp*/ "movl %%esp, %0\n\t" /*保存当前的栈指针到上一个进程的sp中*/ "movl %2, %%esp\n\t" /*把下一个进程的sp赋给esp*/ "movl $1f, %1\n\t" /*保存当前进程的eip到上进一个进程的ip中*/ "pushl %3\n\t" /*将下一个进程的ip压到栈中*/ "ret\n\t"/*返回*/ "1:\t" "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<<<\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" "movl %%esp, %0\n\t" "movl %2, %%esp\n\t" "movl %2, %%ebp\n\t" "movl $1f, %1\n\t" "pushl %3\n\t" "ret\n\t" "popl %%ebp\n\t" :"=m" (prev->thread.sp), "=m" (prev->thread.ip) :"m" (next->thread.sp), "m" (next->thread.ip) ); } }
make 重新编译
运行程序:
qemu -kernel arch/x86/boot/bzImage
运行效果图
总结:
进程切换的关键是保存现场和恢复现场,当切换到下一个进程时要把当前进程用的变量,栈,和相关的寄存器保存到栈中,这一步叫保存现场。当切换回这个进程时再把这些变量,寄存器恢复,这一步叫恢复现场。
相关文章推荐
- linux中的内置命令与外部命令
- 《linux内核分析》 第二周
- Linux内核及分析 第二周 操作系统是如何工作的?
- Linux基础--用户与组
- linux基本命令
- linux下myeclipse的破解
- 【E类】Linux视频课程集
- linux下制作u盘启动盘
- Centos更换yum源,安装ssh server
- Linux基础知识
- Linux基础学习笔记
- linux 知识整理1linux 常见的目录
- 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?
- 10 Useful Sar (Sysstat) Examples for UNIX / Linux Performance Monitoring
- Linux进程时间片的分配(调度策略和参数)
- linux 内核驱动开发
- Linux下 RabbitMQ的安装与配置-3
- linux 内核编译
- linux内核分析 第二周
- linux 最常用的安全措施