实验:基于kernel的简单的时间片轮转多道程序内核

1、实验要求

  • 完成一个简单的时间片轮转多道程序内核代码

    2、实验过程

  • 进入实验楼的linux环境,打开shell,输入以下代码:
cd LinuxKernel/linux-3.9.4
rm -rf mykernel
patch -p1 < ../mykernel_for_linux3.9.4sc.patch
make allnoconfig
make #编译内核请耐心等待
qemu -kernel arch/x86/boot/bzImage

执行的效果如下:

  • 在mykernel的基础上添加mypcb.h,修改mymain.c和myinterrupt.c文件,实现一个简单的操作系统内核,实现效果如下:

    3、mykernel时间片轮转代码分析

    mypcb.h

#define MAX_TASK_NUM        4
#define KERNEL_STACK_SIZE   1024*8/* CPU-specific state of this task */
struct Thread {unsigned long       ip;   //对应eipunsigned long       sp;   //对应esp
};typedef struct PCB{int pid;                 //定义进程idvolatile long state;     //-1 unrunnable, 0 runnable, >0 stoppedchar stack[KERNEL_STACK_SIZE]; //内核堆栈/* CPU-specific state of this task */struct Thread thread;unsigned long   task_entry;   //入口struct PCB *next;
}tPCB;void my_schedule(void); //声明调度函数

本mypcb.h头文件主要定义了程序控制块PCB,包括:
pid:定义进程id
state:进程状态标记,-1是未运行,0为运行,>0为终止
stack:定义使用的堆栈
thread:定义线程
task_entry:进程入口
next:链表指向下一个PCB

myinterrupt.c

#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>
#include "mypcb.h"extern tPCB task[MAX_TASK_NUM];  //extern引用全局变量
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;void my_timer_handler(void) //时钟中断触发本函数
{
#if 1if(time_count%100 == 0 && my_need_sched != 1) //当时钟中断发生100次,并且my_need_sched不为1时,赋值为1{printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");my_need_sched = 1;} time_count ++ ;
#endifreturn;
}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");/* schedule */next = my_current_task->next;prev = my_current_task;if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */ //下一个进程可运行,执行进程切换{my_current_task = next; printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  /* 切换进程 */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"                  /* next process start here */"popl %%ebp\n\t": "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip)); }else{next->state = 0;my_current_task = 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"       /* save eip */  "pushl %3\n\t" "ret\n\t"               /* restore  eip */: "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip));          }   return;
}

本c文件中,定义了my_timer_handler和my_schedule两个函数调用,前者是当时钟中断发生100次,并且my_need_sched不为1时,赋值为1,是mymain.c中my_process函数判定主动调度的标志;后者是执行调度的具体过程,下面对切换进程的汇编代码进行分析:
"pushl %%ebp\n\t" /* save ebp / ebp入栈
"movl %%esp,%0\n\t" /
save esp / 保存当前esp到进程的sp中
"movl %2,%%esp\n\t" /
restore esp / esp指向下一进程
"movl $1f,%1\n\t" /
save eip / 将1f存储到进程的ip中,$1f是标号“1:\t”处,再次调度到该进程时就会从1:开始执行
"pushl %3\n\t" 下一进程的ip入栈
"ret\n\t" /
restore eip / eip指向下一进程的起始地址
"1:\t" /
next process start here */ 下一进程从此处开始执行
"popl %%ebp\n\t" 执行完后出栈释放空间
: "=m" (prev->thread.sp),"=m" (prev->thread.ip) 分别对于上面的%0,%1
: "m" (next->thread.sp),"m" (next->thread.ip) 分别对应上面的%2,%3

mymain.c

