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内核的引导与启动的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23linux 系统宝塔查看网站访问的命令是什么?-icode9专业技术文章分享
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南