一   实验过程及效果

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

make成功:

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

效果分析:可以看到进程在不断切换,分别有进程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 

  

linux内核学习之二 一个精简内核的分析(基于时间片轮转)的更多相关文章

  1. linux内核学习之二:编译内核

    在linux内核学习系列的第一课中讲述了搭建学习环境的过程(http://www.cnblogs.com/xiongyuanxiong/p/3523306.html),环境搭好后,马上就进入到下一环节 ...

  2. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  3. Linux线程学习(二)

    线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换   线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...

  4. linux驱动学习(二) Makefile高级【转】

    转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...

  5. linux shell学习笔记二---自定义函数(定义、返回值、变量作用域)介绍

    linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.下面说说它的定义方法,以及调用需要注意那些事项. 一.定义shell函数(define function) 语法: [ f ...

  6. ucore操作系统学习笔记(二) ucore lab2物理内存管理分析

    一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...

  7. 嵌入式Linux驱动学习之路(五)u-boot启动流程分析

    这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...

  8. Linux操作系统学习_用户态与内核态之切换过程

    因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...

  9. Linux驱动学习(编写一个最简单的模块)

    在Linux中想做驱动开发,那么一定要先熟悉module的使用和编写 一.什么是module 从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些 ...

随机推荐

  1. ASP.NET MVC 静态资源打包和压缩问题小记

    ASP.NET MVC 中有个 BundleConfig 用于静态资源的打包和压缩,我在使用的过程中遇到一些问题,现在做下总结,并给出具体的解决方案. 问题一:打包压缩后的 JavaScript 和 ...

  2. iOS runtime 初步学习

    注: 在Xocde5之后, 使用运行时方法需要进行2步设置1. 在Build Setting中搜索'msg', 设置'Strict Checking' 为 NO2. 使用需要导入头文件 #import ...

  3. RansomNote

    
Given
 an 
arbitrary
 ransom
 note
 string 
and 
another 
string 
containing 
letters from
 all 
th ...

  4. Spring的注解方式实现AOP

    Spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,asp ...

  5. WEB服务器配置

    1.这是我的新服务器,还未做任何配置,系统为windows server2012 R2. 2.首先打开IE浏览器,这里弹出一个对话框,我选择的不推荐. 3.然后下载jdk,并安装 安装直接点下一步,下 ...

  6. Can't connect to local MySQL server through socket '/tmp/mysql.sock'

    找不到/tmp/mysql.sock这个文件,需要查找/tmp/mysql.sock文件位置,并在/etc/my.cnf里面配置 [client]socket=/var/lib/mysql/mysql ...

  7. java selenium后报错Element not found in the cache元素定位要重新赋值之前的定义

    习惯上把定位的元素在操作之前就定位好, 例如: WebElement element1=driver.findElement(...);      ----------declaration1 Web ...

  8. 即时聊天IM之二 openfire 整合现有系统用户

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com  综述: ...

  9. DFD

  10. 十天学会<div+css>横向导航菜单和纵向导航菜单

    纵向导航菜单及二级弹出菜单 纵向导航菜单:一级菜单 <head><style type="text/css">body { font-family: Ver ...