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语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- 微信小程序的canvas和遮盖布颜色设置问题
canvas绘画出并显示小程序的逻辑首先是将网络图片转化为本地图片,其次再将进行绘画.将本地图片和二维码显示在画布上,最后将整个画布截图用api显示在屏幕上.真正的画图让他飞去屏幕外. 有时候会需要用 ...
- (数据科学学习手札70)面向数据科学的Python多进程简介及应用
本文对应脚本已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 一.简介 进程是计算机系统中资源分配的最小单位,也是操作系 ...
- MYSQL5.7修改密码强度策略
---恢复内容开始--- 在MySQL5.6.6之后,ORACLE更新了mysql密码强度必须要使用大小写数字符号来设置密码,但是有时候这样还是很不方便的.所以记录一篇如何修改mysql密码强度的博文 ...
- LVS 十种算法
LVS虚拟服务器是章文嵩在国防科技大学就读博士期间创建的,LVS可以实现高可用的.可伸缩的网络服务. LVS集群组成: 前端:负载均衡层 (一台或多台负责调度器构成) 中间:服务器群组层 (由一组 ...
- TCP/IP协议第一卷第二章
环回接口: 127全网段均被作为环回地址. 传给广播地址或多播地址的数据报复制一份给环回接口,然后传送到以太网上.这是因为广播传送和多播传送的定义包含自己本身. 任何传给该主机IP地址的数据均送到环回 ...
- 模板(ac):启发式合并
首先说明一点:线段树合并不是启发式合并. 启发式合并的大概内容就是:把小的数据结构按照这个数据结构的正常插入方法,一个一个地暴力塞进去. 而线段树合并显然不是这个东西. 这道题的题解太烂了,所以耽误了 ...
- centos7上以RPM方式安装MySQL5.6
1. 下载MySQL http://ftp.ntu.edu.tw/MySQL/Downloads/MySQL-5.6/ MySQL-5.6.36-1.el7.src.rpm MySQL-5.6.36- ...
- MQ应用之解耦
简介 消息队列 MQ 既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积.高吞吐.可靠重试等特性. 应用场景 削峰填谷:诸如秒杀.抢红包.企业开门红等大型活动时皆 ...
- IDEA中WEB项目本地调试和发布的配置分开配置
一个Web项目,开发的时候设置了一些本地内容,比如IP地址,还有本地目录等.开发完成后,要发布到服务器上时,这些本地相关的配置,就需要配置成服务器上IP或目录. 原先的做法就是部署打包的时候,把相关的 ...
- 【NServiceBus】什么是Saga,Saga能做什么
前言 Saga单词翻译过来是指尤指古代挪威或冰岛讲述冒险经历和英雄业绩的长篇故事,对,这里强调长篇故事.许多系统都存在长时间运行的业务流程,NServiceBus使用基于事件驱动的 ...