操作系统启动原理

https://pic1.zhimg.com/80/v2-a2ce6a4d3c9ebd504910da29f1bb17f0_720w.jpg

操作系统启动流程

我们要先了解操作系统开机启动流程,才能继续学习下去,看到网上很多写系统的教程,一开始都是写汇编,写各种 loader.s 之类的,我们得先明白这个是在做什么,然后才能继续跟着学下去。

参考博客文章

参考文章2

目前在看的学习教程

外国人的视频教程

这个人的youtube

操作系统启动流程

参考的文章

执行程序 解释 详细流程
BIOS 主板上有一个特殊的程序,叫BIOS 最开始的时候,计算机会把BIOS程序加载到内存里。然后指令指针(Instruction Pointer)会指向这段简短的BIOS程序;BIOS程序读取硬盘,从里面加载boot loader到内存,并设置指令指针到内存。接着,bootloader执行并且读取硬盘,从里面读取操作系统内核到内存。
bootloader 引导加载程序; 引导模式; 启动加载; 引导代码; 程序; 操作系统 内核运行之前运行,首先bootloader是一段程序,grub是bootloader的一种 ,bootloader要实现的功能就是提供一个菜单给用户,让用户去选择要启动的系统或不同的内核版本
加载内核 加载磁盘驱动程序 ,用于读取磁盘 通过一个 虚拟文件系统,让 bootloader 将文件加载到内存中,该程序来加载启动过程中所最需要的核心模块
运行 /sbin/init等程序 内核加载完毕后,运行用户空间的第一个程序是/sbin/init
启动内核模块 依据/etc/sysconfig/modules文件目录下的文件来装载内核模块。
执行 /etc/rc.d/rc.local Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。相当于用户脚本
执行 /bin/login 让用户输入密码登录

内核初始化视频教程

有fork源码的博客

bootstrapping

开机过程我们叫做 bootstrap

学这个 又要先学习 csapp 的课程

  • Bios
    • Memory mapping
    • power on self test
    • find mbr selector
  • cpu registers – init state
  • MBR selector
    • Load system into RAM
    • Do some initialization
    • Jump to the system code

linux下汇编学习

学习视频教程

https://www.bilibili.com/video/BV1B4411Y7if/?spm_id_from=333.788.recommend_more_video.6

同学给的一个练习代码

学习汇编需要理解的概念:

  1. 总线
  2. CPU,内存
  3. 指令和数据

地址线等知识

名字
地址线 CPU是通过地址总线来指定存储单元的。
数据线 是CPU与内存或其他器件之间的数据传送的通道。
控制线 CPU通过控制总线对外部器件进行控制。

寄存器知识

Name Notes Type 64-bit long 32-bit int 16-bit short 8-bit char
rax Values are returned from functions in this register. scratch rax eax ax ah and al
rcx Typical scratch register. Some instructions also use it as a counter. scratch rcx ecx cx ch and cl
rdx Scratch register. scratch rdx edx dx dh and dl
rbx __ Preserved register: don’t use it without saving it! preserved rbx ebx bx bh and bl
rsp __ The stack pointer. Points to the top of the stack (details coming soon!) preserved rsp esp sp spl
rbp __ Preserved register. Sometimes used to store the old value of the stack pointer, or the “base”. preserved rbp ebp bp bpl
rsi Scratch register. Also used to pass function argument #2 in 64-bit Linux scratch rsi esi si sil
rdi Scratch register. Function argument #1 in 64-bit Linux scratch rdi edi di dil
r8 Scratch register. These were added in 64-bit mode, so they have numbers, not names. scratch r8 r8d r8w r8b
r9 Scratch register. scratch r9 r9d r9w r9b
r10 Scratch register. scratch r10 r10d r10w r10b
r11 Scratch register. scratch r11 r11d r11w r11b
r12 Preserved register. You can use it, but you need to save and restore it. preserved r12 r12d r12w r12b
r13 Preserved register. preserved r13 r13d r13w r13b
r14 Preserved register. preserved r14 r14d r14w r14b
r15 Preserved register. preserved r15 r15d r15w r15b
寄存器
EAX 累加器 Accumulator
EBX 基地址寄存器 base register
ECX 技术寄存器
EDX 数据寄存器 Data register
EBP 堆栈基指针 Base pointer
ESI 变址寄存器 index register
EDI 变址寄存器 index register
ESP 堆栈顶指针 stack pointer
CS 代码段寄存器 code segment register
DS 数据段寄存器 data segment register
ES 附加段寄存器 extra
SS 堆栈段寄存器 stack
FS 附加段寄存器 extra segment register

寻址

代码
movel %eax,%edx edx=eax 寄存器寻址
movl $0x123 ,%edx edx=0x123 立即寻址
movl 0x123,%edx edx=*(int32_t*)0x123 直接寻址
movl (%ebx),%edx edx=*(int32_t)ebx; 间接寻址
movl 4(%ebx), %edx edx=*(int32_t*)(ebx+4)
pushl %eax eax寄存器放到堆栈栈顶
popl %eax 弹出栈顶
call 0x12345 函数调用 pushl %eip(*); movl $012345,%eip(*)
ret pop %eip(*), 星号是伪指令,程序员不能直接修改 eip 这个寄存器

将c语言转化为汇编代码

我学习的课程

这个代码 默认是以 32位的 X86 为例子

编译的代码,和 c的代码如下