#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>
#include "mypcb.h"tPCB task[MAX_TASK_NUM];  //PCB的数组task
tPCB * my_current_task = NULL; //当前task指针
volatile int my_need_sched = 0; //是否需要调度void my_process(void);  //my_process函数声明void __init my_start_kernel(void) //mykernel内核代码的入口
{int pid = 0;int i;/* 初始化0号进程*/task[pid].pid = pid;task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];task[pid].next = &task[pid];/*fork其他进程 */for(i=1;i<MAX_TASK_NUM;i++){memcpy(&task[i],&task[0],sizeof(tPCB));task[i].pid = i;task[i].state = -1;task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];task[i].next = task[i-1].next;task[i-1].next = &task[i];}/* 用task[0]开始0号进程 */pid = 0;my_current_task = &task[pid];asm volatile("movl %1,%%esp\n\t"     /* set task[pid].thread.sp to esp */"pushl %1\n\t"          /* push ebp */"pushl %0\n\t"          /* push task[pid].thread.ip */"ret\n\t"               /* pop task[pid].thread.ip to eip */"popl %%ebp\n\t": : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/);
}   void my_process(void)
{int i = 0;while(1){i++;if(i%10000000 == 0){printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);if(my_need_sched == 1) //判断是否需要调度{my_need_sched = 0;my_schedule();  //这是一个主动调度}printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);}     }
}

本c文件中,有my_start_kernel和my_process两个函数,其中,前者为mykernel内核代码的入口函数,后者为进程的入口函数,进程在运行中输出当前进程号,并通过my_need_sched变量判断是否需要调度。
其中对0号进程的启动汇编代码进行分析:
"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp / 将当前进程0的sp赋给esp
"pushl %1\n\t" /
push ebp / 进程0的sp入栈
"pushl %0\n\t" /
push task[pid].thread.ip / 进程0的ip入栈
"ret\n\t" /
pop task[pid].thread.ip to eip / 将进程0的ip赋给eip
"popl %%ebp\n\t" 执行完其他进程,回到0号进程,出栈
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /
input c or d mean %ecx/%edx*/ 输入,将0号进程的ip、sp值分别存入ecx、edx寄存器中,分别对应上面的%0,%1

4、问题与总结

本次实验没有遇到什么重大的问题,但是小毛病犯了一堆,比如在将代码拷入实验楼linux环境的vim中时,实验楼的粘贴板不知为何复制粘贴会缺失一段代码,后在编译c文件的时候总是报错,这个问题找了好久,后来发现是粘贴板粘贴的代码缺失。还有一个是在make时找不到文件,后来发现修改代码是在/mykernel目录下进行修改的,make编译内核需要在LinuxKernel/linux-3.9.4目录下进行,需要返回上一级菜单进行make。
总共来讲,本周各种事情比较多,学习的计划一拖再拖,推迟了好久才完成,以后一定要合理分配时间,这一点尤为重要。
还有,学习要认真仔细,尽量避免因为犯低级错误而白白消耗大量学习时间。

转载于:https://www.cnblogs.com/intoxication/p/9866849.html

《Linux内核原理与分析》第三周作业相关推荐

  1. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  2. 2022-2023-1 20222809《Linux内核原理与分析》第一周作业

    Linux内核原理与分析第一周作业 配置环境 1.参考Linux(Ubuntu)系统安装图文教程中第二种借助virtualbox成功配置Ubuntu环境 2.升级更新软件包 可以通过调节分辨率和虚拟机 ...

  3. 实验楼 linux内核原理与分析,《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  4. 2021-2022-1 20212820《Linux内核原理与分析》第一周作业

    声明:本文是基于Linux 基础入门_Linux - 蓝桥云课 (lanqiao.cn)这门课学习所写的课程笔记. 实验1 Linux系统简介 Linux主要包括是系统调用和内核两部分 Linux与W ...

  5. 2018-2019-1 20189218《Linux内核原理与分析》第九周作业

    进程调度的时机 进程调度时机就是内核调用schedule函数的时机.当内核即将返回用户空间时,内核会检查need_resched标志是否设置.如果设置,则调用schedule函数,此时是从中断(或者异 ...

  6. 《Linux内核原理与分析》第二周作业

    反汇编一个简单的C程序 1.实验要求 使用: gcc –S –o test.s test.c -m32 命令编译成汇编代码,对汇编代码进行分析总结.其中test.c的具体内容如下: int g(int ...

  7. 20189220 余超《Linux内核原理与分析》第一周作业

    实验一 Linux系统简介 通过实验一主要是学习到了Linux 的历史简介,linux与windows之间的区别,主要是免费和收费,软件和支持,安全性,使用习惯,可制定性,应用范畴等.linux具有稳 ...

  8. 2018-2019-1 20189201 《LInux内核原理与分析》第九周作业

    那一天我二十一岁,在我一生的黄金时代.我有好多奢望.我想爱,想吃,还想在一瞬间变成天上半明半暗的云.那一年我二十一岁,在我一生的黄金时代.我有好多想法.我思索,想象,我不知该如何行动,我想知道一个城市 ...

  9. 2022-2023-1 20222816《Linux内核原理与分析》第一周作业

    目录 实验一     Linux系统简介 实验二     基础概念及操作 实验三     用户及文件权限管理 总结 第一周课后在实验楼学习了<Linux入门>(新版),以下是我本周的学习笔 ...

  10. 2018-2019-1 20189208《Linux内核原理与分析》第九周作业

    活动 main函数编译有问题,div 函数和系统中某个函数重名,浮点输出有问题,scanf也有问题 修改如下 scanf_s("%d %d", &a, &b); p ...

最新文章

  1. 浙江大学计算机考研大纲,2018年浙江大学研究生入学考试《计算机学科专业基础》(878)考试大纲...
  2. QTextEdit 总结
  3. OpenCV3图像处理——霍夫曼变换直线检测
  4. 解决Homebrew报错Error: Failure while executing; git clone https://github.com/Homebrew/homebrew-core....
  5. java double 值是6.346255785955615E-4,这是字母“E”什么意思
  6. mantis config_inc.php g_source,CentOS7下Mantis安装与配置
  7. Android中文URL乱码问题 解决
  8. frdora10_a8_linux,硬盘安装fedora10
  9. Modern Python Cookbook》(Python经典实例)笔记 2.3 编写长行代码
  10. html合并pdf文件,PDF Mergy:合并PDF
  11. CVPR2021 | 视频超分辨率中时空蒸馏方案
  12. mac下载安装adb环境
  13. 南京服务器修复,南京戴尔服务器数据恢复
  14. 前端HTML+CSS学习笔记
  15. 华为交换机端口配置删除_华为交换机配置_华为交换机怎么清除端口下所有配置?...
  16. 巧用Hosts文件 杀掉麻烦的IE浏览器弹出窗口
  17. 对PHM铣刀磨损数据进行分析
  18. 模块耦合名词解释_名词解释(软件工程)
  19. 怎么快速调节EDIUS中声音的淡入淡出?
  20. 成功的经验 失败的教训

热门文章

  1. php判断单向链表中有没有环,python判断链表是否有环的实例代码
  2. delphi 纯虚函数的应用
  3. java程序发送邮件_用java程序发送邮件
  4. php ip to int_ip地址和int相互转换
  5. 训练作用_不同振幅的振动训练对身体的作用
  6. sql int 转string_SQL智能代码补全引擎【sql-code-intelligence】介绍
  7. 【视频课】零基础免费38课时深度学习+超60小时CV核心算法+15大Pytorch CV实践案例助你攻略CV...
  8. [综述类] 一文道尽深度学习中的数据增强方法(上)
  9. 全球及中国电镀砂轮行业盈利模式分析与十四五投资规划研究报告2021年版
  10. 用VC进行COM编程所必须掌握的理论知识