注:袁帥+
原創作品轉載請註明出處 + 《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);
-
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)
-
:
-
);
-
}
-
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);
-
}
-
}
-
}
-
#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進程切換的模擬,進程切換總結起來就那麼一句話,保存當前進程的上下文,恢復被調度進程的上下文,
-
這裏只是簡單
的用一個鏈表來連接各個進程,並沒有涉及到調度算法。 -