Linux 0.11 - 整个操作系统就 20 几行代码-11
Linux 0.11 - 整个操作系统就 20 几行代码-11
整个操作系统就 20 几行代码
第二部分正式开始啦!
在第一部分,用了总共十回的篇章,把进入 main 方法前的苦力工作都完成了,我们的程序终于跳到第一个由 c 语言写的,也是操作系统的全部代码骨架的地方,就是 main.c 文件里的 main 方法。
void main(void) {
ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
main_memory_start = buffer_memory_end;
mem_init(main_memory_start,memory_end);
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
sti();
move_to_user_mode();
if (!fork()) {
init();
}
for(;;) pause();
}
数一数看,总共也就 20 几行代码。
但这的确是操作系统启动流程的全部秘密了,我用空格将这个代码分成了几个部分。
第一部分是一些参数的取值和计算。
void main(void) {
ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
main_memory_start = buffer_memory_end;
...
}
包括根设备 ROOT_DEV,之前在汇编语言中获取的各个设备的参数信息 drive_info,以及通过计算得到的内存边界
- main_memory_start
- main_memory_end
- buffer_memory_start
- buffer_memory_end
从哪获得之前的设备参数信息呢?如果你前面看了,那一定还记得这个表,都是由 setup.s 这个汇编程序调用 BIOS 中断获取的各个设备的信息,并保存在约定好的内存地址 0x90000 处,现在这不就来取了么,我就不赘述了。
内存地址 | 长度(字节) | 名称 |
---|---|---|
0x90000 | 2 | 光标位置 |
0x90002 | 2 | 扩展内存数 |
0x90004 | 2 | 显示页面 |
0x90006 | 1 | 显示模式 |
0x90007 | 1 | 字符列数 |
0x90008 | 2 | 未知 |
0x9000A | 1 | 显示内存 |
0x9000B | 1 | 显示状态 |
0x9000C | 2 | 显卡特性参数 |
0x9000E | 1 | 屏幕行数 |
0x9000F | 1 | 屏幕列数 |
0x90080 | 16 | 硬盘1参数表 |
0x90090 | 16 | 硬盘2参数表 |
0x901FC | 2 | 根设备号 |
第二部分是各种初始化 init 操作。
void main(void) {
...
mem_init(main_memory_start,memory_end);
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
...
}
包括内存初始化 mem_init,中断初始化 trap_init、进程调度初始化 sched_init 等等。我们知道学操作系统知识的时候,其实就分成这么几块来学的,看来在操作系统源码上看,也确实是这么划分的,那我们之后照着源码慢慢品,就好了。
第三部分是切换到用户态模式,并在一个新的进程中做一个最终的初始化 init。
void main(void) {
...
sti();
move_to_user_mode();
if (!fork()) {
init();
}
...
}
这个 init 函数里会创建出一个进程,设置终端的标准 IO,并且再创建出一个执行 shell 程序的进程用来接受用户的命令,到这里其实就出现了我们熟悉的画面(下面是 bochs 启动 Linux 0.11 后的画面)。
第四部分是个死循环,如果没有任何任务可以运行,操作系统会一直陷入这个死循环无法自拔。
void main(void) {
...
for(;;) pause();
}
OK,不要细品每一句话,我们本回就是要你有个整体印象,之后会细细讲这里的每一个部分。
这里再放上目前的内存布局图。
这个图大家一定要牢记在心,操作系统说白了就是在内存中放置各种的数据结构,来实现“管理”的功能。
所以之后我们的学习过程,主心骨其实就是看看,操作系统在经过一番折腾后,又在内存中建立了什么数据结构,而这些数据结构后面又是如何用到的。
比如进程管理,就是在内存中建立好多复杂的数据结构用来记录进程的信息,再配合上进程调度的小算法,完成了进程这个强大的功能。
为了让大家目前心里有个底,我们把前面的工作再再再再在这里做一个回顾,用一张图表示就是:
看到了吧,我们已经把 boot 文件夹下的三个汇编文件的全部代码都一行一行品读过了,其主要功能就是三张表的设置:全局描述符表、中断描述符表、页表。同时还设置了各种段寄存器,栈顶指针。并且,还为后续的程序提供了设备信息,保存在 0x90000 处往后的几个位置上。
最后,一个华丽的跳转,将程序跳转到了 main.c 文件里的 main 函数中。
所以,本讲就是让大家深呼吸,把之前的准备工作再消化消化。如果第一部分全部认真看过的同学,必定觉得这一回是废话。
如果你不这样觉得,那就得再回去重新梳理一边咯,如果有不会的,赶紧查资料搞懂它,因为之后要打一系列的硬仗了!根基不稳,地动山摇!
转载
相关文章
- Linux下快速配置iSCSI存储系统(linux配置iscsi)
- 怎么解决解决Linux虚拟机卡顿的方法(虚拟机linux很卡)
- 函数Linux下禁用PHP函数的步骤(linux关闭php)
- 查看Linux下查看实际IP地址的简单方法(linux实际ip)
- Linux安装LZMA:一步一步实现安全安装(linux安装lzma)
- Linux的发展之路:从分支到大家庭(linux的分支)
- 改变Linux的编码:简单又高效(linux更改编码)
- Linux操作系统复习:25道超深度必考题(linux操作系统复习题)
- Linux系统格式化:重建你的操作系统(linux格式化系统)
- Linux信号处理:实现有效信号传递(linux信号处理函数)
- Linux的分支:一个模式多样化的操作系统(linux的分支)
- Linux的发展史:从分支到领先的操作系统(linux的分支)
- Linux路由转发功能如何关闭(关闭linux路由转发)
- Linux轻松上手:图解安装教程(安装linux教程图解)
- Linux软件:加固操作系统安全(linux软件保护)
- 操作系统 给电脑带来便捷:可装U盘的Linux操作系统(可以装u盘的linux)
- 如何选择适合自己的 Linux 版本?(linux装什么版本)
- Linux NFS搭建:实现文件系统共享(linux nfs 搭建)
- 探究Linux源码规模与庞大:到底有多少行代码?(linux源码有多大)
- Kali Linux 双系统:游刃有余的黑客操作系统(kalilinux双系统)
- ?嵌入式Linux:实现未来科技梦想的可靠手段(嵌入式linux怎么样)
- Linux下快速移动多个文件的技巧(linux移动多个文件)