Mykernel 第二周实验报告

2024-06-06 09:32

本文主要是介绍Mykernel 第二周实验报告,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刘子健 +

原创作品转载请注明出处 + 

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


题目自拟,内容围绕操作系统是如何工作的进行;

博客中需要使用实验截图

博客内容中需要仔细分析进程的启动和进程的切换机制

总结部分需要阐明自己对“操作系统是如何工作的”理解


首先,特别感谢孟宁老师提供了这么好的课程资源.同样对所有contribute to Mykernel的前辈致敬!




   声明,下文会利用自己知道的Linux知识和Mykernel联系起来进行分析讨论,如果没有Linux内核代码阅读经验的童鞋,当我说"Linux中..."的时候请自动开启你的跳转功能,直接无视.:-)



  可能由于GFW的存在,去下个对应版本的Linux内核都是坑爹的事情.我就遇到过.实验室老师用的是3.4.9版本的内核.这里如果手边没有3.4.9的内核(我就这样),但是你手边有别的内核版本一般也是可以的(版本别太低就行, gcc当前版本可能不能编译很早期的内核代码).

  照着信息提示,去搭建好开发环境,这里我建议有一定Linux基础的童鞋不要用实验楼的虚拟机,自己在本地从头到尾的去搭建开发环境.


去把mykernel文件夹下所有和内核版本相关的字符串即3.4.9替换成你自己的内核版本,我这里是3.13.1.用vim的话就是用命令
1, $s/3.4.9/3.13.1
就能搞定啦
然后照着下面的环境配置方法就可以啦


下面开始进行代码分析:

首先从头文件mypcb.h开始.
这里定义最大的任务数为MAX_TASK_NUM即100
以及内核栈的大小 8KB

这里struct Thread有两个member 组成,特意的,用了unsigned long 类型的数据,就是为了取得机器指针长度一致的数据.这里的ip,即instruction pointer , 而sp为stack pointer.分别用来指示当前进程的下一条指令的地址和栈顶指针.


接着,定义结构体struct PCB,这里的PCB应该不是玩硬件同学熟悉的印刷电路板的那个PCB(printed circuit board)

对应的英语全称偶也不是很清楚,不过有一点可以肯定,这个结构体对应着Linux内核里的struct task.

意义在于为进程提供抽象数据结构,把需要描述进程的元素统统用这个结构体封装起来.


呵呵,老师也是良苦用心,这么精简,要知道,真正的struct task有近1000行,一个结构体的定义就1000+行.

state 有三种情况,0为正在运行的进程,-1表示不可运行,大于0则表示进程停止了.

这是Mykernel的进程描述情况,

真正的Linux的Process state有五种.



OK, 头文件pcb.h搞定了.然后分析mymain.c

看看my_start_kernel()函数

void __init my_start_kernel(void)
{int pid = 0;/* Initialize process 0*/task[pid].pid = pid;task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */// set task 0 execute entry address to my_processtask[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 more process */for(pid=1;pid<MAX_TASK_NUM;pid++){   //这里主要是对每个进程的数据结构进行初始化,把所有初始进程除了进程0,都设置为不可运行memcpy(&task[pid],&task[0],sizeof(tPCB));task[pid].pid = pid;task[pid].state = -1;task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority 进程优先级}task[MAX_TASK_NUM-1].next=&task[0];printk(KERN_NOTICE "\n\n\n\n\n\n                system begin :>>>process 0 running!!!<<<\n\n");/* start process 0 by task[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*/
);
}


   这里task数组还算有点意思,task是几个结构体数组, 而且这里task由于内部存在的next指针,还是个链表.单向链表.


   这个时候我们最好一步步的跟踪内核代码,这样体会会更加深刻(虽然Linus大神完全不提倡用调试器,but他是对内核维护的那批神人说的啊,吾辈啥都不知道的时候还是利用神器-qemu帮助自己学习内核吧, 不然你看再多代码可能也没感觉)

首先去./linux-3.13.1/目录下修改好configure文件,是为了方便gdb调试,不然哥们没有提示符很坑爹哦~

make menuconfig

然后你会看到一个图形界面的内核配置选项菜单

然后进入到kernel debugging

然后再去把选项Compile the kernel with debug info(CONFIG_DEBUG_INFO)打钩


看这样就可以在gdb里面调试内核代码了

我们在my_start_kernel函数处设置个断点


具体的如何用qemu和gdb调试内核,我特意做了笔记如下面的link:

http://blog.csdn.net/cinmyheart/article/details/44249291

如果你跟我一样,是用全新的源码搭建环境的话,记得一定要修改内核代码根目录下的Makefile


之前我就一直很奇怪,为什么我编译的内核始终是没有编译自己新添加的文件的,这时候明白了,忘记修改Makefile了.

