什么是操作系统
是计算机硬件和应用之间的一层软件
方便我们使用硬件,如使用显存等 高效的使用硬件,如开多个终端(窗口)
操作系统管理的硬件:CPU管理,内存管理,终端管理,磁盘管理,文件管理(前面为重点部分),网络管理,电源管理,多核管理
上电后PC的自动操作
当计算机上电时例如X86计算机:

ROM BIOS:固化在系统板子上的,B-base IO-输入输出 S-系统
0磁道0扇区:操作系统的引导扇区

汇编完成汇编文件
这里就是加载boot扇区,注意段地址寄存器不能直接用立即数赋值,需要通用寄存器ax的过渡进行间接赋值
jmp 指令 : 直接跳转
jmpi 指令 :间接跳转
如上图:
- 将go赋给ip(段地址) go这条指令从代码的start开始的多少偏移,也就是
go这条指令在代码中的位置- INITSEG赋给cs(偏移地址)

以下代码主要是通过 INT13这个中断进入的
在第四行的cl中就是代表从第二个扇区开始,旁边方框说明了根据SETUPLEN是代表从第二个扇区后的四个扇区!

call read_it 这个代码调用read_it这个中断,在下一个图中可见
吾之意:在显示开机的画面和字符前已经初始化化好了一些预代码,后续才是真系统对应的代码

当执行完后把权限还交给setup,此时jmpi 0,SETUPSEG中
0 —– ip
SETUPSEG —– cs
可以得出cs:ip中的cs(SETUPSEG)需要为9020随后的地址为90200对应第一个张图中写出的宏
操作系统的启动
回顾:OS先执行bootsect.s随后通过INT13中断执行setup.s

INT15:获取物理内存的大小,从而赋值给ax
mov [2],ax:这里的[2]中隐含前面有个段地址reg,随后段地址reg+这个[2]的偏移地址形成0x90002
科普:在inter刚发明pc机的时候内存只有1M,在1M之后的内存称之为拓展内存,至于为什么OS要知道内存大小(每个pc装的内存不统一,有的大有的小),所以OS需要知道内存用来方便管理
setup.s的作用:说白了就是把内核操作系统移动到了0地址开始的位置,以后所有的程序(lg:word、pptx等)都是建立在这个系统之上的,方便操控硬件。
从实模式进入保护模式就是切换了寻址模式,增加了寻址能力

cr0 = 0x1 就是进入保护模式,cs<<4+ip 这样也就是16位 + 4位,最多就是20位,相当于1M内存,但是1M内存怎么可能够使用,所以需要从16位的机器变为32位的机器

此时就要考虑新的寻址方式!!!
答案:采用分页式去寻址
cs — 称之为选择子,即表示只是查到是哪一页,也就是页中的什么位置
ip — 表示对应的基地址
中断方式也采用寻找页中的中断入口函数
但是表又从何而来呢,是在执行jmpi 0,8之前就已经绘制出了gdt表
如下:

第一个和第二个方框使得表绘制出来,而内容就是下面的gdt:0,0,0,0等
一个word为8个Byte = 64b
此时cs和ip都是32b的了
操作系统之GDT和IDT(三)_gdt idt-CSDN博客

仔细看怎么对应进去的,这里是大端排序,红色的段基址取出来就是0x00000000,也就是零,所以jmp跳到了system模块。

Image — 是一个系统的镜像 (源码通过Makefile生成一个操作系统的大致轮廓) 随后就是产生一个桌面(Shell),我们就可以用操作系统

注意!!! — 16位的汇编指令操作和32位指令的操作刚好反过来了 —即源reg和目标reg换位置了 (如下)
mov %as,%fs — 这里和16位的汇编指令不一样,现在已经进入到了32位的模式下,所以这条的指令内容为
mov %as, %fs表示将值从寄存器%as移动(拷贝)到寄存器%fs中。还有一个代码中的A20 — 就是开启20根地址线,寻址能力为4G


汇编进入c语言和函数之间的调用基本一致,都是通过入栈出栈操作的
随后是一些初始化,如下面的内存初始化

弄清一个剩下的都一样,这里给出内存的init

4K就是一页,划分明白,内容这里全为0
这里 mem = 0x90002,前面初始的地址
总结
- bootset : 将操纵系统读入
- setup : 获得了一些硬件参数,启动了保护模式
- head : 初始化了
gdb表以及页表 - main : 初始化内存,硬盘等区块划分
- mem_init: main中的一个函数,用来初始内存块
- 两个表 : IDT表和GDT表
操作系统接口

有如上三种形式使用计算机

fork:指的是在当前父进程下创建的子进程
exec:指的是一个新的父进程用来代替当前的进程

图形按钮就是调用一些重要的函数实现即可,主要是通过堆栈的操作实现

接口 —> 就是
系统调用

一些名词的解释
系统调用的实现
这里通过一个例子来说明当我们的main函数中调用子函数的时候为什么不能直接通过 内存 --> 内存访问 ,假如可以直接访问,结果是在运行别人代码的时候,可以直接把你的用户密码捞上,也就是用户密码泄露,不安全。

上方的代码为两种颜色,即蓝色和红色
- 蓝色 —— 用户段
- 红色 —— 内核段

