2019-2020-1 20199324《Linux内核原理与分析》第三周作业
第二章 操作系统是如何工作的
一.知识点总结
1.计算机的三个法宝
- 存储程序计算机
- 函数调用堆栈机制。堆栈:是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间。
- 中断
2.堆栈相关的寄存器和堆栈操作
- ESP(栈顶指针寄存器):堆栈指针(指向栈顶)
- EBP(基址指针寄存器):基址指针(指向栈顶),在C语言中用作记录当前函数调用的基址。
- EAX:用于暂存一些数值,函数返回值默认使用EAX寄存器存储并返回给上一级调用函数。
- EIP:指示将要执行的下一条指令在存储器中的地址。(总是指向某一条指令的地址)
- CS(代码段寄存器)
- push 压栈,栈顶地址减少四个字节
- pop 出栈,栈顶地址增加四个字节
- call 函数调用,调用一个地址。将当前CS:EIP的值压入栈顶,CS:EIP指向被调用函数的入口地址。
- ret 函数返回,从栈顶弹出原来保存在这里的CS:EIP的值,放入CS:EIP中去。
- enter 用来建立函数堆栈
- leave 用来撤销函数堆栈
3.内嵌汇编
/*
**通过例子来熟悉内嵌汇编的语法规则
*/
#include <stdio.h>
int main(){
unsigned int val1 = 1;
unsigned int val2 = 2;
unsigned int val3 = 0;
printf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3);
asm volatile(
"movl $0,%%eax\n\t" //把EAX清0
"addl %1,%%eax\n\t" //把ECX的值与EAX寄存器求和,然后放到EAX寄存器中去。%eax += val1
"addl %2,%%eax\n\t" //把val2的值加上val2的值再放到EAX中。%eax += val2
"movl %%eax,%0\n\t" //val1+val2的值存储的地方放到内存val3中。val3 = %eax
: "=m" (val3)
: "c" (val1),"d" (val2)
);
printf("val1:%d+val2:%d=val3:%d\n",val1,val2,val3);
return 0;
}
内嵌汇编常用的限定符
|||||||
|:--|:--|
|限定符|描述|
|"a"|将输入变量放入EAX|
|"b"|将输入变量放入EBX|
|"c"|将输入变量放入ECX|
|"d"|将输入变量放入EDX|
|"s"|将输入变量放入ESI|
|"D"|将输入变量放入EDI|
|"r"|将输入变量放入通用寄存器,也就是EAX,EBX,ECX,EDX,ESI,EDI中的一个|
|"eax"|破坏描述部分|
|"m"|内存变量|
|"="|操作数在指令中只是写的(输出操作数)|
|"+"|操作数在指令中是读写类型的(输入输出操作数)|
小结
- 寄存器前面会多一个%转义字符,有两个%
- 输出输入的编号分别为%1,%2,%3......
- 嵌入式汇编的时候每一个输入或输出的部分前面都可以加一个限定符
4.虚拟一个x86的CPU硬件平台
重要指令
$ 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 /*查看搭建起来的内核启动效果*/
搭建起来的内核启动效果如下所示:
在Linux-3.9.4内核源代码根目录下进入mykernel目录,可以看到QEMU窗口输出的mymain.c和myinterrupt.c的代码。
可以看到mymain.c中的函数my_start_kernel(void)函数,每执行100000次要输出“my_start_kernel here i”。
mymain.c中的代码在不停地被执行,同时有一个中断处理程序的上下文环境,周期性地产生时钟中断信号,能够触发mykernel.c中的my_timer_hander(void)函数。这样就模拟了一个带有时钟中断的x86CPU。
5.关键代码分析
1.mypcb.h头文件,用来定义进程控制块。
2.mymain.c 是mykernel内核代码的入口,负责初始化内核的各个组成部分。
asm volatile(
"movl %1,%%esp\n\t" /*将进程原堆栈的栈底的地址存入ESP寄存器中*/
"pushl %1\n\t" /*将当前ESP寄存器的值入栈*/
"pushl %0\n\t" /*将当前进程的EIP寄存器的值入栈*/
"ret\n\t" /*让入栈的进程EIP保存到EIP寄存器中*/
"popl %%ebp\n\t" /*这里不会被执行,只是一种编码习惯,与前面的push结对出现*/
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
堆栈变化:
3.myinterrupt.c 时钟中断处理和进程调度
if(next->state == 0)
{
/*进程调度关键代码*/
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)
);
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
}
else /*next该进程第一次被执行*/
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
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进程的堆栈基地址到EBP寄存器*/
"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从来没有被执行过,是第一次被执行,所以执行else中的代码。下面来分析进程1被调度的堆栈变化。
此时,开始执行进程1,如果进程1执行的过程中发生了进程调度,进程0重新被调度执行,此时应该执行if中的代码。if中的内嵌汇编代码执行过程中堆栈变化分析如下:
2019-2020-1 20199324《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语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- BUUCTF-WEB-easy_tornado
知识点: Python Web 框架:Tornado python中的一个渲染函数:render: 渲染变量到模板中,即可以通过传递不同的参数形成不同的页面. 1. render方法的实质就是生成te ...
- HTTP协议(二):作用
前言 上一节我们简单介绍了一下TCP/IP协议族的基本情况,知道了四大层的职责,也了解到我们这一族的家族成员以及他们的能力. 无良作者把我这个主角变成了配角,让我很不爽,好在我打了作者一顿,没错,这次 ...
- 洛谷 P1964 【mc生存】卖东西(多重背包)
题目传送门 解题思路: 题目里有,多重背包. AC代码: #include<iostream> #include<cstdio> #include<map> usi ...
- Cracking Digital VLSI Verification Interview 第三章
目录 Programming Basics Basic Programming Concepts Object Oriented Programming Concepts UNIX/Linux Pro ...
- spyder.app制作图标
安装了 anaconda3, 自带spyder, 但是只能在terminal 中打开, 非常不友好. 模仿 anaconda3/目录下 Anaconda-Navigator.app, 制作了 spyd ...
- Codeforces Round #620 (Div. 2)E LCA
题:https://codeforces.com/contest/1304/problem/E 题意:给定一颗树,边权为1,m次询问,每次询问给定x,y,a,b,k,问能否在原树上添加x到y的边,a到 ...
- CentOS7下MySQL8的二进制基本安装配置
前言 基于本地Centos7.6虚拟机Mysql8的配置(亲测有效) 一.安装前的准备 1.到官网下载mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz 2.通过Xs ...
- 基于libcurl的POST(http)
#include <stdio.h> #include <curl/curl.h> int main (void) { char *url="http://www.n ...
- Mariadb-10.2.25 多实例
Mariadb-10.2.25 多实例 定义目录 mkdir -p /mysql/{3306,3307,3308}/{bin,data,etc,log,pid,socket} 生成数据库文件 /app ...
- Python dict 字典 keys和values对换
原字典: d1 = { 'en':'英语', 'cn':'中文', 'fr':'法语', 'jp':'日语' } 经过相互对换: d1_inverse = {values:keys for keys, ...