下面是我的3.13.1版本内核里面Makefile的部分截图.记得一定把你要编译的目录添加到core-y里面去!不然你debug的时候总会觉得奇怪,为嘛自己添加代码了但是没有编译.


如果你用的新的源代码.记得还要做一下几件事情.

1.

/arch/x86/kernel/time.c里面添加头文件

#include <linux/timer.h>

2.下面约定,找到相应的文件路径,然后添加带有+开头的代码行.


3.

4.


5.这里可以特别注意到,在init/main.c里面添加了my_start_kernel()

旨在让初始化结束后的引导过程进入my_start_kernel(),我们自己写的函数.


最后把mykernel文件夹放到新的内核源代码目录树下面就OK啦

在my_start_kernel()里面,下面是一段嵌入式汇编,旨在切换堆栈(movl %1 %%esp),然后保存当前进程的堆栈指针和指令指针,最后把进程为pid的堆栈设置为esp新的值.还有指令指针也更新为pid进程的指针.


至于%1 %0是嵌入式汇编,这里可以看到图中的后面部分有两个分号,那里是特指明输入输出寄存器的.

"c" (task[pid].thread.ip)被特别声明,用ecx寄存器来储存task[pid].thread.ip的值,并且可以用%0引用这个值,

同理,edx寄存器来储存task[pid].thread.sp的值

如果对嵌入式汇编不熟悉,传送门在这里:

http://blog.csdn.net/cinmyheart/article/details/24967455

接着会进入my_process函数,这里就是个暴力时间迭代,如果变量i一直++到10000000,然后可以进入if判断,

当前进程就让出CPU.


在mykernel/myinterrupt.c里,有定义好的全局变量,这个变量默认为0,那么之前从未有改变这个变量的值,因此你会发现,这个


my_timer_handler作为一个定时器,每次time_count自增到1000的倍数,然后当前进程不需要进程切换的话,那么我们把my_need_sched置为1,我们将要切换进程,如果my_need_sched本来就是1,那么啥也不需要干


一开始进程0就会切换到进程1而后,进程1会不断的尝试切换到进程2但是一段时间内不会成功,只有当my_need_sched等于1的时候,进程才会切换.而这需要定时器处理函数进入if(),将my_need_sched置为1.条件就是time_count自增1000次.

看你定时器多块啦~


下面是当进程切换到进程3的时候进程不但尝试切换到其他进程(this process 3 -),但是不成功,于是又回到(this process 3+)



博客内容中需要仔细分析进程的启动和进程的切换机制:

之前玩过一点JOS,从系统上电的那一刹那, 除了CS段是0xFFFF之外,其他的段寄存器都是0x0000.故事从这里开始.

   而后便是一系列的初始化过程,init/main.c里面作为初始化的主程序,会调用各种不同的初始化程序.基本上初始化OK了,那么就可以开始创建进程关系链接,以及进程切换的事情了.玩过单片机的童鞋应该很清楚,有时候很喜欢写个while(1)的死循环,其实在内核里面,系统开始运行了就是个大的while(1),只是在这里面各种花样太多了.呵呵.进程是为了抽象CPU,让某一个进程去集中做一件事情,而当运行计算单元(CPU)比较少时,我们可以一步步的做,保存现场,进程见相互切换,实现一个CPU或者比较少的CPU可以"同时"做很多事情.

   传统的,单核计算机的并发就是不停的切换任务.造成一种并行的假象.到后来正在多核CPU的时候才会正真意义上的并行计算.

   就这么多吧,不然又唧唧歪歪不停的啰嗦.


总结部分需要阐明自己对“操作系统是如何工作的”理解

   操作系统嘛~草鸡好玩的东东.

   宏观的意义上来说,这层软件把硬件设备处理的非常非常好,以至于,人们可以不用去管硬件实现,直接和计算机进行交互.通过编程语言.借助操作系统的系统调用,完成你想做的事情.不用去管底层实现.

   如何工作?这要扯扯体系结构的东东,不同的硬件平台,可能初始化的过程不一样,但是只要完成初始化的过程,后面的系统调度策略都是一样的.

         系统从BIOS(这家伙叫basic input and output system),储存在永久记忆的储存芯片里面,记忆这关于计算机硬件的很基本的信息,比方说,最大的内存容量是多少等等,有兴趣的话,可以去看看Linux设备驱动里面,我写了个读取CMOS信息的驱动.

   从BIOS获取基本的硬件信息之后,就会利用这些信息,对硬件进行必要的初始话工作.完成之后,操作系统的特定部分软件就开始维护,并运行这个硬件.而后就可以工作使用啦~

   对于Operating system还木有神马概念的话,推荐看看<<Modern Operating system>>.很好的概念性的书籍.实际动手的话推荐赵炯博士的Linux内核源码注释0.11(我也是水,还没看完.)然后可以玩玩草鸡棒的JOS.JOS这家伙也不是好惹的.呵呵...


   



