2.0 Linux内核的引导与启动

2021/12/24 7:09:40

本文主要是介绍2.0 Linux内核的引导与启动,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1:主要文件

内核版本-0.11
主要文件位置 linux/boot/
bootsect.s setup.s head.s

2: CPU启动

cpu上电后会进入自动模式,并会从0xFFFF0 (x86 是 存储的总线地址)开始自动执行代码,把bootsect从固定的地址拿到内存中的某个固定地址(0x7C00),bootsect.s 执行时把自己移动到(0x90000)进行了一些硬件初始化和参数设置,并把setup.s读到内存的0x90200处,内核其他部分(system模块)读到内存地址0x10000(64KB处)

整体过程
在这里插入图片描述

3: 启动文件分析

3.1 bootsect.s

磁盘引导块程序,在磁盘第一个扇区中的程序

作用:首先将后续的setup.s 代码加载到紧接着bootsect.s的地方
     在显示屏上显示loading system 再将system(操作系统)模块加载到0x10000的地方
      最后跳转到setup.s
//bootsect.s
//移动各个代码的位置
SETUPLEN = 4				! nr of setup-sectors
BOOTSEG  = 0x07c0			! original address of boot-sector
INITSEG  = 0x9000			! we move boot here - out of the way
SETUPSEG = 0x9020			! setup starts here
SYSSEG   = 0x1000			! system loaded at 0x10000 (65536).
ENDSEG   = SYSSEG + SYSSIZE		! where to stop loading

3.2 setup.s

解析bios/bootloader传递的参数
设置系统内核运行的LDT(局部描述符) IDT(中断描述符寄存器)全局描述符(设置全局描述符寄存器)
设置中断控制芯片,进入保护模式运行
跳转到sysytem模块最前面的代码运行(head.s)
在! ok, the read went well so we get current cursor position and save it for
! posterity.

	mov	ax,#INITSEG	! this is done in bootsect already, but...
	mov	ds,ax
	mov	ah,#0x03	! read cursor pos
	xor	bh,bh
	int	0x10		! save it in known place, con_init fetches
	mov	[0],dx		! it from 0x90000.  //读光标位置
 
! Get memory size (extended mem, kB)

	mov	ah,#0x88
	int	0x15
	mov	[2],ax //扩展内存大小

! Get video-card data:

	mov	ah,#0x0f
	int	0x10
	mov	[4],bx		! bh = display page//显存大小
	mov	[6],ax		! al = video mode, ah = window width
! check for EGA/VGA and some config parameters //显示的配置参数

	mov	ah,#0x12
	mov	bl,#0x10
	int	0x10
	mov	[8],ax
	mov	[10],bx
	mov	[12],cx

2个硬盘参数表
根文件系统
....
....

3.3 head.s

加载内核运行是的各数据段寄存器,重新设置中断描述符表
开始内核正常运行时的协处理器等资源
设置内存管理的分页机制
跳转到main.c开始运行

4 主函数

main.c
任何初始化主程序,初始化结束后以任务0(idle任务即空闲任务)的身份运行
关于 static inline _syscall0(int,fork)查看 关于inline和_syscall0和汇编函数带下划线

static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)

/*
 * This is set up by the setup-routine at boot-time
 */
#define EXT_MEM_K (*(unsigned short *)0x90002) //1MB以后的扩展内存的大小
#define DRIVE_INFO (*(struct drive_info *)0x90080)//硬盘参数表32字节内容
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)//根文件系统所在设备号