1
gcc -S -o a.s a.c -m32
1
2
3
4
5
6
int g(int x) {
    return x+3;
}
int main(void) {
    return 0;
}

那个 a.s 的文件 就是 汇编的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	.file	"a.c"
	.text
	.globl	g
	.type	g, @function
g:
.LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	movl	8(%ebp), %eax
	addl	$3, %eax
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	g, .-g
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	movl	$0, %eax
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
	.globl	__x86.get_pc_thunk.ax
	.hidden	__x86.get_pc_thunk.ax
	.type	__x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
.LFB2:
	.cfi_startproc
	movl	(%esp), %eax
	ret
	.cfi_endproc
.LFE2:
	.ident	"GCC: (Debian 10.3.0-11) 10.3.0"
	

所有以为 . 开头的都是辅助信息,没有用的,可以直接删除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
g:
	pushl	%ebp ; base pointer ,压栈
	movl	%esp, %ebp ; stack pointer 
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax  ;累加寄存器
	movl	8(%ebp), %eax ; ebp 赋值给 eax 
	addl	$3, %eax  ; 加3 
	popl	%ebp ; base pointer 弹栈
	ret ; 返回
main:
	pushl	%ebp
	movl	%esp, %ebp  ; stack pointer -> base pointer
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	movl	$0, %eax
	popl	%ebp
	ret
__x86.get_pc_thunk.ax:
	movl	(%esp), %eax
	ret

linux 安装汇编环境

1
2
sudo apt-get install nasm
# 请确定自己的机器是 x86_64的

打印一个 hello world

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 section .data
    ; 定义标签   define bytes     10 表示 \n 换行
    var_text db "hello,world",10
section .text
    ; 设置启动函数
    global _start

; 设置运行的函数
_start:
    mov rax,1
    mov rdi,1
    mov rsi,var_text
    mov rdx,14
    syscall
    mov rax,60
    mov rdi,0
    syscall
1
2
3
4
5
6
7
8
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ nasm -f elf64 -o hello.o hello.asm
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ ls
hello.asm  hello.o
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ man ld
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ ld hello.o -o hello
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ ls
hello  hello.asm  hello.o
lyr@DESKTOP-FSVN6C0:~/tmp/assmbly$ ./hello

进程状态

[[post/14.新语言学习记录/linux/linux基础原理杂记/部署总结/进程状态|机场状态]] [[post/14.新语言学习记录/linux/linux基础原理杂记/cpp语言使用/进程运行的状态 |cpp语言进程运行状态]]

开机后 需要接管的cpu 的控制器

graph LR 开机 --> BIOS BIOS --> 引导程序 -->操作系统

bios

  • post (power on self test)
  • 转交 MBR
  • MBR master boot record

引导程序

  • MBR
  • 分区引导程序
  • 加载内核
  • 转交内核

RAM 和 ROM

ram 是随机存储,rom read only memory ,不可修改, ram容易丢失数据

硬件消息处理流程

graph LR 键盘 --> 硬件缓存区 --> IRQ IRQ --> OS --消息处理event,判断给哪个程序--> APP

按键盘后,现在缓冲区修改,然后在触发中断 ,让操作系统运行程序处理

os会检测 这个按键信息是给哪个运行的程序处理的

ring buffer

键盘的缓冲区其实是一个循环队列 ,

https://images.cnblogs.com/cnblogs_com/leavingQ/201201/201201101351143940.png

操作系统是什么?

  • 一种程序
    • 管理硬件设备、资源应用程序(管理能力)
    • 将硬件能力、资源抽象成为服务让应用程序使用(抽象能力)

内核设计

内核 驱动 服务 应用
时钟 磁盘 文件 shell
进程管理 tty 进程 gui
进程调度 打印机 打印 ..
进程通信 显示器 自检
中断处理 ..

操作系统权限问题

权限怎么处理

  • 用户执行 rm -rf /*
  • 打印机驱动程序改写中断向量表
  • 游戏程序按攻击按钮
  • 显示驱动访问打印机端口

解决方法:

  1. 拆分权限(端口权限,文件权限、操作权限等)
  2. 拆分 内核态(执行操作系统特权指令) 和用户态
sequenceDiagram 应用->>服务: 分配内存 服务->> 服务: 参数/权限检查 服务->> 服务: 调用准备(寄存器、内存等) 服务->> 内核: 中断 内核 --) 服务: 重置中断状态 服务 ->> 服务: 一些处理 服务 ->> 应用:返回
  • 应用程序进行系统调用
  • 服务层解析参数、校验
  • 权限检查
  • 调用准备(寄存器、内存等)
  • trap(软中断)、陷入内核
  • 内核进行处理
  • 内核恢复trap前状态
  • 服务层进行一些处理准备返回数据
  • 返回数据给应用层

总结

  • 为什么用微内核
    • 内核管的少,扩展性钱。 比如调度算法、文件系统扩展

其他笔记

[[post/05.软件和系统/02.系统相关/02.Linux面试|linux面试]]

[[post/08.其他/网友总结/操作系统/内存 |内存]] [[post/03.基础学科/01.操作系统/4.内存管理|内存管理]]

[[post/14.新语言学习记录/语言技术/Golang/理论学习/课外课程学习/linux进程内存原理|go内存]]

[[post/14.新语言学习记录/linux/linux基础原理杂记/cpp语言使用/linux下 进程控制【fork】|linux fork原理]]