PS.贺邦   原创作品转载请注明出处  《Linux内核分析》MOOC课程    http://mooc.study.163.com/course/USTC-1000029000

1.mykernel实验指导(操作系统是如何工作的)

使用实验楼虚拟机打开shell输入下列代码

  • 1 cd LinuxKernel/linux-3.9.4
  • 2 qemu -kernel arch/x86/boot/bzImage

可以看到初始的内核运行情况如下:

内核不停的执行my_start_kernel(),每隔一段时间被my_timer_handler()中断,然后执行一条打印语句:printk(KERN_NOTICE “\n>>>>>>>>>>>>>>>>>my_timer_handler here<<<<<<<<<<<<<<<<<<\n\n”);

后,又回到my_start_kernel()继续执行。

打开mymain.c文件,可以看到其中只有如下这一个函数。它作为内核启动的起始位置,从这个函数开始运行,并无限循环。

打开myinterrupt.c文件,里面也只有一个my_timer_handler(),它被Linux内核周期性调用,从而产生了一个周期性的中断机制。

2.修改内核代码,使之成为一个简单的时间片轮转多道程序内核,然后重新编译运行。

https://github.com/mengning/mykernel上下载mypcb.h;mymain.c;myinterrupt.c; 
然后替换位于home/shiyanlou/LinuxKernel/linux-3.9.4/mykernel/中的mymain.c;myinterrupt.c; 
将mypcb.h也放在这里。

然后执行make,重新编译内核。效果如下:

然后再次输入:

  • qemu -kernel arch/x86/boot/bzImage 命令

启动内核。 可以发现跳转和时间片都有了明显的改变。

实验结果与预期结果相符,实验成功。

3.重点代码理解

mymain.c

