浅学操作系统

162 阅读12分钟

一. 操作系统

说白了,操作系统,就是个 (控制型)软件程序,比如: linux, windows, android, ios, Harmony OS等 操作系统有两个方面的功能:

  1. 对上,给应用程序提供服务(控制应用程序的执行,同时限制不同应用程序占用的资源,给应用程序提供各种各样的服务{如:I/O服务、声卡的访问、网卡的访问})
  2. 对下,对硬件资源进行管控,管理

image.png 作用: image.png 程序员比较关心的操作系统内部组件: image.png

二.操作系统的大体启动过程

每种操作系统启动流程,以及启动细节都不一样,只简单描述大体的启动过程,如图:

预备知识:

操作系统程序事先存放在磁盘中(如:windows操作系统是放在C盘中的)

Bootloader程序:将磁盘中的 " OS " 加载到内存

BIOS(Basic I/O System): 提供开机支持,固定在主板的ROM(Read Only Memory主板中的一块只读内存)区域

ROM(Read Only Memory): 只读内存
RAM(Random Access Memory):随机访问内存

image.png

三.用户态与内核态

为了计算机的安全使用着想,有了用户态和内核态 内核态:可以完全访问所有的硬件,也可以执行机器能够运行的任何指令

  操作系统在运行时 CPU 所处的状态, 这个时候的CPU可以执行任何一条指令(包括特权指令,访问I/O指令)

用户态:只能执行一部分机器指令,对于哪些会影响机器的控制或者可进行I/O操作的指令,在用户态中的程序中是禁止的

 应用程序在运行时 CPU 所处的状态,这个时候的CPU所处的状态级别特别低,不能直接访问某些机器指令,或者不能直接访问I/O(读写磁盘)

image.png

四.操作系统如何与外设硬件交互

image.png

操作系统与硬件交互主要使用: 汇编指令、中断机制(硬中断)

4.1 操作系统使用汇编指令与硬件交互

预备知识:总线,设备控制器,寄存器。 可以AI了解一下。 image.png CPU芯片不会直接与这些设备交互,通常计算机中有个抽象的概念叫 “设备控制器”。 当然有很多种控制器,可以AI问一下,截图只是举几个小例子。

设备控制器(Device Controller)确实是计算机系统中一个至关重要却又容易被忽视的硬件组件。它是CPU(或更广义地说,计算机系统)与各种物理硬件设备(如磁盘、键盘、打印机、显示器、网卡等)之间进行交互的核心桥梁。 image.png 详细的 “设备控制器”, 拿打印机做举例, image.png

操作系统会给这个三个寄存器做唯一标识,方便CPU知道控制那个寄存器,操作系统给这个三个寄存器分配个端口,学名叫“端口映射I/O”,举例如下:

端口映射I/O : 将每个设备控制器的寄存器标志为不同的端口

1754127028123.jpg

与之相仿的还有个 “内存映射I/O”

内存映射I/O : 把I/O设备的各个寄存器都编址,看成“内存地址”

1754126978309.jpg

Window操作系统里就可以看到,某个设备管理器里面的某个I/O方式,它们各有优缺点,AI了解一下吧。 image.png 继续以端口I/O方式叙述

操作系统,做如下汇编指令时 
  OUT 0X03B0 EAX

就是要把 数据A, 从CPU中的EXA寄存器中给到 设备控制器中的 数据寄存器,最后通过控制电路给到打印机,更新详细点的可以看下这个张图片,我我觉得大家了解一下就行,更深原理不用深究

1754126931829.jpg

1754126901868.jpg

4.2 操作系统使用中断机制与硬件交互 (也叫 硬中断,是由硬件触发的)

简单了解一些知识:

(1)所有的操作系统内核部分都有一个服务程序叫——》“中断服务程序(Interrupt Service Routine, ISR)”

(2)中断服务程序在操作系统启动时会初始化出来一个表——》“中断描述符表(Interrupt Descriptor Table)” 里面维护了很多很多中断号,就是个数组表

(3)硬件设备会发送中断信号——》中断号(Interrupt Request, IRQ), 下图展示了window操作系统中某个硬件对应的中断号

