哈工大操作系统-1-基础
本文最后更新于293 天前,其中的信息可能已经过时,如有错误请发送邮件到cishaxiatian@gmail.com

什么是操作系统

计算机硬件应用之间的一层软件

方便我们使用硬件,如使用显存等 高效的使用硬件,如开多个终端(窗口)

操作系统管理的硬件:CPU管理,内存管理,终端管理,磁盘管理,文件管理(前面为重点部分),网络管理,电源管理,多核管理

上电后PC的自动操作

当计算机上电时例如X86计算机:

2_1

ROM BIOS:固化在系统板子上的,B-base IO-输入输出 S-系统

0磁道0扇区:操作系统的引导扇区


2_2
.s后缀的文件:汇编文件,其中c文件称为编译c文件,而汇编文件称为汇编完成汇编文件

这里就是加载boot扇区,注意段地址寄存器不能直接用立即数赋值,需要通用寄存器ax的过渡进行间接赋值

jmp 指令 : 直接跳转

jmpi 指令 :间接跳转

如上图:

  1. 将go赋给ip(段地址) go这条指令从代码的start开始的多少偏移,也就是go这条指令在代码中的位置
  2. INITSEG赋给cs(偏移地址)

2_3

以下代码主要是通过 INT13这个中断进入的

在第四行的cl中就是代表从第二个扇区开始,旁边方框说明了根据SETUPLEN是代表从第二个扇区后的四个扇区!


2_4

call read_it 这个代码调用read_it这个中断,在下一个图中可见

吾之意:在显示开机的画面和字符前已经初始化化好了一些预代码,后续才是真系统对应的代码


2_5

当执行完后把权限还交给setup,此时jmpi 0,SETUPSEG中

0 —– ip

SETUPSEG —– cs

可以得出cs:ip中的cs(SETUPSEG)需要为9020随后的地址为90200对应第一个张图中写出的宏

操作系统的启动

回顾:OS先执行bootsect.s随后通过INT13中断执行setup.s

3_1

INT15:获取物理内存的大小,从而赋值给ax

mov [2],ax:这里的[2]中隐含前面有个段地址reg,随后段地址reg+这个[2]的偏移地址形成0x90002

科普:在inter刚发明pc机的时候内存只有1M,在1M之后的内存称之为拓展内存,至于为什么OS要知道内存大小(每个pc装的内存不统一,有的大有的小),所以OS需要知道内存用来方便管理

setup.s的作用:说白了就是把内核操作系统移动到了0地址开始的位置,以后所有的程序(lg:word、pptx等)都是建立在这个系统之上的,方便操控硬件。




从实模式进入保护模式就是切换了寻址模式,增加了寻址能力

3_2

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


3_3

此时就要考虑新的寻址方式!!!

答案:采用分页式去寻址

cs — 称之为选择子,即表示只是查到是哪一页,也就是页中的什么位置

ip — 表示对应的基地址

中断方式也采用寻找页中的中断入口函数

但是表又从何而来呢,是在执行jmpi 0,8之前就已经绘制出了gdt表

如下:

3_4

第一个和第二个方框使得表绘制出来,而内容就是下面的gdt:0,0,0,0等

一个word为8个Byte = 64b

此时cs和ip都是32b的了

操作系统之GDT和IDT(三)_gdt idt-CSDN博客

3_5

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

3_6

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

3_7

注意!!! — 16位的汇编指令操作和32位指令的操作刚好反过来了 —即源reg和目标reg换位置了 (如下)

mov %as,%fs — 这里和16位的汇编指令不一样,现在已经进入到了32位的模式下,所以这条的指令内容为

mov %as, %fs 表示将值从寄存器 %as 移动(拷贝)到寄存器 %fs 中。

还有一个代码中的A20 — 就是开启20根地址线,寻址能力为4G

3_8


3_9

汇编进入c语言和函数之间的调用基本一致,都是通过入栈出栈操作的

随后是一些初始化,如下面的内存初始化

3_10

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

3_11

4K就是一页,划分明白,内容这里全为0

这里 mem = 0x90002,前面初始的地址

总结

  1. bootset : 将操纵系统读入
  2. setup : 获得了一些硬件参数,启动了保护模式
  3. head : 初始化了gdb表以及页表
  4. main : 初始化内存,硬盘等区块划分
  5. mem_init: main中的一个函数,用来初始内存块
  6. 两个表 : IDT表和GDT表



操作系统接口

4_1

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

4_2

fork:指的是在当前父进程下创建的子进程

exec:指的是一个新的父进程用来代替当前的进程

4_3

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

4_4

接口 —> 就是系统调用

4_5

一些名词的解释

系统调用的实现

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

5_1

上方的代码为两种颜色,即蓝色红色

  • 蓝色 —— 用户段
  • 红色 —— 内核段
5_2
  • 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,阻止跳转到内核态中!!!


5_3

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

5_4

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

5_5

上述代码表示的三个参数分别为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 语言中的预处理符号,用于连接宏。

  • 输出操作数:输出操作数位于冒号 : 后的第一个部分。输出操作数使用 = 符号来标识。


    1. =a 表示该操作数是输出类型,并且输出结果存储在 EAX 寄存器中。
    2. 输出操作数的变量在内联汇编执行后将被赋值。

  • 输入操作数:输入操作数位于第二个冒号 : 后。输入操作数没有 = 符号。


    1. “” 是一个约束(constraint),表示操作数的类型或位置,比如 a 指的是 EAX 寄存器。
    2. 输入操作数的变量在内联汇编执行前传递给汇编代码,但不会被修改。
5_6

INT 0x80:INT这是个中断处理符号,需要根据IDT表寻找到进入哪个中断

void sched_init(void){
set_system_gate(0x80,&system_call);
}

这里的&system_call中断处理函数,是个地址,找到就跳进去

#define set system gate(n,addr) 
  • n –> 中断号
  • addr –> 中断地址
5_7

注意!!! 这里的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的整个流程

5_10

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

操作系统历史

6_1


6_2
6_3

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

6_4

历史如下:

IBM 8080 8bit芯片诞生

1975 — 开发了操作系统CP/M

CP/M:写命令让用户用,执行命令对应的程序,单任务执行

1980 — 8086 16bit芯片诞生,在CP/M开发了QDOS


1975 — BASIC解释器被开发 –> 微软诞生

1977 — 开发出了FAT管理磁盘

QDOS的成功在于以CP/M为基础BASICFAT包含了进来

文件管理和编程环境…都是用户关心的!

1980 — 微软买下了QDOS,改名为MS-DOS

MS-DOS的磁盘、文件、命令让用方便,但似乎可以更方便

1989 — MS-DOS4.0已经能支持鼠标和键盘,但是微软准备放弃

随后windows3.0 出世 ,大获成功,此后就是win95–>winxp–>Vista–>win7–>win8 …

6_5

6_6

我们的任务

7_1

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

7_2

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


mem_map是一个数组存放了所有的页描述符。一个页对应一个页描述符。 也就是上面说的表格

详细见:

https://www.cnblogs.com/linhaostudy/p/17333922.html

7_3

CPU管理、内存管理 为 进程VIEW(进程视图)

磁盘管理、终端设备管 为 文件VIEW(文件视图)

7_4

分为两个阴影展开讲解

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