Have fun : )






这篇关于Mykernel 第二周实验报告的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1035729

相关文章

Linux操作系统 :文件管理(实验报告)

一、实验目的 熟练掌握Shell特性和文件管理命令的使用方法 二、实验环境 硬件:PC电脑一台,网络正常; 配置:win10系统,内存大于8G  硬盘500G及以上 软件:VMware、Ubuntu16.04、 三、实验内容 Linux的文件管理 四、实验步骤 1、以自己姓名的英文缩写xxx来创建一个用户,并设置密码为12345678,进入用户。 程序运

DP第二周作业

http://blog.csdn.net/qq_29300341/article/details/77478605?locationNum=8&fps=1

【信息安全】基于CBC的3DES加解密-实验报告

实验运行效果截图 3DES进行加密 3DES进行解密 然后可以选择你想要的操作,继续加密解密或者退出。 基于CBC模式的3DES加解密 一、实验内容 基于3DES加解密算法,编程实现对任意文件实现加解密的软件。 编程实现DES加密和解密算法,并使用DES加解密算法实现3DES加解密算法。选择一种填充方式,对需要加密的文件进行填充。DES的加解密的工作模式,采用密码

biostar handbook: 第二周笔记汇总+第三周任务布置

第二周已经结束了,我不确定大家对Linux到底了解到了什么程度,但是我觉得再给一周时间让初学者去熟悉Linux肯定是必要的。于是这一周的任务不会太难,只需要让大家去理解本体论(ontology)。 笔记汇总 这周有一些小伙伴开始遭遇人生抉择,有一些则是要出差赶路,所以上交作业不算太多。可能大家对自己的要求有点高了,其实我一直强调的是笔记的不断迭代,只要你写了一点内容就可以发出来,后来不断修改,

「学转录组入门生信」第二周来获取表达量矩阵

我们第二周目标有四个: 整理数据RNA-seq格式了解数据质控数据比对read定量 首先,我们得要知道我们在转录组分析过程中会遇到很多格式,建议先通过搜索查找了解这些格式是什么 fasta/fas/fagtf/gffbedsam/bamcsv/tsv/txt 接着,我们会在分析过程中时刻检查我们的数据质量,所以你要尝试回答下面这几个问题 数据质控要在哪个阶段做不同阶段要看什么标准质控有哪

[34期] 第二周学习总结

时间过得真快 转眼间来兄弟连已经第二周了 想起刚开始到兄弟连的雄心壮志 到现在的状态 心里难免会很内疚 因为这两周以来的状态的确不太好。 记得在家里 学习LINUX 的时候很认真 每节课都仔细的记下笔记 为什么上课的时候就好困呢。。 可能主要原因还是在于自己吧。 没有及时的调整好自己的作息时间。风油精 各种措施都上了 还是困得要死。 李明老师是一个很认真负责的老师 每次问他问题 他都是很乐

《现代通信原理与技术》码间串扰和​​​​​​​无码间串扰的眼图对比实验报告

实    验:码间串扰和无码间串扰的眼图对比实验报告 摘 要:         在数字通信系统中,码间串扰(Inter-Symbol Interference, ISI)是影响信号质量和系统性能的重要因素之一。本实验通过MATLAB软件生成并对比了受码间串扰影响和未受码间串扰影响的数字信号的眼图。结果显示,未受码间串扰影响的眼图具有较为清晰的开口,而受码间串扰影响的眼图则由于符号间的干扰而

2023 hnust 湖科大 嵌入式 实验报告+代码及复习资料等

2023 hnust 湖科大 嵌入式 实验报告+代码及复习资料等 目录 流水灯 1 8位数码管动态扫描 3 按键输入 5 温度与关照 7 看门狗 9 内容 报告 代码 下载链接 https://pan.baidu.com/s/1LIN8rm42yrukXliI3XyZ1g?pwd=1111

JS学习第二周

1.DOM 1.获得DOM对象 document.querySelector() 根据CSS选择器获取第一个匹配的元素: var element = document.querySelector('.myClassName'); // 获取第一个有myClassName类的元素 var anotherElement = document.querySelector('#myElemen

单词记忆(第二周)

transplant: trans - plant 移植 perceive: per - ceive 察觉 paraphrase: para - ph - rase 释义 prospect: pro - s - pect 前景 access: ac - cess 进入,通道,访问 generous; gene - rous 慷慨的,丰富的 lecture: lec - ture 讲座