- CPL:CPL是当前执行的程序或任务的特权级。它被存储在CS和SS的第0位和第1位上。通常情况下,CPL代表代码所在的段的特权级。当程序转移到不同特权级的代码段时,处理器将改变CPL。只有0和3两个值,分别表示用户态和内核态。
- DPL:DPL表示段或门的特权级。它被存储在段描述符或者门描述符的DPL字段中,DPL将会和CPL以及段或者门选择子的RPL相比较,根据段或者门类型的不同,DPL将会区别对待。
- 数值越小 –> 等级越高
绿色方框:为检查权限等级,限制用户态不能随意进入内核态执行
系统在初始化中,hand.s文件初始化了GDT表,在初始好所用硬件的最后会初始化SHELL并保持,此时CPL == 3存储在CS中,而且内核态的DPL == 0存储在GDT表中,每次跳转都要访问GDT表(也就是段reg),此时系统知道了DPL=0,CPL=3,阻止跳转到内核态中!!!

中断是进入内核的唯一方式,但是不是所用中断都能进入内核

通过一个宏将实参传入函数

上述代码表示的三个参数分别为a,b,c
[asm volatile("int 0x80" : "=a" (res) : "" (NR ##name));]()
解释如下:
asm volatile:声明这是一段内联汇编代码,volatile关键字表示编译器不应优化这段汇编代码,因为它可能会影响或依赖于外部状态。
"int 0x80":这是汇编指令,用于执行一个系统调用(在 Linux 上)。int 0x80是 x86 架构中用来触发内核中断的指令,通常用于执行系统调用。
: "=a" (res):这是输出操作数部分。=a表示输出结果将存储在EAX寄存器中,并且赋值给变量res。
: "" (NR ##name):这是输入操作数部分。NR ##name表示将系统调用的编号传递给汇编代码。##是 C 语言中的预处理符号,用于连接宏。
-
输出操作数:输出操作数位于冒号 : 后的第一个部分。输出操作数使用 = 符号来标识。
- =a 表示该操作数是输出类型,并且输出结果存储在 EAX 寄存器中。
- 输出操作数的变量在内联汇编执行后将被赋值。
-
输入操作数:输入操作数位于第二个冒号 : 后。输入操作数没有 = 符号。
- “” 是一个约束(constraint),表示操作数的类型或位置,比如 a 指的是 EAX 寄存器。
- 输入操作数的变量在内联汇编执行前传递给汇编代码,但不会被修改。

INT 0x80:INT这是个中断处理符号,需要根据IDT表寻找到进入哪个中断
void sched_init(void){
set_system_gate(0x80,&system_call);
}
这里的&system_call中断处理函数,是个地址,找到就跳进去
#define set system gate(n,addr)
- n –> 中断号
- addr –> 中断地址

注意!!! 这里的3就是dpl,设置其等级 3 == 用户态
0x80中的8就是10000x,在cs寄存器的后两位就是CPL,刚好是两个00.
cs寄存器
| 1 | 0 | 0 | 0 |
|---|
此时执行就是为内核态去执行
!(/wp-content/uploads/2025/11/5_8.png){.alignnone}
!(/wp-content/uploads/2025/11/5_9.png){.alignnone}
此时找到sys_write后跳入执行——->真正的作用代码
重点:上图结尾的程序框图,printf的整个流程

内核中使用printk进行操作打印,printf是上层应用使用的
操作系统历史



任务调度 和 分时系统 的作用不大,但是概念的提出非常有意义

历史如下:
IBM 8080 8bit芯片诞生
1975 — 开发了操作系统CP/M
CP/M:写命令让用户用,执行命令对应的程序,单任务执行
1980 — 8086 16bit芯片诞生,在CP/M开发了QDOS
1975 — BASIC解释器被开发 –> 微软诞生
1977 — 开发出了FAT管理磁盘
QDOS的成功在于以CP/M为基础将BASIC和FAT包含了进来
文件管理和编程环境…都是用户关心的!
1980 — 微软买下了QDOS,改名为MS-DOS
MS-DOS的磁盘、文件、命令让用方便,但似乎可以更方便
1989 — MS-DOS4.0已经能支持鼠标和键盘,但是微软准备放弃
随后windows3.0 出世 ,大获成功,此后就是win95–>winxp–>Vista–>win7–>win8 …


我们的任务

主要理解怎么控制IO设备,CPU,硬盘等

在linux内核中,所有的物理内存都用struct page结构来描述,这些对象以数组形式存放,而这个数组的地址就是mem_map。内核以节点node为单位,每个node下的物理内存统一管理,也就是说在表示内存node的描述类型struct pglist_data中,有node_mem_map这个成员,其针对平坦型内存进行描述(CONFIG_FLAT_NODE_MEM_MAP),与此相反的是SPARSEMEM,其稀疏性内存描述。
mem_map是一个数组,存放了所有的页描述符。一个页对应一个页描述符。 也就是上面说的表格
详细见:

CPU管理、内存管理 为 进程VIEW(进程视图)
磁盘管理、终端设备管 为 文件VIEW(文件视图)

分为两个阴影展开讲解





