期中总结
一、MOOC课程
(一)计算机是如何工作的
1、冯诺依曼体系结构的核心思想是存储程序计算机。
2、CPU在实际取指令时根据cs:eip来准确定位一个指令。
3、寄存器模式,以%开头的寄存器标示符。
立即数是以$开头的数值。
直接寻址:直接访问一个指定的内存地址的数据。
间接寻址:将寄存器的值作为一个内存地址来访问内存。
变址寻址:在间接寻址之时改变寄存器的数值。
4、eip寄存器不能被直接修改,只能通过特殊指令间接修改。
5、在32位x86 CPU中,我们使用push和pop指令实现入栈和出栈,popl指令可以使得esp寄存器的值增加4。
6、函数的返回值默认使用eax寄存器来返回给上级函数
(二)操作系统是如何工作的
1、三个法宝:存储程序计算机、函数调用堆栈、中断机制。
2、esp 堆栈指针
ebp 基址指针(在C语言中用作记录当前函数调用基址)
3、函数调用堆栈:首先使用gcc -g生成test.c的可执行文件test,然后使用objdump -S获得test的反汇编文件。
4、函数my_start_kernel完成了0号进程的初始化和启动(入口是myprocess)。
5、在my_schedule函数中,完成进程的切换。
6、mykernel实验中,时钟中断处理函数是void my_timer_handler(void)。
7、操作系统两把宝剑:中断上下文的切换、进程上下文的切换。
(三) 构造一个简单的Linux系统MenuOS
1、start_kernel函数相当于普通C程序的main函数。
2、sched_init()进程调度初始化函数,函数内关键的初始化——对0号进程,即idle进程进行初始化;
3、Linux源码结构中,和进程间通信相关的目录是:ipc
4、MenuOS 系统的文件系统支持的命令是:help、quit、version
5、gdb中设置断点使用的命令是:break
(四) 扒开系统调用的三层皮(上)
1、系统调用的三层皮:1API(xyz)、中断向量(system_call)、中断服务程序(sys_xyz)。
2、system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即系统调用号。
3、Linux中,系统调用号是使用eax寄存器传递的。系统调用号将API xyz和中断服务程序sys_xyz关联起来。
4、Linux中可以通过执行int $128来执行系统调用。
5、Linux中,用户态切换到内核态时,int指令会保存:用户态堆栈顶地址、当时的状态字、当时的cs:eip值
6、系统是通过中断的方式将用户态转换为内核态,并通过调用系统函数来实现系统功能。
7、当系统中断出现时,CPU保护现场和上下文切换来保护目前用户态所运行的状态,并通过返回系统调用函数的值来让用户判断是否已经有效地调用,结果如何。
8、系统调用是一个软中断,中断号是0x80——通过int 0x80,触发系统调用
(五) 扒开系统调用的三层皮(下)
1、make rootfs:一个脚本,自动编译自动生成根文件系统,并自动启动MenuOS。
2、gdb
(gdb)file linux-3.18.6/vmlinux //在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 //建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)b sys_time
(gdb)c //启动到MenuOs,在MenuOs中使用time,会停在time函数处
(gdb)list //对应代码
(gdb)s //单步执行
(gdb)finish //将这个函数执行完
3、32位x86 Linux系统中系统调用处理过程的最后一条汇编指令是iret。
4、Linux内核中,系统调用处理过程中保护现场使用的宏是SAVE_ALL。
(六) 进程的描述和进程的创建
1、操作系统的三大管理功能包括:进程管理、内存管理、文件系统
2、PCB task_struct中包含:进程状态、进程打开的文件、进程优先级信息
3、通过唯一的进程标识PID来区别每个进程。
4、fork()系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。
5、fork()实际上是由clone()系统调用实现的。
6、Linux中,fork、vfork和clone三个系统调用都是通过调用do_fork来实现进程的创建。
7、Linux中,1号进程是所有用户态进程的祖先,0号进程是所有内核线程的祖先。
8、Linux中,fork()系统调用产生的子进程在系统调用处理过程中从ret_from_fork处开始执行。
(七) 可执行程序的装载
1、Linux下有三种目标文件格式,它们是:共享目标文件格式、可执行文件格式、可重定位文件格式。
2、运行时动态装载链接至少需要用到函数:dlopen、dlsym
3、一般系统调用库函数API的参数传递过程,比如execve系统调用,先进行函数调用参数传递,然后系统调用参数传递,最后又进行函数调用参数传递。
4、Linux下可以使用readelf命令查看分析ELF格式文件。
5、ELF可执行文件会被默认映射到0x8048000这个地址。
6、动态连接有两种形式:可执行程序装载时动态连接和运行时动态链接。
7、execve执行静态链接程序时,通过修改内核堆栈中保存的eip的值作为新进程的起点。
8、可执行程序加载的主要工作:当创建或者增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到一个虚拟的内存段。
9、32位x86进程地址空间共4G,内核空间是1G。
10、如果是一个静态连接的文件,elf_entry就是指的main函数开始的位置。如果是一个需要依赖动态链接库的文件,elf_entry指向的是动态链接器的起点,将cpu控制权交给ld来加载依赖库并完成动态链接。
(八) 进程的切换和系统的一般执行过程
1、不同类型的进程有不同的调度需求
第一种分类:
(1)I/O-bound:频繁进行I/O,花费很多时间等待I/O操作的完成。
(2)CPU-bound:计算密集型,需要大量CPU时间进行计算。
第二种分类:
(1)批处理进程:不必交互、很快响应。
(2)实时进程:要求响应时间短。
(3)交互式进程(shell)。
2、进程调度的时机:
(1)中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
(2)内核线程(只有内核态没有用户态的特殊进程)可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
(3)用户态进程无法实现主动调度,只能被动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
3、进程的切换
(1)为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
(2)挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
(3)进程上下文包含了进程执行需要的所有信息
1)用户地址空间:包括程序代码,数据,用户堆栈等
2)控制信息:进程描述符,内核堆栈等
3)硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
4、Linux进程调度是基于分时和优先级的。
5、Linux中,内核线程是只有内核态没有用户态的特殊进程。
6、内核可以看作各种中断处理过程和内核线程的集合。
7、Linux系统的一般执行过程 可以抽象成正在运行的用户态进程X切换到运行用户态进程Y的过程。
8、Linux中,内核线程可以主动调度,主动调度时不需要中断上下文的切换。 √
9、Linux内核调用schedule()函数进行调度,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换。
二、课本《Linux内核设计与实现》
(一) 第一章 Linux内核简介
1、操作系统是指在整个系统中负责完成最基本功能和系统管理的那些部分。
2、内核独立于普通应用程序,一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限。这种系统态和被保护起来的内存空间,统称为内核空间。
3、在系统中运行的应用程序通过系统调用来与内核通信。
4、应用程序完成其工作的基本行为方式是:应用程序通过系统调用界面陷入内核。
5、处理器的活动必然其下三者之一:
(1)运行于用户空间,执行用户进程。
(2)运行于内核空间,处于进程上下文,代表某个特定的进程执行。
(3)运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断。
6、操作系统内核可以分为两大阵营:单内核和微内核(第三阵营是外内核,主要用在科研系统中)。Linux是一个单内核。
7、内核可以直接调用函数。
8、Linux内核有两种,稳定的和处于开发中的:从副版本号如果是偶数,就是稳定版,如果是奇数,就是开发版。
(二) 第二章 从内核出发
1、获取内核源码 Git。Git是分布式的;下载和管理Linux内核源代码;
2、获取最新提交到版本树的一个副本
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
当下载代码后,更新自己的分支到最新分支 $ git pull
3、内核开发的特点:无libc库抑或标准头文件、没有内存保护机制、不轻易在内核中使用浮点数、容积小而固定的栈、同步和并发、可移植性。
4、内核有独一无二的特质。它实施自己的规则和奖惩措施,拥有整个系统的最高管理权。
(三) 第五章 系统调用
1、系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个:
(1)为用户空间提供了一种硬件的抽象接口
(2)系统调用保证了系统的稳定和安全
(3)每个进程都运行在虚拟系统中,而在用户空间和系统的其余部分提供这样一层公共接口。
2、在Linux中,系统调用是用户空间访问内核的唯一手段:除异常和陷入外,它们是内核唯一的合法入口。
3、一般情况下,应用程序通过在用户空间实现的应用编程接口(API)而不是直接通过系统调用来编程。
4、当用户空间的进程执行一个系统调用时,就用系统调用号指明到底执行哪个系统调用。进程不会提及系统调用的名称。
5、系统调用号独一无二,一旦分配就不能再有任何变更。否则编译好的应用程序就会崩溃。
6、Linux系统调用比其他许多操作系统执行得要快。原因:
(1)上下文切换时间短。
(2)系统调用处理程序和每个系统调用本身也都非常简洁。
7、通知内核的机制是软中断实现的:通过引发一个异常来促使系统切换到内核态去指向异常处理程序,而此时的异常处理程序就是系统调用的处理程序。
8、在x86系统上预定义的软中断是中断号128,通过int $0x80指令触发该中断。
9、内核在执行系统调用时处于进程上下文。current指针指向当前任务,即引发系统调用的那个进程。
10、在进程上下文中,内核可以休眠并且可以被抢占。
11、当系统调用返回时,控制权仍然在system_call()中,它最终会负责切换到用户空间,并让用户进程继续执行下去。
(四) 第十八章 调试
1、在用户级的程序里,bug表现比较直接;在内核中却不清晰。
2、内核级开发的调试工作远比用户级开发艰难的多。准备工作需要的是:
(1)一个bug
(2)一个藏匿bug的内核版本
(3)相关内核代码的知识和运气
3、引用空指针会产生一个oops;垃圾数据会导致系统崩溃。
4、内核中的printk()比比皆是:
(1)在中断上下文和进程上下文中被调用
(2)在任何持有锁时被调用
(3)在多处理器上同时被调用,并且不必使用锁。
5、printk()和printf()在使用上最主要的区别就是前者可以指定一个日志级别,内核根据这个级别来判断是否在终端上打印消息。内核把级别比某个特定值低的所有消息显示在终端上。
6、oops是内核告知用户有不幸发生的最常用的方式。
7、内核很难自我修复,也不能将自己杀死,只能发布oops,过程包括:向终端上输出错误消息、输出寄存器中保存的信息、输出可供跟踪的回溯线索。
8、oops发生时:
(1)在中断上下文时发生:内核无法继续,会陷入混乱,导致系统死机
(2)在idle进程(pid为0)或init进程(pid为1)时发生,结果同样是系统陷入混乱。
(3)在其他进程运行时发生,内核会杀死该进程并尝试着继续执行。
9、oops中包含的重要信息:寄存器上下文和回溯线索
(1)回溯线索:显示了导致错误发生的函数调用链。
(2)寄存器上下文信息也很有用,比如帮助冲进引发问题的现场
10、内核调试配置选项:内核开发菜单项中,依赖CONFIG_DEBUG_KERNEL。
11、系统请求键:sysctl来标记该特性的开或关(需要启用时:echo 1 > /proc/sys/kernel/sysrq)
12、内核调试器:gdb vmlinux /proc/kcore
13、使用Git进行二分搜索
(五) 第三章 进程管理
1、进程就是处于执行期的程序;进程就是正在执行的程序代码的实时结果;进程是处于执行期的程序以及相关的资源的总称;进程包括代码段和其他资源。
线程:是在进程中活动的对象。进程的另一个名字是任务。
2、执行线程,简称线程,是在进程中活动的对象。每个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器。
3、内核调度的对象是线程,而不是进程。Linux对线程并不特别区分,视其为特殊的进程。
4、在现代操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。在线程之间可以共享虚拟内存,但每个都拥有各自的虚拟处理器。
5、程序通过exit()系统调用退出程序。
6、进程描述符中的state域是用来描述进程当前状态的。共有五种状态,标志如下:TASK_RUNNING(运行)、TASK_INTERRUPTIBLE(可中断)、TASK_UNINTERRUPTIBLE(不可中断)、TASK_TRACED(被其他进程跟踪的进程)、TASK_STOPPED(停止)。
7、程序执行系统调用或者触发异常后,会陷入内核空间,这时候内核“代表进程执行”并处于进程上下文中。在此上下文中current宏有效。
8、进程对内核的访问必须通过接口:系统调用和异常处理程序。
9、所有的进程都是pid为1的init进程的后代。
10、内核线程:独立运行在内核空间的标准进程。内核线程没有独立的地址空间,只在内核空间运行,从来不切换到用户空间,可以被调度和被抢占。内核线程启动后就一直运行直到调用do_exit()退出,或者内核的其他部分调用kthread_stop()退出,传递给kthread_stop()的参数为kthread_create()函数返回的task_struct结构的地址。
11、当一个进程终结时,内核必须释放它所占有的资源并告知父进程。
(六) 第七章 链接
1、链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载到存储器并执行。
2、链接可以执行于编译时,加载时,运行时。
3、大多数编译系统提供编译驱动程序,它代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。
4、为了构造可执行文件,链接器必须完成两个主要任务:符号解析、重定位。
5、编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件。
6、链接器解析符号引用的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来。
7、重定位由两步组成:重定位节和符号定义、重定位节中的符号引用
8、无论何时汇编器遇到对最终位置未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。
9、共享库的一个主要目的就是允许多个正在运行的进程共享存储器中相同的库代码,因而节约宝贵的存储器资源。
10、不需要链接器修改库代码就可以在任何地址加载和执行这些代码。这样的代码叫做与位置无关的代码(PIC)。
(七) 第四章 进程调度
1、多任务操作系统就是能同时并发的交互执行多个进程的操作系统。
2、多任务操作系统使多个进程处于堵塞或者睡眠状态,实际不被投入执行,这些任务尽管位于内存,但是并不处于可运行状态。
3、多任务系统分类:非抢占式多任务、抢占式多任务
4、Linux提供了抢占式的多任务模式。
5、O(1)调度器拥有数以十计的多处理器的环境,但缺少交互进程。
6、反转楼梯最后期限调度算法(RSDL),吸取了队列理论,公平调度。又被称为完美公平调度算法(CFS)。
7、进程可以分为:I/O消耗型、处理器消耗型
8、调度策略:尽量降低它们的调度频率,延长其运行时间。
9、调度策略通常要在两个矛盾的目标中间寻找平衡:
(1)进程调度迅速(响应时间短)
(2)最大系统利用率(高吞吐量)
10、Linux倾向于优先调度I/O消耗型进程。
11、调度算法中最基本的一类就是基于优先级的调度,这是一种根据进程的价值和其对处理器时间的需求来对进程分级的想法。调度程序总是选择时间片未用尽而且优先级最高的进程运行。
12、Linux采用了两种不同的优先级范围:
(1)nice
范围[-20,19],默认值为0;nice值越大,优先级越低;
Linux系统中nice值代表时间片的比例,可以通过ps-el命令查看系统中进程列表,结果中标记NI的一列及时进程对应的nice值。
(2)实时优先级
其值可以配置,默认变化范围是[0,99];值越高优先级越高;
13、任何实时进程的优先级都高于普通的进程,也就是说实时优先级和nice优先级处于互不相交的两个范畴。
14、时间片表示进程在被抢占前所能持续运行的时间。I/O消耗型进程不需要很长的时间片,而处理器消耗型进程希望时间片越长越好。
15、Linux调度器是以模块方式提供,目的是允许不同类型的进程可以有针对性地选择调度算法。
16、CFS的做怯是允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程,而不再采用分配给每个进程时间片的做法了,在所有可运行进程总数基础上计算出一个进程应该运行多久。而不是依靠nice 值来计算时间片。
17、nice 值在 CFS 中被作为进程获得的处理器运行比的权重:越高的nice 值(越低的优先级)进程获得更低的处理器使用权重。
18、任何进程所获得的处理器时间是由它自己和其他所有可运行进程nice 值的相对差值决定的。
19、CFS调度算法的实现:
四个组成部分:时间记账、进程选择、调度器入口、睡眠和唤醒
20、休眠有两种相关的进程状态:TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE。唯一区别是处于TASK_UNINTERRUPTIBLE的进程会忽略信号,而处于TASK_INTERRUPTIBLE状态的进程如果接收到一个信号,会被提前唤醒并响应该信号
21、用户抢占在以下情况时产生:
(1)从系统调返回用户空间时;
(2)从中断处理程序返回用户空间时;
22、内核抢占会发生在:
(1)中断处理程序正在执行,且返回内核空间之前
(2)内核代码再一次具有可抢占性的时候。
(3)如果内核中的任务显式地调用 schedule()
(4)如果内核中的任务阻塞(这同样也会导致调用schedule())。
23、Linux提供了两种实时调度策略:SCHED_FIFO和 SCHED_RR。而普通的、非实时的调度策略是SCHED_NORMAL。
三、总结
光阴似箭,日月如梭,不知不觉已经过了半个学期,在这半个学期中,通过MOOC课程的视频学习、课本的自学以及课上老师的讲解,获得了很多知识。知道了计算机、操作系统是如何工作的,学会了构造一个简单的Linux系统MenuOS,理解了系统调用的三层皮:1API(xyz)、中断向量(system_call)、中断服务程序(sys_xyz)。进程的描述和进程的创建等内容。
课堂上老师会对网课进行总结梳理、答疑解惑,让我们对知识的理解更加深刻。最后,谢谢老师的悉心教导!
四、每周MOOC课程笔记链接汇总
[第一周——计算机是如何工作的] (http://www.cnblogs.com/20135235my/p/5219970.html)
[第二周——操作系统是如何工作的] (http://www.cnblogs.com/20135235my/p/5237267.html)
[第三周——构造一个简单的Linux系统MenuOS] (http://www.cnblogs.com/20135235my/p/5268446.html)
[第四周——扒开系统调用的三层皮(上)] (http://www.cnblogs.com/20135235my/p/5291918.html)
[第五周——扒开系统调用的三层皮(下)] (http://www.cnblogs.com/20135235my/p/5322737.html)
[第六周——进程的描述和进程的创建] (http://www.cnblogs.com/20135235my/p/5349690.html)
[第七周——可执行程序的装载] (http://www.cnblogs.com/20135235my/p/5372261.html)
[第八周——进程的切换和系统的一般执行过程] (http://www.cnblogs.com/20135235my/p/5400741.html)
五、每周读书笔记链接汇总
[第一章——Linux内核简介] (http://www.cnblogs.com/20135235my/p/5291959.html)
[第二章——从内核出发] (http://www.cnblogs.com/20135235my/p/5291992.html)
[第三章——进程管理] (http://www.cnblogs.com/20135235my/p/5349711.html)
[第四章——进程调度] (http://www.cnblogs.com/20135235my/p/5398066.html)
[第五章——系统调用] (http://www.cnblogs.com/20135235my/p/5322773.html)
[第七章——链接] (http://www.cnblogs.com/20135235my/p/5372229.html)
[第十八章——调试] (http://www.cnblogs.com/20135235my/p/5347846.html)