image.png 工作流程简单描述:

  1. 硬件事件发生:

    • 一个硬件设备完成了某个操作(如磁盘读取完成)、有数据到达(如网络数据包到达网卡)、需要服务(如键盘按键被按下)或 发生错误。
    • 该硬件设备通过主板上的中断控制器(如Intel的APIC或传统的PIC)向CPU发送一个特定的中断请求信号(IRQ)。每个设备通常关联一个或多个唯一的IRQ号。
  2. CPU响应中断:

    • CPU在每条指令执行结束时,都会检查是否有待处理的中断请求(除非中断被明确屏蔽)。
    • 如果检测到未屏蔽的有效中断请求(IRQ),且当前程序的执行优先级低于该中断的优先级,CPU会立即中断当前程序的执行流。
    • CPU自动保存当前程序的关键上下文信息(至少包括程序计数器PC/指令指针IP、程序状态字PSW/标志寄存器Flags)。这些信息被压入当前栈(通常是内核栈)或特定的中断栈,以便后续能正确恢复被中断的程序。
  3. 查找中断服务程序:

    • CPU根据收到的IRQ号,查询一个由操作系统在启动时设置好的数据结构——中断描述符表(Interrupt Descriptor Table, IDT)。
    • IDT 本质上是一个数组,每个条目对应一个可能的中断号(包括硬件IRQ和软件异常/陷阱)。每个条目包含了对应中断服务程序(ISR)的入口地址(段选择子和偏移量)以及必要的特权级等信息。
  4. 执行中断服务程序:

    • CPU跳转到查找到的ISR入口地址,开始执行。

这种方式是现代操作系统管理硬件、实现并发和多任务处理的核心机制,它解决了轮询(Polling)效率低下的问题,让CPU能够在硬件需要服务时被“主动”通知。

核心思想:硬件设备(如键盘、鼠标、网卡、磁盘控制器、定时器)在需要操作系统的关注时(例如,数据准备好、操作完成、发生错误),会主动向CPU发送一个中断信号(Interrupt Request, IRQ)。CPU收到这个信号后,会立即暂停当前正在执行的程序(无论它是用户程序还是内核代码),保存其当前状态(称为上下文切换的一部分),然后转而执行一段专门为处理该硬件事件而编写的函数,即中断服务程序(ISR)。ISR执行完毕后,CPU通常会恢复之前被中断的程序继续执行。

扩展知识点:

DMA (Direct Memory Access) 直接内存访问 DMA 是一种硬件机制(在主板上的一个硬件),允许某些计算机内部的子系统(主要是外部设备,如磁盘驱动器、网卡、声卡、图形卡等)直接在系统内存(RAM)中读取或写入数据,而无需中央处理器(CPU)的持续参与。 它的核心目标就是显著减少 CPU 在处理大量数据传输任务上的开销。 为什么需要 DMA? (没有 DMA 的世界) 想象一下没有 DMA 的情况(也称为 PIO - Programmed I/O):

  1. CPU 全程参与: 当设备(比如硬盘、磁盘 )需要将数据读取到内存时:
    • CPU 必须从硬盘控制器的一个 I/O 端口逐个字节(或字)地读取数据。
    • 然后 CPU 再把这个数据逐个字节(或字)地写入到内存的目标地址。
  2. 高 CPU 占用率: 对于一个大文件(比如 1GB),CPU 就需要执行数十亿次这样的“读端口 -> 写内存”操作。这期间 CPU 几乎完全被占用,无法执行其他更有用的任务(如运行应用程序)。
  3. 效率低下: CPU 的速度远高于 I/O 设备。让高速的 CPU 等待慢速的 I/O 设备完成每一次字节传输是巨大的资源浪费。
  4. 增加延迟: 因为 CPU 被 I/O 传输阻塞,整个系统的响应速度会变慢。

五.应用程序如何与操作系统交互

5.1 系统调用

作用:系统调用主要解决的是用户态和内核态的切换

过程:Linux操作系统如何从用户态陷入(trap)内核态

  • linux 32位操作系统:80中断
  • linux 64位操作系统:syscall汇编指令

image.png

