初始化程序---main(1)

(1)小结:

1.bootsect.s程序的主要功能:将setup.s和system模块加载到内存中,并且将自身移动到0x90000处,然后控制权交给setup.s程序

2.setup程序:利用BIOS获取硬件参数并保存(main.c会用到的);将system移动到0x00000;描述符表寄存器设置;硬件中断设置;设置CR0进入32位保护模式,控制权交给head.s

3.head.s程序:初步初始化中断描述符表项(哑中断);检查A20;测试是否有协处理器;初始化内存页目录表;跳转到main.c执行内核初始化

(2)功能描述

系统执行完boot/head.s程序就会将执行权交给main.c
main.c首先利用setup.s取得的系统参数设置系统的根文件设备号以及一些内存全局变量,这些变量指明了主内存的开始地址,系统所拥有的内存容量和作为高速缓存区的末端地址。若定义了虚拟盘,则主内存将适当减少
如同系统功能
在这里插入图片描述
高速缓冲区还要扣除被显存和ROM BIOS占用的部分。
高速缓存区是用于磁盘等块设备临时存放数据的地方,以1k字节为数据块单位。

主内存区的内存由内存管理模块mm通过分页机制进行管理分配,以4k字节为一个内存页单位

内核程序可以自由访问高速缓冲区的数据,但需要通过mm才能使用分配到的内存页面

内核进行所有的方面的硬件初始化工作。包括陷阱门,块设备,字符设备,tty,还包括人工设置的第一个任务(task 0).
所有的初始化工作完成后程序就设置中断允许标志以开启中断,并切换到任务0中允许。

在整个内核初始化完成后,内核将执行权切换到用户模式(任务0),
CPU从0特权级切换到了第3个特权级,然后此时main函数工作就在任务0中,最后系统第一次调用 进程创建函数fork(),创建出一个用于运行init()
的子进程(init进程)

内核初始化流程
在这里插入图片描述

1.main.c程序首先确定如何分配使用系统物理内存,然后调用内核各部分的初始化函数分别对内存管理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘硬件进行初始化处理。在完成了这些操作之后,系统各部分已经处于可运行状态。此后程序把自己“手工”移动到任务0(进程0)中运行,并使用fork/调用首次创建出进程1(inti进程),并在其中调用min()函数。在该函数中程序将继续进行应用环境的初始化并执行shell登录程序。而原进程0则会在系统空闲时被调度执行,因此进程0通常也被称为idle进程。此时进程0仅执行pause()系统调用,并又会调用调度函数。

2.init 函数的功能可分为4个部分:①安装根文件系统:@显示系统信息;@运行系统初始资源配置文件re中的命令;④执行用户登录shell程序。

扫描二维码关注公众号,回复: 13155115 查看本文章

3.代码首先调用系统调用setup(),用来收集硬盘设备分区表信息并安装根文件系统。在安装根文件系统之前,系统会先判断是否需要先建立虚拟盘。若编译内核时设置了虚拟盘的大小,并在前面内核初始化过程中已经开辟了一块内存用作虚拟盘,则内核就会首先尝试把根文件系统加载到内存的虚拟盘区中。

4.然后inii打开一个终端设备tty0,并复制其文件描述符以产生标准输入stdin.标准输出stdout和错误输出water设备。内核随后利用这些描述符在终端上显示一些系统信息,例如高速缓冲区中缓冲块总数、主内存区空闲内存总字节数等。

5.接着init又新建了一个进程(进程2),并在其中为建立用户交互使用环境而执行一些初始配置操作,即在用户可以使用shelt命令行环境之前,内核调用/bin/sh程序运行了配置文件et心/c中设置的命令。文件的作用与DOS系统根目录上的AUTOEXEC.BAT文件类似。这段代码首先通过关闭文件描述符D,并立刻打开文件/te陵rù,从而把标准输入stdm定向到etc/re文件上。这样,所有的标准输入数据都将从该文件中读取。然后内核以非交互形式执行/sin/sh,从而实现执行/etc. re文件中的命令。当该文件中的命令执行完毕后,/bin/sh就会立刻退出。因此进程2也就随之结束。

6.init函数的最后一部份用于在新建进程中为用户建立一个新的会话,并运行用户登录shell程序,/bin/sh。在系统执行进程2中的程序时,父进程(mit进程)一直等待着它的结束。随着进程2的退出,父进程就进入到一个无限循环中。在该循环中,父进程会再次生成一个新进程,然后在该进程中创建一个新的会话,并以登录shell方式再次执行程序Dim/sh,以创建用户交互shell环境。然后父进程继续等待该子进程。登录shell虽然与前面的非交互式shell是同一个程序/bin/sh,但是所使用的命令行参数( argV[])不同。登录shelL的第0个命令行参数的第1个字符一定是一个减号比。这个特定的标志会在/bn/h执行时通知它这不是一次普通的运行,而是作为登录sh出l运行Jim/sh的。从这时开始,用户就可以正常使用Linux命令行环境了,而父进程随之又进入等待状态。此后若用户在命令行上执行了exit或Ilogount命令,那么在显示一条当前登录sheel退出的信息后,系统就会在这个无限循环中再次重复以上创建登录shelf进程的过程。

7.任务1中运行的init()函数的后两部分实际上应该是独立的环境初始化程序mit等的功能。

3.内联函数
由于创建新进程是通过完全复制父进程的代码段和数据段,因此在首次使用fork()创建进程的时候,为了确保新进程 用户态栈中没有进程0的多余信息,要求进程0在创建首个新进程(进程1)之前,不要使用其用户态栈,即要求任务0不要调用函数。

所以在main程序移动到任务0执行后,任务0中的代码fork()不能以函数形式进行调用,从而引入了gcc函数内嵌形式来执行这个系统调用。

static inline _syscall0(int ,fork) —>内联函数
通过声明一个内联函数,可以让gcc把函数的代码集成调用到它的代码中
省去函数调用的内存,提高代码执行速度
注;这里的0表示后面为参数,若有一个参数则是_syscall1

-syscall0()是unistd.h中内嵌宏代码,以嵌入式汇编形式调用Linux的系统调用中断 int 0x80,也即是int fork()创建进程系统调用

gcc语法文章详情:
添加链接描述

#define _syscall0(type,name) \
type name(void) \
{
      
       \
long __res; \                    // 声明一个寄存器
__asm__ volatile ("int $0x80" \  //调用系统中断 0x80
	: "=a" (__res) \             //将返回值-->eax(_res) 输出寄存器
	: "0" (__NR_##name)); \    //输入为系统中断调用号_NR_name 输入寄存器
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

(4)CMOS

PC机的CMOS内存大小为128或者64字节内存块,是系统实时时钟芯片RTC的一部分,保存时间和日期信息,存放的格式是BCD码

要访问CMOS需要通过端口0x70(地址端口),0x71(数据端口)

CMOS64字节信息:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_53144843/article/details/120395215
今日推荐