2019-2020-3 20199317《Linux内核原理与分析》第三周作业
第2章 操作系统是如何工作的
1 计算机的三大法宝
存储程序计算机:冯诺依曼结构
函数调用堆栈机制:记录调用的路径和参数的空间
中断机制:由CPU和内核代码共同实现了保存现场和恢复现场,把ebp,esp,eip寄存器的数据push到内核堆栈中。再把eip指向中断程序的入口,保存现场。
EBP(基址指针)寄存器在C语言中用作记录当前函数调用的基址,如果当前函数调用比较深,每一个函数的EBP是不一样的。函数调用堆栈就是由多个逻辑上的堆栈叠起来的框架,利用这样的堆栈框架实现函数的调用和返回。
对于x86体系结构来讲,堆栈空间是从高地址向低地址增长的,如下图所示:
2 在mykernel基础上构造一个简单的操作系统内核
C代码中内嵌汇编的语法如下:
_asm_ _volatile_ (
汇编语句模板:
输出部分:
输入部分:
破坏描述部分
);
这里可以把内嵌汇编当作一个函数,则第二部分输出和第三部分输入相当于函数的参数和返回值,而第一部分的汇编代码则相当于函数内部的具体代码。
构造一个简单的操作系统内核为:
第一步:增加一个mypcb.h头文件,用来定义进程控制块(Process Control Block),也就是进程结构体的定义,在LInux内核中是struct tast_struct结构体,如下图所示:
第二步:对mymain.c进行修改,这里是mykernel内核代码的入口,负责初始化内核的各个组成部分。在Linux内核源代码中,实际的内核入口是init/main.c中的start_kernel(void)函数。如下图所示:
第三步:对myinterrupt.c进行修改,主要是增加了进程切换的代码my_schedule(void)函数,在Linux内核源代码中对应的是schedule(void)函数。如下图所示:
最后,用cd ..命令返回上一层目录linux-3.9.4下,输入make命令重新编译,再输入qemu -kernel arch/x86/boot/bzImage,就可以看到重新编译后的内核启动效果如下图所示:
从图中可看到进程在切换。
3 代码分析
mypcb.h:此文件主要定义了一个PCB结构体,也就是所谓的进程管理块,用来记录进程的有关信息。
mymain.c:该文件就是完成了内核的初始化工作,并且创建了4个进程,进程从0号开始执行,并且根据标志位判断进程是否需要调度,此时会执行my_schedule()方法来完成相应的调度。
myinterrupt:此文件主要是产生时钟中断,用一个时间计数器周期性的检查循环条件,当条件满足时便会产生中断,并将进程调度标志位置1,当标志位为1时,进程便会执行my_schedule()方法,首先将当前进程的信息通过嵌入式汇编语句保存到堆栈当中,然后将下一个进程的地址赋给当前运行程序的指针,完成调度。
启动第一个进程即0号进程的关键汇编代码为:
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 */
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
这里需要注意的是%1是指后面的“ “d”(task[pid].thread.sp)”,%0是指后面的“ “c”(task[pid].thread.ip)”,进程0被初始化时,进程0的堆栈和相关寄存器的变化过程如下图所示:
进程调度代码如下:
if(next->state == )/* next->state == 0对应进程next对应进程曾经执行过 */
{
/* 进行进程调度关键代码 */
asm volatile(
"pushl %%ebp\n\t" /* 保存当前EBP到堆栈中 */
"movl %%esp,%0\n\t" /* 保存当前ESP到当前进程PCB中 */
"movl %2,%%esp\n\t" /* 将next进程的堆栈栈顶的值存到ESP寄存器中 */
"movl $1f,%1\n\t" /* 保存当前进程的EIP值,下次恢复进程后将在标号1开始执行 */
"pushl %3\n\t" /* 将next进程继续执行的代码位置(标号1)压栈 */
"ret\n\t" /* 出栈标号 1到EIP寄存器*/
"1:\t" /* 标号1,即next进程开始执行的位置 */
"popl %%ebp\n\t" /* 恢复EBP寄存器的值 */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
else /* next该进程第一次被执行 */
{
next->state = ;
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" /* 保存当前EBP到堆栈中 */
"movl %%esp,%0\n\t" /* 保存当前ESP到PCB */
"movl %2,%%esp\n\t" /* 将next进程的栈顶地址到ESP寄存器中 */
"movl %2,%%ebp\n\t" /* 将next进程的堆栈基地址到ESP寄存器中 */
"movl $1f,%1\n\t" /* 保存当前进程的EIP寄存器值到PCB,这里$1f是指上面的标号1 */
"pushl %3\n\t" /* 把即将执行的进程的代码入口地址入栈 */
"ret\n\t" /* 出栈进程的代码入口地址到EIP寄存器*/
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
为了简便,假设系统只有两个进程,分别是进程0和进程1。进程0由内核启动时初始化执行,然后需要进程调度,开始执行进程1。下面从进程1被调度开始分析堆栈变化,因为进程1从来没有被执行过,此时执行else中的代码。堆栈和相关寄存器的变化过程如下图所示:
如果进程1执行的过程中发生了进程调度,进程0重新被调度执行了,这个时候应该执行if中的代码,if中的内嵌汇编代码执行过程中堆栈的变化分析如下图所示:
然后这里有一个问题:$1f为前方的标号1,if中有标号1,else中没有标号1。这是因为else中是进程第一次被执行的代码,只用将其存入prev->thread.ip,并不需要使用$lf来获取程序入口地址,但是if中的代码代表的是进程再次被重新调度执行,prev->thread.ip变成了next->thread.ip,此时进入了if代码块中会将next->thread.ip压栈,并由ret出栈到EIP寄存器中,这时就需要使用上次被调度出去时保存的$1f。
4 总结
操作系统首先初始化内核相关的进程,然后开始循环运行这些进程,当进程间进行切换时,利用内核堆栈所保存的每个进程的sp,ip即所对应的%esp,%eip寄存器中的值,对当前的进程的sp,ip即对应%esp,%eip寄存器的值进行保存(中断上下文),并用下一个进程的sp,ip的值赋值给%esp,%eip寄存器(进程间切换)。
2019-2020-3 20199317《Linux内核原理与分析》第三周作业的更多相关文章
- 2019-2020-1 20199329《Linux内核原理与分析》第九周作业
<Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...
- 20169212《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...
- 20169210《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业
2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...
- 2017-2018-1 20179215《Linux内核原理与分析》第二周作业
20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...
- 2019-2020-1 20209313《Linux内核原理与分析》第二周作业
2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第一周作业
Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...
- 《Linux内核原理与分析》第一周作业 20189210
实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...
- 2018-2019-1 20189221《Linux内核原理与分析》第二周作业
读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- 【XSY2525】Maze 2017多校
Description 考虑一个 N×M 的网格,每个网格要么是空的,要么是障碍物.整个网格四周都是墙壁(即第1行和第n行,第1列和第m列都是墙壁),墙壁有且仅有两处开口,分别代表起点和终点.起点总是 ...
- MIT线性代数:5.转置,置换,向量空间
- os模块操作文件
os模块: path=os.path.join(os.path.dirname(os.path.dirname(__file__)),'images') path:运行脚本的当前文件下的上一个文件的地 ...
- 分手是祝愿:dp
Description Zeit und Raum trennen dich und mich. 时空将你我分开. B 君在玩一个游戏,这个游戏n个灯和n个开关组成,给定这n个灯的初始状态,下标为从1 ...
- [考试反思]0815NOIP模拟测试22
40分,15名. 1-4:120 75 70 70 35分20名...总之差距极小不想说了 昨天教练说:以后的考试还是联赛知识点,但是难度比联赛高. 没听进去,以为是对于所有人而言的,也就是T1难度变 ...
- NOIP模拟 4
T1没开longlong T2忘了有向... T3是个好题,可以说将复杂度从N^2优化到NlogN是一个质的飞跃 考虑分治(要想出log可不就要分治么!(segtree也行 但我不会) 对于一个分治区 ...
- 一种logging封装方法,不会产生重复log
在调试logging的封装的时候,发现已经调用了logging封装的函数,在被其它函数再调用时,会出现重复的logging.原因是不同的地方创建了不同的handler,所以会重复,可以使用暴力方法解决 ...
- 【科创人·独家】MegaEase左耳朵耗子陈皓复盘创业:第一年盈利被当骗子,线下广阔天地大有可为
[科创人·独家]MegaEase左耳朵耗子陈皓复盘创业:第一年盈利被当骗子,线下广阔天地大有可为 原创: babayage CTO科创圈 与上百位科技创业者共同关注科创人的成长心路. 文末有彩蛋:& ...
- Windows服务的删除与添加
Windows服务的删除与添加 以管理员身份运行 CMD 服务的删除 sc delete 服务名称 服务添加 sc create 服务名称 binpath="服务EXE文件所在的目录&quo ...
- pat 1042 Shuffling Machine(20 分)
1042 Shuffling Machine(20 分) Shuffling is a procedure used to randomize a deck of playing cards. Bec ...