5.1.1 32位操作系统:INT 80 软中断(应用程序触发的

image.png

5.1.2 64位操作系统:汇编指令syscall

image.png

操作系统的 “系统调用服务程序” 是怎么知道程序代码调用那个系统调用方法?

在 Linux 操作系统中,当用户空间的程序(比如你的 C 程序)执行一个系统调用(如 read, write, open, fork 等)时,内核的系统调用服务程序(通常指中断/异常处理程序,特别是系统调用入口点)通过 sys_call_table 来找到并执行对应的内核函数的。核心机制是利用系统调用号作为索引。这里对应的方法参数传递涉及到寄存器的数据存储使用,过程有些复杂,大家AI了解一下。

  1. 系统调用号:

    • 每个系统调用在内核中都被分配一个唯一的整数编号,称为系统调用号(syscall number)。
    • 这个编号是系统调用在 sys_call_table 中的索引。
    • 当用户程序想要发起一个系统调用时,它必须将想要调用的系统调用号放入特定的寄存器。在 x86-64 架构上,这个寄存器是 %rax (RAX)。
  2. 触发系统调用:

    • 用户程序通过执行一条特殊的指令来请求内核服务,从而从用户态(User Mode)切换到内核态(Kernel Mode)。在 x86-64 架构上,常见的指令是:
      • syscall (现代 x86-64 CPU 上最常用的、更快的指令)
      • int 0x80 (传统的、较慢的软件中断方式,现在主要用于 32 位兼容模式或旧系统)
    • 执行这条指令会触发一个硬件异常(或称为陷阱/Trap),导致 CPU 切换到内核模式,并跳转到内核预定义的系统调用入口点代码(例如 entry_SYSCALL_64 或 entry_INT80_34)。

六.异常(Exception)

明明操作系统也没崩溃或死机啊!为什么管他们叫异常呢? 那是因为从应用程序角度来看的,下面截图介绍

中断、系统调用(陷阱)、故障、终止。它们在操作系统中都属于异常 ,因为它们打断了 应用程序 的按顺序执行。

image.png

6.1 中断(interrupt)

1754126841817.jpg

6.2 陷阱(trap 系统调用)

1754126807856.jpg

Linux (x86_64) 常见系统调用表

系统调用号用户态函数名内核态函数名简要说明
0readsys_read从文件描述符读取数据
1writesys_write向文件描述符写入数据
2opensys_open打开或创建一个文件
3closesys_close关闭一个文件描述符
4statsys_stat获取文件状态
5fstatsys_fstat根据文件描述符获取文件状态
6lstatsys_lstat获取符号链接文件状态
8lseeksys_lseek重新定位读/写文件偏移量
9mmapsys_mmap将文件或设备映射到内存
10mprotectsys_mprotect设置内存区域的保护属性
11munmapsys_munmap取消内存映射
12brksys_brk改变数据段空间大小
13rt_sigactionsys_rt_sigaction设置信号处理函数
14rt_sigprocmasksys_rt_sigprocmask设置或获取阻塞信号集
22pipesys_pipe创建管道
32dupsys_dup复制一个文件描述符
33dup2sys_dup2复制一个文件描述符到指定编号
39getpidsys_getpid获取进程ID
56clonesys_clone创建子进程(线程)
57forksys_fork创建子进程
58vforksys_vfork创建一个子进程,并阻塞父进程
59execvesys_execve执行程序
60exitsys_exit终止当前进程
61wait4sys_wait4等待子进程状态改变
62killsys_kill向进程发送信号
63unamesys_uname获取当前内核名称和信息
80chdirsys_chdir改变当前工作目录
81fchdirsys_fchdir根据文件描述符改变工作目录
82renamesys_rename重命名文件
83mkdirsys_mkdir创建目录
84rmdirsys_rmdir删除空目录
85creatsys_creat创建并打开一个新文件
86linksys_link创建硬链接
87unlinksys_unlink删除一个文件名(可能是软/硬链接)
88symlinksys_symlink创建符号链接
89readlinksys_readlink读取符号链接指向的路径
90chmodsys_chmod改变文件权限
158arch_prctlsys_arch_prctl架构特定的进程控制(x86_64 设置 FS/GS 段等)
202futexsys_futex快速用户空间互斥锁(线程同步底层原语)
217getdents64sys_getdents64获取目录项列表(现代版本)
257openatsys_openat相对于目录描述符打开文件(open 的现代版本)

6.3 故障(fault)·

1754126761745.jpg

6.4 终止(abort)

1754125432795.jpg

发生这些异常时,操作系统是怎么知道它到底是那种异常的呢?

(1) 异常号: 操作系统为每一种类型的异常,都分配了一个 “唯一” 的非负整数的异常号(exception number)

举例:X86-64位操作系统中定义的 “异常号”

1754125380616.jpg

(2) 异常跳转表:操作系统在启动后会初始化一个向量表——异常跳转表,说白了就是数组

image.png

1754125222429.jpg