计算机体系的基本工作原理
冯诺伊曼体系结构
其要点是存储程序计算机。着重说明了计算机的基本执行逻辑:取指令和执行指令。同时也介绍了计算机存储系统的层次结构,由快到慢为寄存器、内存、硬盘、互联网分布式存储。以及计算机的总线结构和可寻址的地址空间。
基本汇编指令
为了与Linux内核的汇编形式一致,这里采用AT&T汇编。
基本汇编的语法规则:
movl %eax, %edx 把eax的内容放到edx中
movl $0x123, %edx 把0x123的十六进制数值放到edx中,开头有$符号表示这是一个立即数
movl 0x123, %edx 把十六进制0x123内存地址指向的内存中存储的数据放到edx中,开头无$符号表示这是一个地址
movl (%ebx), %edx 把ebx寄存器中存储的数据当作内存地址,把这个内存地址存储的数据放到edx中
movl 4(%ebx), %edx 在上一条内存地址的基础上加立即数4,取出对应地址中存储的数据放入edx中
对内存堆栈的操作:
pushl、popl、subl等。
函数调用与返回指令:
call指令实际做的就是将eip寄存器的当前值压栈并将要调用函数的地址赋给eip。
出于安全方面的原因,eip寄存器不能被程序员直接使用。
ret指令实际做的事就是出栈并将值赋给eip。
函数调用时的堆栈框架
可以发现调用前后有一部分汇编代码是通用的。
深入理解系统调用
Linux的简要结构
用户态和内核态
用户态和内核态是Linux对程序权限的划分。内核态的程序可以执行特权指令,并且可以访问全部的内存地址。
用户态程序只能访问一部分内存地址。这样划分权限可以保证操作系统的稳定和安全。
中断
中断主要是是为了满足处理器和外设的速度不匹配而设置的一种机制。当外设忙于处理任务时处理器可以去做别的事情。同时中断也用于进程间的切换。
中断的分类
中断分为外中断和内中断。外中断是由外部设备所引起的中断,如打印机、键盘等。内中断是cpu内部产生的中断,内中断也称为trap。
系统调用与中断的关系
系统调用是一种特殊的中断。系统调用就是利用陷阱这种方式从用户态进入内核态的。
中断与异常的区别
中断是异步的,由硬件随机产生。在程序执行的任何时候都有可能出现。异常是同步的,在执行指令时由CPU控制单元产生(除零错误等)。
系统调用
系统调用把用户与底层的硬件隔离开来,提高了系统的安全性与稳定性。系统编程接口的API就是系统调用的库函数,一个API可能对应一个或多个系统调用,一个系统调用也可能对应多个API。
Linux内核通过系统调用号来确定需要调用那个系统调用。
Linux通过执行int $0x80或syscall指令来触发系统调用的执行,进入内核后开始执行相应的中断服务程序。Linux系统调用的参数传递是通过寄存器传参的方式。
中断上下文切换——以系统调用为例
系统调用实质上是一种特殊的中断,触发时会保存当前执行程序的栈顶地址、内核的状态字等。并且CS:EIP寄存器会指向中断处理程序的入口,对于系统调用来说就是指向系统调用的入口。
可执行程序的工作原理:
ELF概述
Linux上的可执行文件称为ELF,ELF文件参与程序的链接和执行。ELF分为三种类型:可重定位文件、可执行文件以及动态链接库文件。
ELF文件格式
程序结果汇编后的.o文件已经是ELF格式文件。分为BSS段,存放程序中未初始化的全局变量。数据段,存放程序中已初始化的全局变量。代码段,存放程序执行的代码。
符号表的功能是找未知函数在其他库文件中的代码段的具体位置。
重定位
重定位是把程序的逻辑地址空间变换成进程线性地址空间的过程。也就是链接时对目标程序中指令和数据地址修改的过程。
静态链接与动态链接
静态链接和动态链接:静态链接在链接时直接将需要的代码复制到最终可执行文件中,优点是执行快,环境依赖度低,缺点是浪费内存空间。动态链接在程序加载或运行时由操作系统加载指定的代码。优点是省空间,缺点是影响程序性能,对库的依赖度高。动态链接又分为装载时动态链接和运行时动态链接。
execve与fork的区别与联系
execve执行会覆盖掉当前的执行程序,execve返回时已经不是原来的可执行程序了,而是一个新的可执行程序。fork父进程和子进程中各返回一次,其中父进程返回到调用的下一条指令,子进程返回到ret_from_fork。
Linux中进程的描述、创建与调度
Linux中进程的描述
Linux中使用数据结构task_struct去描述进程,task_struct中有几个比较重要的元素:
- 标识符:跟这个进程相关的唯一标识符,用来区别其他进程。
- 状态:如果进程正在执行,那么进程处于执行状态。
- 优先级:相对于其他进程的优先级。
- 程序计数器:程序中即将被执行的下一条指令的地址。
- 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
- 上下文数据:进程执行时处理器的寄存器中的数据。
- I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备(如磁带驱动器)和被进程使用的文件列表。
- 审计信息:可包括处理器时间总和,使用的时钟数总和,时间限制,审计号等。
上述信息被存放在进程控制块(PCB)中,PCB由操作系统创建和管理。
Linux用双向链表struct list_head tasks管理所有的进程。
Linux中进程的创建
Linux中0号进程初始化是通过硬编码的方式固定下来的,除0号进程以外的其他进程都是通过do_fork复制的方式初始化的。
Linux进程调度的时机
Linux内核用schedule函数进行进程调度。schedule函数负责在运行队列中选择一个进程并执行。进程调度的时机就是内核调用schedule函数的时机,具体分为用户进程上下文主动调用特定的系统调用进入中断上下文、系统调用返回用户态前进行进程调度。内核线程或可以中断的中断处理程序执行过程中发生中断,在中断返回前进行进程调度以及内核线程主动调用schedule函数进行进程调度。
进程的上下文包含了进程执行需要的所有信息。进程切换就是变更进程上下文。
进程调度的策略
需要考虑我们的目的去确定具体的调度策略,即我们是为了追求高资源利用率还是追求响应及时。根据不同的调度策略采取不同的调度算法。
Linux中的时钟
Linux的计时体系结构
- 更新自系统启动以来所经过的时间
- 更新日期和时间
- 确定当前进程的执行时间,考虑是否需要抢占
- 更新资源使用统计情况
- 检查到期的软定时器
相对时间和墙上时间
相对时间:记录系统从启动直到当前时刻的时间。相对时间由系统时钟中断进行维护。
墙上时间:系统启动的时候根据实时RTC芯片保存的数据进行初始化,在系统运行期间由系统时钟维护并在合适的时刻和RTC芯片进行同步。墙上时间存储在系统核心变量xtime中,该变量记录了现实世界中年月日格式的时间。
Linux文件系统
Linux中的文件类型
- 正规文件:指系统所规定的普通格式的文件,包括系统文件、用户文件和库文件等。
- 目录文件:由文件目录构成的一类文件,用来维护文件系统结构和管理普通文件。
- 符号链接文件:是一个短文件,包含了另一个文件的任意路径名。
- 设备文件:包括块设备文件和字符设备文件。在Linux系统中,所有的输入输出设备都被看作文件。
- 管道文件:进行进程间通信的文件。
- 套接字文件:在发送方和接收方各创建一个套接字通信端点以获得tcp服务。
虚拟文件系统VFS
要实现操作系统对不同文件系统的支持,就要将各种文件系统的操作和管理纳入到一个统一的框架之中,对用户程序隐去各种不同文件系统的实现细节。问用户程序提供一个统一的、抽象的、虚拟的文件系统界面。虚拟文件系统对下规范了具体文件系统都要满足的一些要求,对上提供了统一的调用接口。
Linux系统的一般执行过程
1、正在运行的用户态进程X
2、发生中断,保存当前的CPU上下文到进程X的内核堆栈。加载当前进程内核堆栈的相关信息,跳转到中断处理程序。
3、保存现场,此时完成了中断上下文的切换。进程X从用户态进入了内核态。
4、中断处理过程中调用了schedule函数,其中的switch_to做了关键的进程上下文切换,将当前进程X的内核堆栈切换到进程调度算法选出的Y进程的内核堆栈。并完成EIP等寄存器状态的切换。
5、从进程Y继续执行。
6、恢复现场,要注意这里现在是进程Y的中断处理过程中。
7、从Y的进程内核堆栈中弹出之前完成的压栈内容,此时完成了中断上下文的切换。从进程Y的内核态返回了用户态。
8、继续运行用户态进程Y。
从ls命令的执行来看Linux系统的架构
控制台输入ls命令后,屏幕中断会显示我们敲出的命令,shell分析命令,调用系统调用fork生成自身的一个副本,调用exec将ls的可执行文件装入系统内存,接着从系统调用返回。执行shell。
心得体会
上完这门课后,我对Linux操作系统有了一个更加全面且深入的认识。由于之后的学习及工作肯定需要经常和Linux打交道,因此这门课补全了我Linux知识的空缺部分。
最后,衷心的希望《Linux操作系统分析》这门课越办越好!