void__init my_start_kernel(void) {

Int pid = 0;

Int i;

/* Initialize process 0*/

task[pid].pid = pid;//task[0].pid=0;

ask[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */

task[pid].task_entry = task[pid].thread.ip = (unsignedlong)my_process;//令0号进程的入口地址为my_process();

task[pid].thread.sp = (unsignedlong)&task[pid].stack[KERNEL_STACK_SIZE-1];//0号进程的栈顶为stack[]数组的最后一个元素

task[pid].next = &task[pid];//next指针指向自己/*fork more process */

for(i=1;i<MAX_TASK_NUM;i++)//根据0号进程,复制出几个只是编号不同的进程

{

memcpy(&task[i],&task[0],sizeof(tPCB));//void *memcpy(void *dest, const void *src, size_t n);从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

task[i].pid = i; task[i].state = -1;//这些进程的状态都设置为未运行。

task[i].thread.sp = (unsignedlong)&task[i].stack[KERNEL_STACK_SIZE-1]; task[i].next = task[i-1].next;//新创建的进程的next指向0号进程的首地址

task[i-1].next = &task[i];//前一个进程的next指向最新创建的进程的首地址,从而成为一个循环链表。

mypcb.h

#define MAX_TASK_NUM 4 //最大进程数,这里设置为了4个。

#define KERNEL_STACK_SIZE 1024*8 //每个进程的内核栈的大小。

/* CPU-specific state of this task */

structThread { unsignedlongip;//用于保存进程的eip

unsignedlongsp;//用户保存进程的esp};

typedefstructPCB{ intpid;//进程的id号

volatilelongstate; /* 进程的状态:-1 unrunnable, 0 runnable, >0 stopped */

charstack[KERNEL_STACK_SIZE];//进程的栈,只有一个核心栈。/* CPU-specific state of this task */

structThread thread;//每个进程只有一个线程。

unsignedlongtask_entry;//进程的起始入口地址。

myinterrupt.c

if(next->state == 0)/*如果下一个将要运行的进程已经处于运行状态 -1 unrunnable, 0 runnable, >0 stopped */

{

/* switch to next process */

asm volatile(

"pushl %%ebp\n\t"       /* 保存当前进程的ebp到自己的栈中。    save ebp */

"movl %%esp,%0\n\t"     /* 保存当前进程的esp到自己的栈中。    save esp */

"movl %2,%%esp\n\t"     /* 从next->thread.sp中弹出下一个进程的esp。与第二句相对应。   restore  esp */

"movl $1f,%1\n\t"       /* 将下一个进程的eip设置为1f。$1f就是指标号1:的代码在内存中存储的地址  save eip */

"pushl %3\n\t"          /* 将next->thread.ip压入当前进程的栈中。*/

"ret\n\t"               /* 从当前进程的栈中弹出刚刚压入的next->thread.ip。完成进程切换。  restore  eip */

"1:\t"                  /* 即$1f指向的位置。next process start here */

"popl %%ebp\n\t"        /* 切换到的进程把ebp从栈中弹出至ebp寄存器。与第一句相对应。*/

: "=m" (prev->thread.sp),"=m" (prev->thread.ip)

: "m" (next->thread.sp),"m" (next->thread.ip)

);

my_current_task = next; //当前进程切换为next

printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid); //打印切换信息

}

else//如果下一个将要运行的进程还从未运行过。

{

next->state = 0;//将其设置为运行状态。

my_current_task = next;////当前进程切换为next

printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);//打印切换信息

/* switch to new process */

asm volatile(

"pushl %%ebp\n\t"       /* save ebp */

"movl %%esp,%0\n\t"     /* save esp */

"movl %2,%%esp\n\t"     /* restore  esp */

"movl %2,%%ebp\n\t"     /* restore  ebp */

"movl $1f,%1\n\t"       /* 将要被切换出去的进程的ip设置为$1f。这样等一下它被切换回来时(一定是运行状态)肯定会进入if判断分支,可以从if中的标号1处继续执行。  save eip */

"pushl %3\n\t"          /* 将next->thread.ip(因为它还没有被运行过,所以next->thread.ip现在仍处于初始状态,即指向my_process(),压入将要被切换出去的进程的堆栈。*/

"ret\n\t"               /* 将刚刚压入的next->thread.ip出栈至eip,完成进程切换。   restore  eip */

4.分析进程的启动和进程的切换机制。

首先,内核启动__init my_start_kernel(void),创建了4个进程,分别是0,1,2,3号,设置0号为运行态,其它3个进程为未运行态。

0号进程的入口都被初始化为 task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;即指向my_process()。 
0号进程的栈顶被初始化为 task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1]; 
之后的进程也都是根据0号进程复制得到,所以它们的起始入口也是my_process(),初始栈顶也是指向了自己的stack[KERNEL_STACK_SIZE-1];

my_current_task = &task[pid];将当前进程设置为0号进程。然后从0号进程开始运行。 
“movl %1,%%esp\n\t” 
将0号进程的栈顶放入esp寄存器。 
“pushl %1\n\t” /* push ebp */ 
当前esp指向了stack数组的末尾,这里也是栈顶,因为栈是空的,所以esp==ebp。 
“pushl %0\n\t” /* push task[pid].thread.ip */ 
“ret\n\t” /* pop task[pid].thread.ip to eip */ 
切换到0号进程的入口地址开始执行。 
“popl %%ebp\n\t” 
这句多余,在ret之后,不会被执行。 

: “c” (task[pid].thread.ip),”d” (task[pid].thread.sp)

之后0号进程不断执行my_process()。一段时间后,my_timer_handler()被内核调用,触发中断, my_need_sched = 1;将全局变量my_need_sched 设置为了1。

此后,当0号进程执行到了if(my_need_sched == 1)时就会进入这个if条件分支中,执行 my_schedule();执行进程调度。

0号进程的next指针指向的是1号进程,所以在my_schedule()中的next指针指向了1号进程,prev指针指向了0号进程。 
因为1号进程当前还未被运行过,所以会执行else条件分支:next->state = 0;//将1号进程设置为运行状态。 
my_current_task = next;//当前进程切换为1号进程printk(KERN_NOTICE “>>>switch %d to %d<<<\n”,prev->pid,next->pid);//打印switch 0 to 1 
“pushl %%ebp\n\t” /* save ebp */ 
“movl %%esp,%0\n\t” /* save esp */ 
将0号进程的ebp和esp都保存到0号进程的栈上。 
“movl %2,%%esp\n\t” /* restore esp */ 
“movl %2,%%ebp\n\t” /* restore ebp */ 
将1号进程的存在1号进程结构体中next->thread.sp保存的esp的值存入esp寄存器和ebp寄存器,因为1号进程还未被运行过,所以esp仍指向了1号栈的stack[KERNEL_STACK_SIZE-1]。 
“movl $1f, %1\n\t” 将0号进程的eip设置为if。 
“pushl %3\n\t” 
“ret\n\t” 
将1号进程的eip加入0号进程的栈中,然后通过ret指令,将这个eip从0号进程的栈中弹出,存入eip寄存器,完成从0号进程到1号进程的切换。此后类似。

5.对“操作系统是如何工作的”理解。

操作系统的内核有一个起始位置,从这个起始位置开始执行。开始工作时,CPU分配给第一个进程,开始执行第一个进程,然后通过一定的调度算法,比如时间片轮转,在一个时间片后,发生中断,

第一个进程被阻塞,在完成保存现场后将CPU分配给下一个进程,执行下一个进程。这样,操作系统就完成了基本的进程调度的功能。

Linux内核分析:完成一个简单的时间片轮转多道程序内核代码的更多相关文章

  1. Linux内核分析—完成一个简单的时间片轮转多道程序内核代码

    ---恢复内容开始--- 20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10 ...

  2. 20135202闫佳歆--week2 一个简单的时间片轮转多道程序内核代码及分析

    一个简单的时间片轮转多道程序内核代码及分析 所用代码为课程配套git库中下载得到的. 一.进程的启动 /*出自mymain.c*/ /* start process 0 by task[0] */ p ...

  3. linux内核分析第二周-完成一个简单的时间片轮转多道程序内核代码

    中断时计算机运行的一个非常重要的功能.之所以重要,是因为由于种种原因,计算机不能将一个程序从头执行到尾不间断,而是可能会出现很多像等待输入设备输出设备的过程,如果没有中断系统,CPU只能等待,造成资源 ...

  4. linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码

    计算机如何工作 三个法宝:存储程序计算机.函数调用堆栈.中断机制. 堆栈 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 Esp 堆栈指针  (stack pointer) ...

  5. Linux内核分析第二周学习博客——完成一个简单的时间片轮转多道程序内核代码

    Linux内核分析第二周学习博客 本周,通过实现一个简单的操作系统内核,我大致了解了操作系统运行的过程. 实验主要步骤如下: 代码分析: void my_process(void) { int i = ...

  6. Linux内核设计第二周学习总结 完成一个简单的时间片轮转多道程序内核代码

    陈巧然 原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.使用实验楼的虚拟机 ...

  7. Linux内核分析— —构造一个简单的Linux系统MenuOS(20135213林涵锦)

    Linux内核分析— —构造一个简单的Linux系统MenuOS 实验内容 Linux内核的启动过程,从start_kernel到init进程启动 使用实验楼的虚拟机打开shell cd LinuxK ...

  8. Linux内核分析-构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS linux内核目录结构 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel C ...

  9. Linux内核分析——构造一个简单的Linux系统MenuOS

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.Linux内核源代码简 ...

随机推荐

  1. sql !=与null

    在写SQL 条件语句是经常用到 不等于‘<>’的筛选条件,此时要注意此条件会将字段为null的数据也当做满足不等于的条件而将数据筛选掉. 例:表A A1  B1 1 0 2 1 3 NUL ...

  2. python第三十四课——1.匿名函数的定义和使用

    演示匿名函数的定义和使用 # 定义无参有返回值的有名函数: def func(): return True # 定义无参有返回值的匿名函数 f=lambda : True # 调用有名函数执行 pri ...

  3. AIX平台安装Oracle11gR2数据库

    1. 前提条件 1.1 认证操作系统 Certification Information for Oracle Database on IBM AIX on Power systems(Doc ID ...

  4. MyBatis之Collection

    Collection翻译过来,意为"集合"的意思,既然是集合,肯定是代表多个. MyBatis以其自身,小巧易懂,闻名于JavaEE. 传统的JDBC就不说了,Hibernate记 ...

  5. Vue购物车

    index.html <!DOCTYPE html><html>    <head>        <meta charset="utf-8&quo ...

  6. window.location对象详解

    window.location.href(当前URL) 结果如下: http://www.myurl.com:8866/test?id=123&username=xxx window.loca ...

  7. Java java.text.ParseException: Unparseable date

    用java将字符串转换成Date类型是,会出现java.text.ParseException: Unparseable date异常. 例如下面的这段代码就会出现上面的异常: public bool ...

  8. ceph 部署步骤和原理理解

    1.ceph的官方源在国外,网速比较慢,此处添加ceph源为阿里源(每个节点上均执行) vim /etc/yum.repos.d/ceph.repo [Ceph] name=Ceph packages ...

  9. 第43章 RTC—实时时钟

    第43章     RTC—实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fireg ...

  10. Liunx-mkdir命令

    1. 新建一个文件夹 one 2. 新建三个文件夹three,four,five 3. 新建一个多层级文件夹 201904/a/01