操作系统
转载声明
# 操作系统基础
什么是操作系统? - 知乎 (zhihu.com) (opens new window)
操作系统(Operation System, OS) 是指控制和管理整个计算机系统的硬件和软件资源的系统软件。
# 系统调用
操作系统运行环境 | lllllan (opens new window)
用户程序通过调用操作系统提供的一些接口,去只能在内核态运行的子程序。
- 设备管理。完成设备的请求或释放,以及设备启动等功能。
- 文件管理。完成文件的读、写、创建及删除等功能。
- 进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。
- 进程通信。完成进程之间的消息传递或信号传递等功能。
- 内存管理。完成内存的分配、回收以及获取作业占用内存区大小及始址等功能。
# 硬链接和软链接
- 硬链接就是在目录下创建一个条目,记录着文件名与
inode编号,这个inode就是源文件的inode。删除任意一个条目,文件还是存在,只要引用数量不为0。但是硬链接有限制,它不能跨越文件系统,也不能对目录进行链接。 - 符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为
Windows的快捷方式。当源文件被删除了,链接文件就打不开了。因为记录的是路径,所以可以为目录建立符号链接。
# 中断的处理过程
- 保护现场:将当前执行程序的相关数据保存在寄存器中,然后入栈。
- 开中断:以便执行中断时能响应较高级别的中断请求。
- 中断处理
- 关中断:保证恢复现场时不被新中断打扰
- 恢复现场:从堆栈中按序取出程序数据,恢复中断前的执行状态。
# 中断和轮询
- 轮询:CPU对特定设备轮流询问。中断:通过特定事件提醒CPU。
- 轮询:效率低等待时间长,CPU利用率不高。中断:容易遗漏问题,CPU利用率不高。
# 用户态切换到内核态 ★
三种方式:
- 系统调用。用户进程主动要求切换到内核态的一种方式
- 异常。程序运行过程遇到了事先不可知的异常,转由内核相关程序来处理
- 外围设备的中断。
# 虚拟内存
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。 现代所有用于一般应用的操作系统都对普通的应用程序使用虚拟内存技术,老一些的操作系统,如DOS和1980年代的Windows,或者那些1960年代的大型机,一般都没有虚拟内存的功能 ——维基百科
如何理解虚拟内存 - 知乎 (zhihu.com) (opens new window)
虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存。
# 虚拟内存的技术实现
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
请求分页和分页存储管理的区别
请求分页建立在分页管理之上。他们的根本区别是是否将程序所需要的全部地址空间都装入主存,这也是请求分页存储管理可以提供虚拟内存的原因。请求分页不要求将作业全部地址同时装入主存,基于这一点,请求分页可以提供虚存,而分页管理不能。
不管是上面哪种实现方式,我们一般都需要:
- 一定容量的内存和外存:在载入程序的时候,只需要将程序的一部分装入内存,而将其他部分留在外存,然后程序就可以执行了。
- 缺页中断: 如果需要执行的指令或访问的数据尚未在内存(称为缺页或缺段),则由处理器通知操作系统将相应的页面或段调入到内存,然后继续执行程序
- 虚拟地址空间: 逻辑地址到物理地址的变换
# 页面置换算法
缺页中断: 要访问的页不在主存中,需要操作系统将其调入主存再进行访问。在这个时候,被内存映射的文件实际上称为了一个分页交换文件。
当发生缺页中断时,如果当前内存中没有空闲的页面,操作系统就必须在内存中选择一个页面移出内存,以便为即将调入的页面让出空间。用来选择淘汰哪一页的规则就叫页面置换算法。
- OPT 最佳页面置换算法: 该算法所选择的被淘汰的页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。
- FIFO 先进先出页页面置换算法: 总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰
- LRU 最近最久未使用页面置换算法: LRU算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问依赖所经历的时间T,当需淘汰一个页面时,选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰
- LFU 最少使用页面置换算法: 选择在之前时期内使用最少的页面淘汰
# 进程切换后虚拟地址的转换会变慢 ★
进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用 Cache 来缓存常用的地址映射,这样可以加速页表查找,这个 Cache 就是 TLB(translation Lookaside Buffer,TLB本质上就是一个Cache,是用来加速页表查找的)。
由于每个进程都有自己的虚拟地址空间,那么显然每个进程都有自己的页表,那么当进程切换后页表也要进行切换,页表切换后TLB就失效了,Cache 失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致 TLB 失效,因为线程无需切换地址空间,因此我们通常说线程切换要比较进程切换块,原因就在这里。
# 缺页中断处理流程
- 保护 CPU 环境
- 分析中断原因
- 转入缺页中断处理程序
- 恢复 CPU 环境
# IO管理
# IO多路复用
IO 多路复用 | lllllan (opens new window)
# 缓冲区
设备独立性软件 | lllllan (opens new window)

