注:袁帅+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
linux内核分析的第二课:完成一个简单的时间片轮转多道程序内核代码
使用下面的程序来进行分析,在实验楼的环境下操作:
mypcb.h:
#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
-
structTread{
-
unsignedlong eip;
-
unsignedlong esp;
-
};
-
-
typedef struct PCB{
-
long pid;
-
long state;
-
char stack[KERNEL_STACK_SIZE];
-
long task_entry;
-
structTread thread;
-
struct PCB*next;
-
}PCB_t;
-
-
void my_schedule(void);
mymain.c:
#include<linux/types.h>
-
#include<linux/module.h>
-
#include<linux/proc_fs.h>
-
#include<linux/kernel.h>
-
#include<linux/syscalls.h>
-
#include<linux/stackprotector.h>
-
#include<linux/string.h>
-
#include<linux/ctype.h>
-
#include<linux/delay.h>
-
#include<linux/ioport.h>
-
#include<linux/init.h>
-
#include<linux/initrd.h>
-
#include<linux/bootmem.h>
-
#include<linux/acpi.h>
-
#include<linux/tty.h>
-
#include<linux/percpu.h>
-
#include<linux/kmod.h>
-
#include<linux/vmalloc.h>
-
#include<linux/kernel_stat.h>
-
#include<linux/start_kernel.h>
-
#include<linux/security.h>
-
#include<linux/smp.h>
-
#include<linux/profile.h>
-
#include<linux/rcupdate.h>
-
#include<linux/moduleparam.h>
-
#include<linux/kallsyms.h>
-
#include<linux/writeback.h>
-
#include<linux/cpu.h>
-
#include<linux/cpuset.h>
-
#include<linux/cgroup.h>
-
#include<linux/efi.h>
-
#include<linux/tick.h>
-
#include<linux/interrupt.h>
-
#include<linux/taskstats_kern.h>
-
#include<linux/delayacct.h>
-
#include<linux/unistd.h>
-
#include<linux/rmap.h>
-
#include<linux/mempolicy.h>
-
#include<linux/key.h>
-
#include<linux/buffer_head.h>
-
#include<linux/page_cgroup.h>
-
#include<linux/debug_locks.h>
-
#include<linux/debugobjects.h>
-
#include<linux/lockdep.h>
-
#include<linux/kmemleak.h>
-
#include<linux/pid_namespace.h>
-
#include<linux/device.h>
-
#include<linux/kthread.h>
-
#include<linux/sched.h>
-
#include<linux/signal.h>
-
#include<linux/idr.h>
-
#include<linux/kgdb.h>
-
#include<linux/ftrace.h>
-
#include<linux/async.h>
-
#include<linux/kmemcheck.h>
-
#include<linux/sfi.h>
-
#include<linux/shmem_fs.h>
-
#include<linux/slab.h>
-
#include<linux/perf_event.h>
-
#include<linux/file.h>
-
#include<linux/ptrace.h>
-
#include<linux/blkdev.h>
-
#include<linux/elevator.h>
-
-
#include<asm/io.h>
-
#include<asm/bugs.h>
-
#include<asm/setup.h>
-
#include<asm/sections.h>
-
#include<asm/cacheflush.h>
-
-
#include"mypcb.h"
-
-
#ifdef CONFIG_X86_LOCAL_APIC
-
#include<asm/smp.h>
-
#endif
-
-
PCB_t task[MAX_TASK_NUM];
-
PCB_t* my_current = NULL;
-
int my_need_schedule=0;
-
void my_process(void);
/*my_start_kernel首先初始化任务0的PCB,然后在初始化其他任务的PCB*/
-
void __init my_start_kernel(void)
-
{
-
int pid =0;
-
/* initail task 0*/
-
task[0].pid=0;
-
task[0].state=0;
-
task[0].task_entry= task[0].thread.eip=(unsignedlong)my_process;
-
task[0].thread.esp=(unsignedlong)&task[0].stack;
-
task[0].next=&task[0];
-
/*fork task*/
-
int i;
-
for(i=1;i<MAX_TASK_NUM;i++){
-
memcpy(&task[i],&task[0],sizeof(task[0]));
-
task[i].pid= i;
-
task[i].state=-1;
-
task[i].thread.esp=(unsignedlong)&task[i].stack;
-
task[i].next= task[i-1].next;
-
task[i-1].next=&task[i];
-
}
-
pid=0;
-
my_current=&task[0];
- /*这段内嵌汇编代码很重要,是用来启动任务0的*/
-
asmvolatile(
-
"movl %1,%%esp\n\t"//这里恢复任务0的内核栈
-
"pushl %1\n\t" //保存任务0的内核栈
-
"pushl %0\n\t" //把EIP入栈
-
"ret\n\t" //执行完这条指令,eip寄存器的值就执行了任务0的入口(即my_process)
-
"popl %%ebp\n\t" //正常情况下,是不会运行到这里的
-
:
-
:"c"(task[0].thread.eip),"d"(task[0].thread.esp)
-
:
-
);
-
}
/*每执行10000000次判断一下是否需要调度*/
-
void my_process(void)
-
{
-
int i=0;
-
while(1){
-
i++;
-
if(i%10000000==0){
-
i=0;
-
printk("this is process %ld -\n",my_current->pid);
-
if(my_need_schedule==1){
-
my_need_schedule=0;
-
my_schedule();
-
}
-
printk("this is process %ld +\n",my_current->pid);
-
}
-
}
-
}
myinterrupt.c:
-
#include<linux/kernel_stat.h>
-
#include<linux/export.h>
-
#include<linux/interrupt.h>
-
#include<linux/percpu.h>
-
#include<linux/init.h>
-
#include<linux/mm.h>
-
#include<linux/swap.h>
-
#include<linux/pid_namespace.h>
-
#include<linux/notifier.h>
-
#include<linux/thread_info.h>
-
#include<linux/time.h>
-
#include<linux/jiffies.h>
-
#include<linux/posix-timers.h>
-
#include<linux/cpu.h>
-
#include<linux/syscalls.h>
-
#include<linux/delay.h>
-
#include<linux/tick.h>
-
#include<linux/kallsyms.h>
-
#include<linux/irq_work.h>
-
#include<linux/sched.h>
-
#include<linux/sched/sysctl.h>
-
#include<linux/slab.h>
-
-
#include<asm/uaccess.h>
-
#include<asm/unistd.h>
-
#include<asm/div64.h>
-
#include<asm/timex.h>
-
#include<asm/io.h>
-
-
#define CREATE_TRACE_POINTS
-
#include<trace/events/timer.h>
-
#include"mypcb.h"
-
/*
-
* Called by timer interrupt.
-
*/
-
volatileint time_counter=0;
-
externint my_need_schedule;
-
externPCB_t* my_current;
- externPCB_t task[MAX_TASK_NUM];
-
-
/*这是时钟中断程序,用来更新进程的时间片*/
-
void my_timer_handler(void)
-
{
-
if((time_counter%1000==0)&&(my_need_schedule==0)){
-
my_need_schedule=1;
-
-
printk(">>>>time_hander here<<<<<\n");
-
}
-
time_counter++;
-
}
-
- /*这是进程调度程序*/
- void my_schedule(void)
-
{
-
PCB_t* next;
-
PCB_t* prev;
-
if(my_current==NULL|| my_current->next==NULL){
-
return;
-
}
-
printk(">>>here my_schedule<<<\n");
-
next= my_current->next;
-
prev= my_current;
-
if(next->state==0){ //如果进程不是首次调度的
-
asmvolatile(
-
"pushl %%ebp\n\t" //保存当前进程的内核栈
-
"movl %%esp,%1\n\t" //保存当前进程内核栈
-
"movl $1f,%0\n\t" //保存当前进程执行位置
-
"movl %3,%%esp\n\t" //恢复即将调度进程的内核栈
-
"pushl %2\n\t" //把即将调度进程的eip压栈
-
"ret\n\t" //把刚才压入的eip弹出到eip寄存器
-
"1:"
-
"popl %%ebp\n\t" //每次调度进程时都从这里开始执行
-
:"=m"(prev->thread.eip),"=m"(prev->thread.esp)
-
:"m"(next->thread.eip),"m"(next->thread.esp)
-
:
-
);
-
my_current= next;
-
printk("switch %ld to %ld\n",prev->pid,next->pid);
-
}
-
else //进程不是首次调度
-
{
-
next->state=0;
-
printk("switch %ld to %ld\n",prev->pid,next->pid);
-
asmvolatile(
-
"pushl %%ebp\n\t" //保存一下ebp
-
"movl %%esp,%1\n\t"//保存当前进程的内核栈
-
"movl $1f,%0\n\t"//保存当前进程执行位置
-
"movl %3,%%esp\n\t"//恢复即将调度进程的内核栈
-
"movl %3,%%ebp\n\t"
-
"pushl %2\n\t" //恢复执行环境
-
"ret\n\t"
-
"1:"
-
"popl %%ebp\n\t"
-
:"=m"(prev->thread.eip),"=m"(prev->thread.esp)
-
:"m"(next->thread.eip),"m"(next->thread.esp)
-
:
-
);
-
}
-
}
-
总结:这段程序是对linux进程切换的模拟,进程切换总结起来就那么一句话,保存当前进程的上下文,恢复被调度进程的上下文,
-
这里只是简单
的用一个链表来连接各个进程,并没有涉及到调度算法。 -