void main(void)		/* This really IS void, no error here. */
{			/* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 /*
 	下面的代码用于保存 
 	根设备号:ROOT_DEV  
 	高速缓存末端地址:buffer_memory_end 
 	机器内存数:memory_end 
 	主存内内的开始地址:main_memory_start 
 */
 	ROOT_DEV = ORIG_ROOT_DEV;//设置操作系统的根文件
 	drive_info = DRIVE_INFO;//设置驱动系统的操作参数
	memory_end = (1<<20) + (EXT_MEM_K<<10);//解析setup.s获取系统内存参数,设置系统内存大小 (系统本身内存(1<<20) 为1M) + ((EXT_MEM_K<<10)为 EXT_MEM_K * 1KB)
	memory_end &= 0xfffff000;;//忽略不到4KB的内存数, 4K对齐
	if (memory_end > 16*1024*1024)//控制操作系统的内存最大内存为16M
		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; //主内存的开始位置是缓冲内存的结束地址
#ifdef RAMDISK //在Makefile中定义了内存虚拟盘符RAMDISK,则初始化了虚拟盘,主内存起始地址将会向后移动
	main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
	mem_init(main_memory_start,memory_end); //主内存区初始化
	trap_init();//陷阱门(硬件中断向量)初始化
	blk_dev_init();//块设备初始化
	chr_dev_init();//字符设备初始化
	tty_init();//tty初始化
	time_init();//设置开机启动时间
	sched_init();//调度程序初始化,并创建0号进程
	buffer_init(buffer_memory_end);//缓存管理初始化,建内存链表
	hd_init();//硬盘初始化
	floppy_init();//软驱动初始化
	sti();//初始化完成,开启中断
	move_to_user_mode();//移到用户模式
	if (!fork()) {		/* we count on this going ok */
		init(); //1号进程执行
	}
	/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
	for(;;) pause();//pause系统调用,会把任务0转换成可中断等待状态,再执行调度函数。
	//但是调度函数只要发现系统中没有其他任务可以运行时就会切换到任务0,而不会依赖任务0的状态
}

对应的内存的划分
在这里插入图片描述

init 0号进程运行的
起点:磁盘引导程序,需要将内核等移入内存中运行,并初始化多种模块
终点:运行第一个应用程序,

void init(void)
{
	int pid,i;

	setup((void *) &drive_info); //系统调用 获取硬盘参数并加载虚拟盘(如果存在)和安装根文件系统设备
	(void) open("/dev/tty0",O_RDWR,0); //打开标准输入控制台
	(void) dup(0);//dup复制句柄 ,打开标准输出控制台
	(void) dup(0);//dup复制句柄 ,打开标准错误控制台
	printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
		NR_BUFFERS*BLOCK_SIZE);
	printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
	//创建2号进程,如果在1号父进程创建成功则 fork函数返回0,如果在子进程中fork则返回父进程PID,对应25行的宏定义,对应函数时sys_setup
	//返回子为0 在子进程中进行
	//返回值非0 返回值为子进程进程号,则在父进程中进行
	if (!(pid=fork())) {
		//子进程
		close(0);//关闭创建的标准输入输出
		if (open("/etc/rc",O_RDONLY,0))//打开系统配置文件
			_exit(1);
		execve("/bin/sh",argv_rc,envp_rc);//挂接文件系统,执行shell程序
		_exit(2);
	}
	if (pid>0)
		while (pid != wait(&i)) //等待子程序退出
			/* nothing */;
	//如果进程2退出,或者进程创建失败。就进入循环中,创建新的子进程,新的子进程退出或者失败,就再创建,循环往复
	while (1) {
		if ((pid=fork())<0) {
			printf("Fork failed in init\r\n");
			continue;
		}
		if (!pid) {//子进程
			close(0);close(1);close(2); //关闭
			setsid();
			(void) open("/dev/tty0",O_RDWR,0); //打开新的控制台
			(void) dup(0);
			(void) dup(0);
			_exit(execve("/bin/sh",argv,envp));//执行shell,用来相应输入的shell命令
		}
		while (1)
			if (pid == wait(&i))//等待子程序退出
				break;
		printf("\n\rchild %d died with code %04x\n\r",pid,i);
		sync();
	}
	_exit(0);	/* NOTE! _exit, not exit() */
}

在这里插入图片描述



这篇关于2.0 Linux内核的引导与启动的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程