20169210《Linux内核原理与分析》第十周作业
第一部分:实验
进程的调度时机与进程的切换
操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已。
对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键。
进程调度的时机
- 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
- 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
- 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
进程的切换
- 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
- 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
- 进程上下文包含了进程执行需要的所有信息
- 用户地址空间:包括程序代码,数据,用户堆栈等
- 控制信息:进程描述符,内核堆栈等
- 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
- schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
- next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部
- context_switch(rq, prev, next);//进程上下文切换
- switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程
switch_to函数如下所示:
31#define switch_to(prev, next, last)
32do {
33 /*
34 * Context-switching clobbers all registers, so we clobber
35 * them explicitly, via unused output variables.
36 * (EAX and EBP is not listed because EBP is saved/restored
37 * explicitly for wchan access and EAX is the return value of
38 * __switch_to())
39 */
40 unsigned long ebx, ecx, edx, esi, edi;
41
42 asm volatile("pushfl\n\t" /* 保存当前进程flags */
43 "pushl %%ebp\n\t" /* 当前进程堆栈基址压栈*/
44 "movl %%esp,%[prev_sp]\n\t" /*保存ESP,将当前堆栈栈顶保存起来*/
45 "movl %[next_sp],%%esp\n\t" /*更新ESP,将下一栈顶保存到ESP中*/
//完成内核堆栈的切换
46 "movl $1f,%[prev_ip]\n\t" /*保存当前进程EIP*/
47 "pushl %[next_ip]\n\t" /*将next进程起点压入堆栈,即next进程的栈顶为起点*/
48
//完成EIP的切换
__switch_canary
//next_ip一般是$1f,对于新创建的子进程时ret_from_fork
49 "jmp __switch_to\n" /*prev进程中,设置next进程堆栈*/
//jmp不同于call是通过寄存器传递参数
50 "1:\t" //next进程开始执行
51 "popl %%ebp\n\t"
52 "popfl\n"
53
54 /*输出变量定义*/
55 : [prev_sp] "=m" (prev->thread.sp), //[prev_sp]定义内核堆栈栈顶
56 [prev_ip] "=m" (prev->thread.ip), //[prev_ip]当前进程EIP
57 "=a" (last),
58
59 /* 要破坏的寄存器: */
60 "=b" (ebx), "=c" (ecx), "=d" (edx),
61 "=S" (esi), "=D" (edi)
62
63 __switch_canary_oparam
64
65 /* 输入变量: */
66 : [next_sp] "m" (next->thread.sp), //[next_sp]下一个内核堆栈栈顶
67 [next_ip] "m" (next->thread.ip),
68 //[next_ip]下一个进程执行起点,,一般是$1f,对于新创建的子进程是ret_from_fork
69 /* regparm parameters for __switch_to(): */
70 [prev] "a" (prev),
71 [next] "d" (next)
72
73 __switch_canary_iparam
74
75 : /* 重新加载段寄存器 */
76 "memory");
77} while (0)
使用的还是time命令
test.c函数加的关于time的内容。
#include <time.h>
#include "menu.h"
int Time(int argc, char *argv[])
{
time_t tt;
struct tm *t;
tt = time(NULL);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return 0;
}
int TimeAsm(int argc, char *argv[])
{
time_t tt;
struct tm *t;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0xd,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return 0;
}
MenuConfig("time","Show System Time",Time);
MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
使用gdb跟踪schedule函数
设置断点
按C往下执行,跟踪到schedule(),调用了__schedule()函数。
进入__schedule()函数内部如图所示
往下执行可以看到找下一个要运行的进程。
__schedule()函数内部有一句判断是否需要进行上下文切换的语句。context_switch()是进程上下文切换调用的函数。
但是再往下就执行不下去了。
Linux系统的一般执行过程
最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程
1.正在运行的用户态进程X
2.发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
3.SAVE_ALL //保存现场
4.中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换
5.标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)
6.restore_all //恢复现场
7.iret - pop cs:eip/ss:esp/eflags from kernel stack
8.继续运行用户态进程Y
几种特殊情况
- 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
- 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
- 创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
- 加载一个新的可执行程序后返回到用户态的情况,如execve;
第二部分:教材
进程地址空间
进程地址空间是指用户空间中进程的内存,是每个用户空间进程所看到的内存。Linux采用虚拟内存技术,进程之间以虚拟的方式共享内存,每个进程好像都可以访问整个系统的所有物理内存。
进程地址空间由进程可寻址的虚拟内存组成。可以被访问的合法地址空间称为内存区域,进程只能访问有效内存区域内的内存地址。访问了不在有效范围内的内存区域或者以不正确的方式访问了有效地址的进程会被内存终止,并返回“段错误”信息。
内存区域包含各种内存对象:
- 代码段,即可执行文件代码的内存映射。
- 数据段,即可执行文件的已初始化全局变量的内存映射。
- 包含未初始化全局变量即bss段的零页的内存映射。
- 用于进程用户空间栈的零页的内存映射。
- 每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss段也会被载入进程的地址空间。
- 任何内存映射文件。
- 任何共享内存段。
- 任何匿名的内存映射,如malloc分配的内存。
页高速缓存和页回写
写缓存有三种策略:
- 不缓存,高速缓存不缓存任何写操作,跳过缓存写到磁盘上,同时使缓存中数据失效。
- 写透缓存,自动更新内存缓存,同时也更新磁盘文件。
- “回写”,写操作直接写到缓存中,后端存储不会立刻直接更新,将页高速缓存中被写入的页面标记成“脏”,由回写进程将脏页写回磁盘。(Flusher内核线程负责)
20169210《Linux内核原理与分析》第十周作业的更多相关文章
- 20169210《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...
- 20169212《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...
- 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 20199329《Linux内核原理与分析》第九周作业
<Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...
- 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语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- LED汽车前大灯
一.LED汽车前大灯遇到问题.分析和解决 问题1: 当电源电压增大时,LED等闪烁,而且电源电压增大的越多闪烁的频率越低. 原因分析: 电源电压从12V升高到24V过程中,开关MOS管的Vds增大,Q ...
- CocoaPods安装和使用及问题:Setting up CocoaPods master repo-b
目录 CocoaPods是什么? 如何下载和安装CocoaPods? 如何使用CocoaPods? 场景1:利用CocoaPods,在项目中导入AFNetworking类库 场景2:如何正确编译运行一 ...
- Android WebView缓存分析
http://blog.csdn.net/a345017062/article/details/8703221 WebView的缓存可以分为页面缓存和数据缓存. 页面缓存是指加载一个网页时的htm ...
- 使用SeaJS实现模块化JavaScript开发(新)
本文转自张洋,因为SeaJS更新版本很快,所以原文中很多地方不太适用,在这里发布一个更新版. 前言 SeaJS是一个遵循CommonJS规范的JavaScript模块加载框架,可以实现JavaSc ...
- 有很多功能交给IIS实现就好了啊,之前干嘛要自己写?
比方说设置默认主页啊,错误页伪静态之类的
- hdu 4445
今天模拟了一场去年金华的现场赛: 我和小珺两人出了5个题,感觉还可以: 不过这次的题目确实比较简单: 这个题目感觉不错,不难,以前见过用这种方法的,但一直没写过: 这次写下练练手: 思路,将角度分成1 ...
- IronPython脚本调用C#dll示例
上篇Python脚本调用C#代码数据交互示例(hello world)介绍了与C#紧密结合的示例,这里还将提供一个与C#结合更紧密的示例,直接调用C#编写的DLL. 我们还是沿用了上篇文章的 ...
- USB 2.0 A型、B型、Mini和Micro接口定义及封装
USB全称Universal Serial Bus(通用串行总线),目前USB 2.0接口分为四种类型A型.B型.Mini型还有后来补充的Micro型接口,每种接口都分插头和插座两个部分,Micro还 ...
- Android用户界面UI组件--AdapterView及其子类(五) Spinner和SpinnerAdapter
Spinner就是下拉框组件,可以自定义下拉布局样式,可以使用ArrayAdapter以及SpinnerAdapter适配 在Adapter中实现SpinnerAdapter,继承BaseAdapte ...
- Python自然语言处理学习笔记(69)
http://www.cnblogs.com/yuxc/archive/2012/02/09/2344474.html Chapter8 Analyzing Sentence Structure ...