缓冲区溢出的危害
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
危害有以下两点:
- 程序崩溃,导致拒绝服务
- 跳转并且执行一段恶意代码
造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
# Unix常见的IO模型 ★
IO模型的详细说明见:2021-Java后端工程师必会知识点-(操作系统) - 掘金 (juejin.cn) (opens new window)
对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
- 等待数据准备就绪 (Waiting for the data to be ready)
- 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux系统产生了下面五种网络模式的方案:
- 阻塞式IO模型(blocking IO model)
- 非阻塞式IO模型(noblocking IO model)
- IO复用式IO模型(IO multiplexing model)
- 信号驱动式IO模型(signal-driven IO model)
- 异步IO式IO模型(asynchronous IO model)
# select、poll 和 epoll 之间的区别 ★
一文搞懂select、poll和epoll区别 - 知乎 (zhihu.com) (opens new window),有代码,看不懂
细说select、poll和epoll之间的区别与优缺点_七月朔风的技术博客_51CTO博客 (opens new window)
(1)select:时间复杂度 O(n)
select 仅仅知道有 I/O 事件发生,但并不知道是哪几个流,所以只能无差别轮询所有流,找出能读出数据或者写入数据的流,并对其进行操作。所以 select 具有 O(n) 的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
(2)poll:时间复杂度 O(n)
poll 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的。
(3)epoll:时间复杂度 O(1)
epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的 I/O 事件通知我们。所以说 epoll 实际上是事件驱动(每个事件关联上 fd)的。
select,poll,epoll 都是 IO 多路复用的机制。I/O 多路复用就是通过一种机制监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),就通知程序进行相应的读写操作。但 select,poll,epoll 本质上都是同步 I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步 I/O 则无需自己负责进行读写,异步 I/O 的实现会负责把数据从内核拷贝到用户空间。
- select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
- select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

# 文件管理
# inode
硬盘的最小存储单位是扇区(Sector),块(block)由多个扇区组成。文件数据存储在块中。块的最常见的大小是 4kb,约为 8 个连续的扇区组成(每个扇区存储 512 字节)。一个文件可能会占用多个 block,但是一个块只能存放一个文件。
虽然,我们将文件存储在了块(block)中,但是我们还需要一个空间来存储文件的 元信息 metadata :如某个文件被分成几块、每一块在的地址、文件拥有者,创建时间,权限,大小等。这种 存储文件元信息的区域就叫 inode,译为索引节点:i(index)+node。 每个文件都有一个 inode,存储文件的元信息。
可以使用 stat 命令可以查看文件的 inode 信息。每个 inode 都有一个号码,Linux/Unix 操作系统不使用文件名来区分文件,而是使用 inode 号码区分不同的文件。
简单来说:inode 就是用来维护某个文件被分成几块、每一块在的地址、文件拥有者,创建时间,权限,大小等信息。
简单总结一下:
- inode :记录文件的属性信息,可以使用 stat 命令查看 inode 信息。
- block :实际文件的内容,如果一个文件大于一个块时候,那么将占用多个 block,但是一个块只能存放一个文件。(因为数据是由 inode 指向的,如果有两个文件的数据存放在同一个块中,就会乱套了)