linux内核源码阅读-初始化主程序
来自:https://in1t.top/2020/03/26/linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB-%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%BB%E7%A8%8B%E5%BA%8F/
main.c 功能描述
之前 setup 在 0x90000 ~ 0x901FF 保存了一些重要的机器参数,其中包括主内存区的开始地址,内存大小和作为高速缓冲区内存的末端地址,如果 Makefile 中还定义了 RAMDISK 虚拟盘,则主内存区还会减小。
高速缓冲是用于供磁盘等块设备临时存放数据的地方,以 1KB 为一个数据块单位,其中包含了显存及其 BIOS 占用的区域。现在 main.c 将会用这些参数来划分内存区域
之后调用一堆初始化函数对内存、陷阱门、块设备、字符设备、tty、时间、进程调度、缓冲区、硬盘、软驱进行初始化,并完成进程 0 的创建,从内核态切换为用户态。
此时第一次调用 fork 函数创建用于运行 init 函数的子进程 1,init 函数主要作用为
- 安装根文件系统
- 显示系统信息
- 执行资源配置文件
- 执行登录 shell 程序
流程图如下:
代码分析
*.h 头文件没有指明路径默认在 include 目录下
1
|
// Line 7
|
__always_inline 与 syscall 等在 include/unistd.h 中都有定义,_syscall 后面的数字表示定义的函数有几个参数,括号中的前两个参数为函数返回值类型及函数名,之后的每两个参数代表该函数参数类型及参数名
1
|
// Line 24
|
比如 _syscall1(int,setup,void *,BIOS) 表示定义了 int setup(void *BIOS) 这么一个函数,具体代码:
1
|
// include/unistd.h Line 267
|
对于 gcc AT&T 内嵌汇编不太熟悉的话,可以参考这篇 csdn 博文。以 _syscall0(int,fork) 为例,_syscall0(int,fork) 后产生了这么一个函数:
1
|
int fork(){
|
然后包含一堆头文件,引用一些初始化函数,定义一些常量
1
|
// Line 29
|
接下来定义读取 CMOS 时钟信息的宏及 time_init 函数,从 CMOS 中读出的信息都是 BCD 码的形式,用 4 bits(半个字节)表示一个 10 进制数。于是定义一个 BCD_TO_BIN 的宏,val&15 取 10 进制个位,val>>4 取 10 进制十位,乘 10 与个位相加得到实际的十进制对应的二进制数值
1
|
// Line 69
|
outb_p 与 inb_p 定义在 include/asm/io.h 中,分别表示向 io 端口输出信息及从 io 端口读取信息:
1
|
// include/asm/io.h Line 11
|
main 之前最后还定义了一些用于内存划分的变量
1
|
// Line 98
|
main 函数:
1
|
// Line 104
|
之前有说过 pause 的进程需等待一个信号才能被激活,进程 0 是个例外,当没有其他进程在运行时,会激活进程 0
接下来声明了一个 printf 函数,为 init 中输出信息做准备,并设置了一些配置文件
1
|
// Line 152
|
进程 1 执行的 init 函数,该函数首先对第一个将要执行的 shell 程序的环境进行初始化,然后以登录 shell 的方式加载并执行。如果运行登录 shell 的进程结束了,进程 1 又会重新新建一个进程,继续运行登录 shell。这里的登录 shell 指的就是开机完成用户可以键入用户名密码登录系统的 shell
1
|
// Line 169
|
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义