Linux内核分析之操作系统是如何工作的
在本周的课程中,孟老师主要讲解了操作系统是如何工作的,我根据自己的理解写了这篇博客,请各位小伙伴多多指正。
一、知识点总结
1. 三个法宝
存储程序计算机:所有计算机基础性的逻辑框架。
堆栈:高级语言的起点,函数调用需要堆栈机制。
中断机制:多道系统的基础,是计算机效率提升的关键。
2. 函数调用堆栈
堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间,即CPU内已经集成好了很多功能。
堆栈含以下元素:
函数调用框架
传递参数
保存返回地址(%eax)
提供局部变量空间 等等
C语言编译器对堆栈的使用有一套的规则。
堆栈相关的寄存器:
esp,堆栈指针
ebp,基址指针
堆栈操作:
push,栈顶地址减少4个字节(32位)
pop,栈顶地址增加4个字节 注:ebp在C语言中用做记录当前函数调用基址。
其他关键寄存器:
cs:eip:指向地址连续的下一条指令
顺序执行:总是指向地址连续的下一条指令
跳转/分支:执行这样的指令的时候,cs:eip的值会根据程序需要被修改
call:将当前cs:eip的值压入栈顶,cs:eip指向被调用函数的入地址
ret:从栈顶弹出原来保存在这里的cs:eip的值,放入cs:eip中 发生中断时
二、Mykernel精简内核程序实验
1.实验过程
首先用下面两条命令
cd LinuxKernel/linux-3.9.
qemu -kernel arch/x86/boot/bzImage

由图可知:每执行my_ start_ kernel函数一次或两次,my_ time_ hander函数就执行一次。

mymain.c ——系统中唯一的进程。mystartkernel之前的都是硬件初始化的工作,之后是整个操作系统的入口,开始执行操作系统。其中代码完成的工作是每循环10000次,打印一句话。

myinterrupt.c ——时间中断处理程序

每执行一次,都会执行一次时钟中断,每次时钟中断都调用printk并输出。
以上的实验环境仅仅是模拟了时钟中断,一直都是一个进程的run。需要加入下面的代码,才能开始进程切换调度。
需要在mykernel下重新更新文件:myinterrupt.c,主要负责中断以及进程切换;mymain.c,初始化系统环境,mypcb.h描述了进程控制块的定义。
再次到Linux3.9.4目录下重新编译,用下面命令:
make allnoconfig
make
qemu -kernel arch/x86/boot/bzImage

