“Linux内核分析”实验二报告
张文俊 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、第二周学习内容总结
1、计算机工作“三大法宝”
首先,计算机工作原理最重要的三个内容就是:存储程序计算机工作模型、中断机制和函数调用堆栈。
存储程序计算机工作模型是计算机系统最最基础性的逻辑结构;
中断机制是多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程序;
函数调用堆栈是高级语言得以运行的基础,有了高级语言及函数后,堆栈成为了计算机的基础功能,函数参数传递机制和局部变量存储。
2、详细介绍函数调用堆栈
首先,堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间,即CPU内已经集成好了很多功能。
堆栈含以下元素:
函数调用框架、传递参数、保存返回地址、提供局部变量空间、C语言编译器对堆栈的使用有一套的规则。
了解堆栈存在的目的和编译器对堆栈使用的规则是理解操作系统一些关键性代码的基础。
其次,堆栈相关的寄存器常用的共有四种,esp、ebp、push、pop。详细解释如下:
esp,堆栈指针,指向栈顶;
ebp,基址指针,指向栈底,在C语言中用作记录当前函数调用基址;
push,栈顶地址减少4个字节,从低地址向高地址;
pop,栈顶地址增加4个字节,从高地址向低地址;
最后,课程介绍了函数参数的传递与框架——
(1)建立框架,即call指令
相当于
push %ebp
movl %esp,%ebp
cs:eip原来的值指向call下一条指令,该值被保存到栈顶cs:eip的值指向function的入口地址
(2)执行函数主体代码
(3)拆除框架
相当于
movl %ebp,%esp
pop %ebp
ret
函数的返回值通过eax寄存器传递
3、参数传递
这里以老师在课堂上举出的例子:
#include <stdio.h>
void p1(char c)
{
printf("%c",c);
}
int p2(int x,int y)
{
return x+y;
}
int main(void)
{
char c ='a';
int x,y;
x =1;
y =2;
p1(c);
z = p2(x,y);
printf("%d = %d+%d",z,x,y);
}
首先,观察P2的堆栈框架:
这里,使用变址寻址方式,将x+y的值赋给eax。
然后,我们观察main是如何传递参数给P2的:
和main中的局部变量:
可以看到,汇编代码中用变址寻址把y的值和x的值存放到堆栈中,然后进行局部变量调用。
4、C代码中嵌入汇编代码
____asm____
(
汇编语句模板:
输入部分:
输出部分:
破坏描述部分:
);
二、实验二内容
本次实验内容是在mykernel基础上构造一个简单的操作系统内核。
首先我们cd进入LinuxKernel/linux-3.9.4文件,执行qemu -kernel arch/x86/boot/bzImage,可以看到窗口弹出如下:
然后,我们查看mymain.c和myinterrrupt.c文件:
可以发现,mymain是系统中唯一的进程,函数主要部分是my_start_kernel。每循环10万次,就打印一次my_start_kernel here.
myinterrupt是时间中断处理程序,每进行一次就会发生一次时钟处理中断,每次时钟中断都调用printk并输出。
程序分析:
mypcb.h
#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
/* CPU-specific state of this task */
struct Thread {
unsigned long ip;//保存eip
unsigned long sp;//保存esp
};
typedef struct PCB{
//用于表示一个进程,定义了进程管理相关的数据结构
int pid;
volatile long state; /* 定义进程的状态:-1 不可运行, 0 可运行, >0 停止 */
char stack[KERNEL_STACK_SIZE];
//内核堆栈
struct Thread thread;
unsigned long task_entry; //指定进程入口
struct PCB *next;//进程链表
}tPCB;
void my_schedule(void);//调用了my_schedule,表示调度器
mymain.c
void my_timer_handler(void)
{
#if 1
if(time_count%1000 == 0 && my_need_sched != 1)
//设置时间片的大小,时间片用完时设置一下调度标志。当时钟中断发生1000次,并且my_need_sched!=1时,把my_need_sched赋为1。当进程发现my_need_sched=1时,就会执行my_schedule。
{
printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
my_need_sched = 1;
}
time_count ++ ;
#endif return;
}
void my_schedule(void)
{
tPCB * next; //下一个进程
tPCB
* prev; //当前进程
if(my_current_task == NULL //task为空,即发生错误时返回
|| my_current_task->next == NULL)
{
return;
}
printk(KERN_NOTICE ">>>my_schedule<<<\n");
/* schedule */
next = my_current_task->next; //将当前进程的下一个进程赋给next
prev = my_current_task;//当前进程为prev
if(next->state == 0)
/* -1 unrunnable, 0 runnable, >0 stopped */
{
//在两个正在执行的进程之间做上下文切换
asm volatile( "pushl %%ebp\n\t"
/* 保存当前进程的ebp */
"movl %%esp,%0\n\t" /* 保存当前进程的esp */
"movl %2,%%esp\n\t" /* 重新记录要跳转进程的esp,将下一进程中的sp放入esp中 */
"movl $1f,%1\n\t" /* $1f指标号1:的代码在内存中存储的地址,即保存当前的eip */
"pushl %3\n\t" //将下一进程的eip压入栈,%3为 next->thread.ip
"ret\n\t" /* 记录要跳转进程的eip */
"1:\t" /* 下一个进程开始执行 */
"popl %%ebp\n\t"
: "=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 /* 与上一段代码不同的是如果下一个进程为新进程时,就运用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" /* 保存当前进程的ebp */ "movl %%esp,%0\n\t" /* 保存当前进程的esp */ "movl %2,%%esp\n\t" /* 重新记录要跳转进程的esp */
"movl %2,%%ebp\n\t" /* 重新记录要跳转进程的ebp */ "movl $1f,%1\n\t" /* 保存当前eip */ "pushl %3\n\t" "ret\n\t" /* 重新记录要跳转进程的eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip) ); }
return; }
精简内核运行结果:
三、总结
操作系统是管理计算机系统的全部硬件资源包括软件资源及数据资源;控制程序运行;改善人机界面;为其它应用软件提供支持等,使计算机系统所有资源最大限度地发挥作用,为用户提供方便有效的服务界面。
Linux是一个多进程的操作系统,所以,其他的进程必须等到正在运行的进程空闲CPU后才能运行。
当正在运行的进程等待其他的系统资源时,Linux内核将取得CPU的控制权,并将CPU分配给其他正在等待的进程,这就是进程切换。
内核中的调度算法决定将CPU分配给哪一个进程。
进程是动态执行的实体,内核是进程的管理者。
进程不但包括程序的指令和数据,而且包括程序计数器和CPU的所有寄存器以及存储临时数据的进程堆栈。
所以,正在执行的进程包括处理器当前的一切活动。
进程既可以在用户态下运行,也能在内核下运行,只是内核提供了一些用户态没有的核心服务,
因此进程在访问这些服务时会产生中断,必须进行用户态与内核态的切换。
“Linux内核分析”实验二报告的更多相关文章
- 【MOOC EXP】Linux内核分析实验二报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [操作系统是如何工作的] 教学内 ...
- 【MOOC EXP】Linux内核分析实验八报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的切换和系统的一般执行过程 知识点 ...
- 【MOOC EXP】Linux内核分析实验七报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 可执行程序的装载 知识点梳理 一.预处 ...
- 【MOOC EXP】Linux内核分析实验六报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的描述和进程的创建 知识点梳理: ...
- “Linux内核分析”实验一报告
张文俊 + 原创作品转载请注明出处 + <Linux 内核分析> MOOC 课程 实验要求: 1.总结部分要求阐明自己对“计算机是如何工作的”理解: 2.博客中需要使用实验截图: 实验内容 ...
- “Linux内核分析”实验三报告
构造一个简单的Linux系统 张文俊+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10000290 ...
- 【MOOC EXP】Linux内核分析实验四报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [使用库函数API和C代码中嵌入汇编代 ...
- 【MOOC EXP】Linux内核分析实验一报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [反汇编一个简单的C程序] 实验 ...
- 【MOOC EXP】Linux内核分析实验三报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [跟踪分析Linux内核的启动过程] ...
随机推荐
- Mysql表类型(存储引擎)的比较
面试官问:你知道mysql有哪些存储引擎,区别是啥? 我:一脸闷逼,于是乎下来补一补,以作备查 1.和大多数数据库不同,MySQL 中有一个存储引擎的概念,针对不同的存储需求可以选择最优的存储引擎. ...
- 前端工程构建工具之Yeoman
一.Yeoman 简介 通常在开发新项目时我们都需要配置工程环境,开发目录,需要下载一些库.框架文件(如 jQuery.Backbone 等),配置编译环境(Less.Sass.Coffeescrip ...
- docker-compose.md
安装 pip python 2.7+的系统同yum先安装pip命令. # yum install -y python2-pip # pip install docker-compose 网络安装 # ...
- BZOJ3676:[APIO2014]回文串(SAM,Manacher)
Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行 ...
- 当我们跑SparkSQL时候为了更好地了解SparkSQL运行,可以WEBUI看SQL的Tab
- PHP foreach 循环使用"&$val" 地址符“&”
在熟悉项目代码的时候 看到这样的foreach 循环: foreach($data as &$val){ .... } 第一次看到循环里面使用了地址符“&”,我印象中的这个符号 是直接 ...
- ORACLE RMAN备份及还原(转)
RMAN可以进行增量备份:数据库,表空间,数据文件 只有使用过的block可以被备份成backup set 表空间与数据文件对应关系:dba_data_files / v$datafile_heade ...
- Node.js实战(八)之回调函数
Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都 ...
- Vue复选框的全选
<!DOCTYPE html><html> <head> <meta charset="utf-8"> ...
- CentOS7下双网卡iptables端口转发规则
1. 拓扑图 10.1.1.173(内网目标) <-------- 10.1.1.207(内网网关)+172.16.5.100(外网入口) <----------- 172.16.6. ...