linux内核学习之二 一个精简内核的分析(基于时间片轮转)

一   实验过程及效果

1.准备好相关的代码,分别是mymain.c,mypcb.h,myinterrupt.c ,如下图,make

linux内核学习之二    一个精简内核的分析(基于时间片轮转)

make成功:

linux内核学习之二    一个精简内核的分析(基于时间片轮转)

在qemu创建的虚拟环境下的运行效果:(使用的命令如上图所示)

linux内核学习之二    一个精简内核的分析(基于时间片轮转)

linux内核学习之二    一个精简内核的分析(基于时间片轮转)

linux内核学习之二    一个精简内核的分析(基于时间片轮转)

效果分析:可以看到进程在不断切换,分别有进程0,1,2,3,每隔一段时间就进行一次切换。

二  具体代码

mypcb.h

#define MAX_TASK_NUM  4                            //定义最大任务(进程数)
#define KERNEL_STACK_SIZE 1024*8 //定义内核堆栈的大小 struct Thread{
unsigned long ip;
unsigned long sp;
}; 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); //声明调度函数

mypcb.h

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> #ifdef CONFIG_X86_LOCAL_APIC
#include <asm/smp.h>
#endif #include"mypcb.h" tPCB task[MAX_TASK_NUM];
tPCB * my_current_task = NULL;
volatile int my_need_sched = ; //定义调度标志 void my_process(void); void __init my_start_kernel(void) //进程初始化,并使进程0开始运行
{
int pid=;
int i ; /*进程0初始化*/ task[pid].pid = pid;
task[pid].state = ;
task[pid].task_entry = task[pid].thread.ip = (unsigned long) my_process; //指定进程0的入口
task[pid].thread.sp=(unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-];
task[pid].next = &task[pid];
for(i=;i<MAX_TASK_NUM;i++) //初始化进程1,2,3
{
memcpy(&task[i],&task[],sizeof(tPCB));
task[i].pid = i;
task[i].state = -; /* -1 unrunnable, 0 runnable, >0 stopped */
task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-];
task[i].next = task[i-].next; //构建进程控制块链表
task[i-].next = &task[i]; } pid = ;
my_current_task = &task[pid];
asm volatile( //嵌入式汇编代码,使进程0运行起来
"movl %1,%%esp\n\t"
"pushl %1\n\t"
"pushl %0\n\t"
"ret \n\t"
"popl %%ebp\n\t"
:
:"c" (task[pid].thread.ip),"d" (task[pid].thread.sp) );
/* while(1)
{
i++;
if(i%100000 == 0)
printk(KERN_NOTICE "my_start_kernel here %d \n",i); }*/
} void my_process(void) //进程的处理任务,每个进程都一样
{
int i = ;
while() //死循环
{
i++;
if(i% == ) //每10000000个指令周期检测一次是否调度
{
printk(KERN_NOTICE "THIS IS PROCESS %d -\n ",my_current_task->pid);
if(my_need_sched == )
{
my_need_sched =;
my_schedule(); //发生调度
}
printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid); } }
}

mymian.c

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" extern tPCB task[MAX_TASK_NUM];
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = ; /******* Called by timer interrupt.******/ void my_timer_handler(void) //定时器中断的调用函数
{
#if 1
if(time_count% == && my_need_sched != )
{
printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
my_need_sched = ;
}
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 == )/* -1 unrunnable, 0 runnable, >0 stopped */
{
/* switch to next process */
asm volatile( //进程调度的关键代码,相关的堆栈操作
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
"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
{ //如果下一任务(进程)不为0,从未被调度过
next->state = ;
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"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip) ); }
return;
}

myinterrupt.c

三  代码以及原理分析

在代码的已经进行了简要的注释,下面进行分析:首先在mypcb.h文件中定义了进程控制块的结构体 ,其中成员pid代表的是进程号(在内核中唯一标识一个进程),state代表进程的状态,为了简化,并没有列出我们在操作系统中学到的所有的进程状态,0代表运行态,-1非运行态,>0停止态,“struct Thread thread;”定义了进程堆栈操作相关的寄存器;“task_entry;”定义了进程的入口,即具体的处理函数;“struct PCB *next;”定义指向下一进程的指针。首先通过“void __init my_start_kernel(void)”进行初始化0号进程,指定了进程0的入口my_process();并且初始化进程1,2,3,构建了进程链表,接下来用嵌入式汇编使进程运行起来,即执行my_process()函数,可以看到函数内部有个while(1)死循环,每10000000个周期打印进程号信息、检测一次是否发生了调度,如果发生了调度,清除调度的标识my_need_sched,(就是使本进程能够被抢占,相当于清信号量的意思)就执行调度函数my_schedule();调度的标识 my_need_sched在哪里发生改变呢?这涉及到定时器中断了——在my_interrupt.c文件可以看到“void my_timer_handler(void)”,这就是定时器中断的处理函数,当定时中断次数达到1000次时,改变一次调度标志 ,表示能够调度了(主动调度),接下来就是调度函数进行调度了,其中的两段嵌入式汇编就是调度的关键,本质就是修改eip,指向要调度的函数入口,并进行进程上下文的保存(堆栈的方式)。后面就是无限循环了, 每10000000个周期检测一次是否发生了调度,这在期间定时器中断修改调度标识,使能发生调度。。。。。。

总结:个人对“操作系统如何工作的理解”

操作系统是对硬件的抽象,摆脱了用户直接操作硬件(否则想要用电脑之前必须成为编程高手),所以操作系统在电脑大众化的过程中扮演了不可磨灭的角色。同时操作系统是对硬件的最大化利用,在多用户系统中,使每个用户感觉自己在独享电脑;在多任务系统中,使每个进程(任务)感觉自己在霸占cpu。。。闲话不多说,要想要操作系统实现上述的功能(不发生混乱的局面),必须安排好调度机制,一种机制就是时间片轮转机制,就是多用户或者多任务轮流使用cpu,且使用时间有限,时间到了必须让出cpu,但是谁能担当这个安排时间角色呢?这就需要硬件的帮忙——定时器,当设定好规定时间后,当定时的时间到了,就以中断(异步)方式通知cpu作出相应的动作。。。。这是这种机制使计算机有序完成各种任务——任务管理,内存管理,文件管理,I/O设备管理等。

声明:1.实验环境基于网易实验平台——实验楼的64位linux环境。

2. 代码借鉴了孟宁老师的代码。

3.错误之处恳请指导~~~

by:方龙伟

原创作品 转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 

  

上一篇:git常用操作指令


下一篇:sql篇 select from where group by having order by