linux内核学习之二 一个精简内核的分析(基于时间片轮转)
一 实验过程及效果
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内核学习之二 一个精简内核的分析(基于时间片轮转)的更多相关文章
- linux内核学习之二:编译内核
在linux内核学习系列的第一课中讲述了搭建学习环境的过程(http://www.cnblogs.com/xiongyuanxiong/p/3523306.html),环境搭好后,马上就进入到下一环节 ...
- Linux内核学习笔记二——进程
Linux内核学习笔记二——进程 一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...
- Linux线程学习(二)
线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换 线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...
- linux驱动学习(二) Makefile高级【转】
转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...
- linux shell学习笔记二---自定义函数(定义、返回值、变量作用域)介绍
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.下面说说它的定义方法,以及调用需要注意那些事项. 一.定义shell函数(define function) 语法: [ f ...
- ucore操作系统学习笔记(二) ucore lab2物理内存管理分析
一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- Linux操作系统学习_用户态与内核态之切换过程
因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...
- Linux驱动学习(编写一个最简单的模块)
在Linux中想做驱动开发,那么一定要先熟悉module的使用和编写 一.什么是module 从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些 ...
随机推荐
- Java - 安全的退出线程
stop() 存在的问题 使用 stop() 来退出线程是不安全的.它会解除由线程获取的所有锁,可能导致数据不一致. 举个例子: public class StopTest { public stat ...
- Windows 更改桌面位置
运行regedit HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders 找到 ...
- Nginx
Nginx的常识 1.nginx三大功能:(百万级别繁忙服务器占有率) 1)web服务器 轻量级,处理的并发量更大. 2)反向代理服务器 通过将用户端的请求,透明的转送给应用服务器.这样所 有的客户端 ...
- 前端静态资源版本更新与缓存之——gulp自动化添加版本号
公司项目每次发布后,偶尔会有缓存问题,然后看了下gulp,发现gulp还能给js,css自动化添加版本号,可解决缓存的问题,所以自动化实现静态资源的版本更新才是正道.通过网上的资料试过了两种办法: 1 ...
- asp 使用TreeView控件
这段代码为了使用 TreeNodeCheckChanged 事件,会有回刷新的效果: 不喜欢的可查看改进版,利用js控制选择操作,无界面刷新, “http://www.cnblogs.com/GoCi ...
- MR21、MR22和CK24的区别
MR21和CK24都是用于修改物料标准价,但两者功能和用法上有所区别:MR21是直接更改单价, CK24是在CK11N或CK40N价格评估的基础上的价格核发, CK24记录价格评估, 价格评估在做 ...
- null
期末考备考最后三天. 加油,把这学期学的知识给它搞透了.
- 20145218 GDB调试汇编堆栈过程分析
GDB调试汇编堆栈过程分析 虚拟机中分析过程 输入gcc - g example.c -o example -m32指令在64位机器上产生32位汇编,但出现以下错误: 这时需要使用sudo apt-g ...
- 关于codeblock中一些常用的快捷键(搬运)
关于codeblock中一些常用的快捷键(搬运) codeblock作为一个常用的C/C++编译器,是我最常用的一款编译器,但也因为常用,所以有时为了更加快速的操作难免会用到一些快捷键,但是因为我本身 ...
- 抓包工具--Fiddler及charles的使用
Fiddler和charles--是抓包工具,可以抓到pc端的请求,手机上设置代理后也可以抓到手机上的请求,也可以修改请求数据和返回的数据. 1.网页抓包,打开Fiddler或Charles应用直接访 ...