2.进程的启动和进程的切换机制
进程的启动:从void__init my_start_kernel(void)开始启动,函数前面是一些初始化工作,包括对于进程的fork。每个进程初始时,除了pid之外,对于所定义的pcb来说,其他都是相同的,以一个链表来组织各进程。嵌入式汇编代码的主要作用就是初始化第0号进程,将0号进程的esp写入esp寄存器,由于进程还未开始执行,因此进程的esp以及ebp都应该是指向栈底的(空栈),另外,0号进程的eip在这里已经指向了my_process代码段地址,ret之后,开始执行0号进程。
void __init my_start_kernel(void)
{
int pid = ;
int i;
/* Initialize process 0*/
task[pid].pid = pid;
task[pid].state = ;/* -1 unrunnable, 0 runnable, >0 stopped */
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-];
task[pid].next = &task[pid];
/*fork more process */
for(i=;i<MAX_TASK_NUM;i++)
{
memcpy(&task[i],&task[],sizeof(tPCB));
task[i].pid = i;
task[i].state = -;
task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-];
task[i].next = task[i-].next;
task[i-].next = &task[i];
}
/* start process 0 by task[0] */
pid = ;
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*/
);
}
进程的切换:通过schedule()函数进行进程切换,挡发生时钟中断的时候,中断处理程序会将需要调度的flag置为1,而与此同时,每一个当前执行的进程都在轮询,如果一旦flag被置为了1,则开始进行进程切换。case1: 进程开始切换的时候,是切换到一个进程状态为已经在运行的进程。在切换进程之前,将1f存到prev的eip中,1f指的是被标记为1的地址,ret之后,弹栈next的eip,开始执行next进程。
if(next->state == )/* -1 unrunnable, 0 runnable, >0 stopped */
{
/* switch to next process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
"1:\t" /* next process start here */
"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);
}
case2:进程切换到一个新进程。由于这里所切换的进程是一个新进程,所以这里的state置为0(running状态),这里的movl $1f,%1\n\t中的1f在进程再次调度回来的时候,走的是case1的分支代码块,所以case2中的 中没有标记为1的代码块,也没有pop ebp的操作。
else
{
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" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl %2,%%ebp\n\t" /* restore ebp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
三、对操作系统如何工作的理解
操作系统是管理计算机系统的全部硬件资源包括软件资源及数据资源;控制程序运行;改善人机界面;为其它应用软件提供支持等,使计算机系统所有资源最大限度地发挥作用,为用户提供方便有效的服务界面。linux内核从一个初始化上下文环境的函数开始执行,即start_kernel函数,创建很多进程或者fork若干进程,当中断发生的时候,如mykernel中就是时钟中断发生之后,接下来OS就会为各进程进行调度,在调度队列中选取出一个适合的进程,由CPU和内核堆栈保存前一个进程的各寄存器信息(进程描述块中的thread结构体保存大部分CPU寄存器,但是诸如eax、ebx等等这些通用寄存器是保存在内和堆栈中的),将eip指向要调度的进程执行的地址,开始执行。在本次课程中,我对于嵌入汇编的内容仍不熟练,所以为了更深入理解Linux需加强对汇编和操作系统的理解。
刘帅
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Linux内核分析之操作系统是如何工作的的更多相关文章
- linux 内核 第二周 操作系统是如何工作的
姬梦馨 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一:计算机的三个法宝 存储程序计算机工 ...
- Linux内核分析之计算机是如何工作的
一.计算机工作原理 本周实验主要是反汇编C代码,生成汇编程序.冯·诺依曼理论的要点是:数字计算机的数制采用二进制,计算机应该按照程序顺序执行.人们把冯·诺依曼的这个理论称为冯·诺依曼体系结构.CPU通 ...
- linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码
计算机如何工作 三个法宝:存储程序计算机.函数调用堆栈.中断机制. 堆栈 函数调用框架 传递参数 保存返回地址 提供局部变量空间 堆栈相关的寄存器 Esp 堆栈指针 (stack pointer) ...
- 《Linux内核分析》第二周 操作系统是如何工作的?
[刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK TWO(2 ...
- Linux内核分析——操作系统是如何工作的
万子惠 + 原创作品转载请注明出处 + <Linux内核分析> 实验部分 使用实验楼的虚拟机打开shell 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain. ...
- Linux内核分析第二周学习总结:操作系统是如何工作的?
韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.函数调用堆栈 ...
- Linux内核分析— —操作系统是如何工作的(20135213林涵锦)
mykernel实验指导(操作系统是如何工作的) 实验要求 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3. ...
- Linux内核分析第二周:操作系统是如何工作的
第一讲 函数调用堆栈 计算机是如何工作的? (总结)——三个法宝 1,存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 2,函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆 ...
- 《Linux内核分析》第二周笔记 操作系统是如何工作的
操作系统是如何工作的 一.函数调用堆栈 1.三个法宝 计算机是如何工作的?(总结)——三个法宝(存储程序计算机.函数调用堆栈.中断机制) 1)存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: ...
随机推荐
- Contos7 装bcm4312无线网卡驱动
本次装网卡比较的无语,报错网上竟然找不到答案,误打误撞给装好了,做下记录以后可能会用的上. 首先去官网下载网卡驱动:http://www.broadcom.com/support/802.11 我系统 ...
- js实现日历卡
效果图如下 首先先添加简单的样式 <style type="text/css"> *{padding:0;margin:0;} #tab { margin:0 auto ...
- 自定义UITableViewCell
随着日常的使用,系统提供的cell已经不能满足开发的需要,因为系统提供的是单一的,所以 这就引来了自定义cell的出现,可以根据 自己的需要来布局各个控件所处的位置.不同位置显示不同的控件. 创建一个 ...
- docker-compose安装使用
Docker Compose的工作原理 Docker Compose将所管理的容器分为三层,工程(project),服务(service)以及容器(contaienr).Docker Compose运 ...
- ASP.NET实现大文件下载
https://support.microsoft.com/zh-cn/kb/812406 http://www.cnblogs.com/luisliu/p/4253815.html 当我们的网站需要 ...
- C# 定制 Attribute 简单使用
所谓 “定制Attribute”,就是一个类的实例,它被序列化成驻留在元数据的一个字节流. 我们可以使用 Attribute 来保存注释: namespace AttributeDemo { [Att ...
- static 变量
被static 修饰的变量全部称为静态变量.所有的静态变量全部存储在静态存储区.按静态变量定义的位置不同,又分为全局静态变量和局部静态变量. 1)全局静态变量 在全局变量的说明前加上static,就是 ...
- 如何在A用户下建立视图,这个视图是A的表与B的表进行关联的?
这个前提条件是,同一个数据库,不同用户!!!如果是不同数据库,就要用dblink了 一开始,我直接创建视图,但是提示“权限不足”: 于是我是用A登陆,直接用select * from B.sa_tas ...
- asp.net GridView控件的列属性
BoundField 默认的数据绑定类型,通常用于显示普通文本 CheckBoxField 显示布尔类型的数据.绑定数据为TRUE时,复选框数据绑定列为选中状态:绑定数据为FALSE时,则显示未选中状 ...
- ORACLE rowid切分大表
通过如下sql获取rowid切分范围 ) || dbms_rowid.rowid_create(, DOI, lo_fno, lo_block, ) ) || ) || dbms_rowid.rowi ...