抄写Linux源码(Day15:进入 main 函数后)

2023-10-07 07:20

本文主要是介绍抄写Linux源码(Day15:进入 main 函数后),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

回忆我们需要做的事情:
为了支持 shell 程序的执行,我们需要提供:
1.缺页中断(不理解为什么要这个东西,只是闪客说需要,后边再说)
2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的,所以需要这两个东西)
3.fork,execve, wait 这三个系统调用,也可以说是 进程调度 (否则无法 halt shell 程序并且启动另外的程序)
4.键盘驱动、VGA/console/uart 驱动、中断处理 (支持键盘输入和屏幕显示)
5.内存管理 (shell 启动其它进程时,不能共用内存,而是切换其它进程的页表)
6.为了写代码方便,我们需要从 MBR 进入到 main 函数,这也是从 汇编 切换到 C 语言 — 已经完成
7.应用程序申请内存的接口

现在已经进入 main 函数了,那么,进入 main 函数后我们要怎么实现上面提到的,还没完成的 6 个要求呢?我们要实现它们才能启动 shell

看闪客文章第十一回 — start

在第一部分,用了总共十回的篇章,把进入 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;elsebuffer_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;elsebuffer_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 处,现在这不就来取了么,我就不赘述了。

在这里插入图片描述

第二部分是各种初始化 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 函数中。

所以,本讲就是让大家深呼吸,把之前的准备工作再消化消化。如果第一部分全部认真看过的同学,必定觉得这一回是废话。

如果你不这样觉得,那就得再回去重新梳理一边咯,如果有不会的,赶紧查资料搞懂它,因为之后要打一系列的硬仗了!根基不稳,地动山摇!

预知后事如何,且听下回分解。

看闪客文章第十一回 — end

这篇关于抄写Linux源码(Day15:进入 main 函数后)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/156901

相关文章

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很