Linux内核学习篇二:内核初始化

本节主要是记录一下Linux经过引导启动后的初始化工作,即/linux/init/main.c文件完成的工作。 这里主要记录一些初始化的流程,对于细节函数不会深究,然后重点说几个在学习中遇到的问题。

先来总览main.c函数,有几个突出的特点:
1)包含了非常多的头文件,如:<linux/tty.h><linux/sched.h><unistd.h>等等。
2)通过extern声明了很多外部函数,如:extern int vsprintf(); extern void init(); 等等。
3)采用了大量的宏定义,如:#define EXT_MEM_K (*(unsigned short *)0x90002) 等等。
以上这几个特点也从侧面说明了main函数是linux内核中的一个核心函数,其初始化过程与很多文件和函数息息相关。

main.c主要分为以下几部分:

一、初始化设备

这些函数基本都是用extern声明的外部函数,其具体定义大多存在于内核中的其他文件中,所以对此不予深究。
1)init()                 函数原型,初始化
2)blk_dev_init()    块设备初始化(块设备:比如硬盘、软盘等随机读取设备)
3)chr_dev_init()    字符设备初始化(字符设备:比如键盘、串口等顺序读取设备)
4)hd_init()            硬盘初始化
5)floppy_init()       软驱初始化
6)mem_init()        内存管理初始化
7)rd_init()             内存管理初始化

(注:
    (1)字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。举例来说,键盘、串口、调制解调器都是典型的字符设备。
    (2)块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘、软盘、CD-ROM驱动器和闪存都是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符设备不同,块设备并不支持基于字符的寻址。

二、初始化时间

开机时的系统时间是如何保存的?又是如何被系统读取的呢?
答案就是CMOS!CMOS是一块由电池独立供电的内存区域,所以即使电脑关机了,它内部的时间也是会不断更新的,这样就保证了开机时能够顺利读取其中的实时时间。
1)CMOS内存中的有效区域地址为 0x00 ~ 0x33(一共是14字节),其中包含了当前年、月、日、时、分、秒等信息。
2)初始化过程通过调用例如:time.tm_sec = CMOS_READ(0) 这样的函数返回各种时间信息,其中 0 代表CMOS中的信息地址。 time.tm_sec 等变量是 BCD码 , 需要转换成十进制进行显示,所以定义了宏  #define BCD_TO_BIN(val)  ((val) = ((val)&15) +((val)>>4)*10)   进行转换。 (val)&15 为取其个位数上的值,(val)>>4)*10为取其十位数上的值并加权。
3)最后一步:startup_time = kernel_mktime(&time); 将取出来的时间”time“送入kernel_mktime进行处理,返回值即为系统的启动时间了。

(注:
    关于CMOS内存的问题还有一个小插曲~  众所周知,Linus是初代linux内核版本的编写者,当时由于他手里并没有CMOS内存的相关资料,也就是说”他并不知道CMOS中某个字节代表什么?哪个字节代表年,哪个字节代表分?“,他在注释里解释说”我并不知道CMOS内存的地址分别表示什么信息,我都是一个个试出来的……“。我是服了。。。  

三、初始化内存

首先,介绍内存初始化之前,我们需要先了解内存的分布情况。为此从书上截了一下内存分布图,便于直观理解(其实是为了偷懒没自己画。。。)
可以看到,内存主要由四部分组成:内核区、高速缓冲区、虚拟盘、主内存区
内核区:之前第一篇介绍的引导模块,system模块等内核部分的数据都是存放在这里的。
高速缓冲区:主要是为硬盘、软盘等块设备进行读写数据准备的。
虚拟盘区:对于含有RAM虚拟盘的系统,会从主内存区的前端取出一部分作为虚拟盘区。
主内存区:所有应用程序都可以访问的数据区域,有MM内存管理模块负责分配和释放,进行分页管理。

了解了内存结构,那我们就要按照这个结构对内存进行初始化了~
1)利用memory_end 设定内存大小
2)根据内存大小范围,利用buffer_memory_end 设置缓冲区末端的位置
3)main_memory_start = buffer_memory_end ;即主内存区域起始位置 = 缓冲区末端位置
4)根据RAMDISK大小 , 将main_memory_start 位置向后移动,预留出 虚拟盘区 的空间。

四、初始化进程

自从程序启动到现在,虽然经过了很多的引导和初始化过程,但是此时仍然是一个单进程程序!那怎么实现多进程的任务呢?那就需要初始化进程了。
当前进程习惯的称为进程0,我们需要创造新的进程去执行其他应用,所以运行了如下程序:

if( ! fork() ){    
    init();    //子进程(称为进程1)去执行了 init() 函数, 而父进程则直接跳过了 if语句执行下一步,为什么?请参见下注。
}

for(;;)  pause();        //父进程执行到了这里,并且停住了,所以说父进程执行完初始化后就什么也不做了!

(注:
    这里介绍一下fork()函数,这是一个创建新进程(我们一般称为子进程)的函数。它有如下特点,请一定牢记!
    1)如果当前是子进程,fork()返回值 = 0;
    2)如果当前是父进程,fork()返回值 = 子进程的ID;

以上是main.c的简介,请注意是简!介!所以如果想要仔细研究各个函数的功能和实现细节的话,需要进一步去学习了,这里只是做一下思路的整理~


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 引子 2 1.1 上电 2 1.2 BIOS时代 3 1.3 内核引导程序 5 2 内核映像的形成 8 2.1 MakeFile预备知识 9 2.1.1 Makefile书写规则 9 2.1.2 Makefile变量 10 2.1.3 条件判断 14 2.1.4 函数 17 2.1.5 隐含规则 17 2.1.6 定义模式规则 19 2.1 KBuild体系 23 2.1.1 内核目标 24 2.1.2 主机程序 26 2.1.3 编译标志 27 2.2 内核编译分析 28 2.2.1 编译配置 29 2.2.2 寻找第一个目标 32 2.2.3 prepare和scripts目标 38 2.2.4 递归编译各对象 41 2.2.5 链接vmlinux 44 2.2.6 制作bzImage 50 3 实模式下的内核代码 57 3.1 内核映像内存布局 58 3.2 实模式汇编代码header.S 60 3.2.1 无用的bootsect代码 60 3.2.2 初始化头变量hdr 63 3.2.3 准备实模式下C语言环境 64 3.3 实模式代码main函数 69 3.3.1 复制初始化头变量 71 3.3.2 初始化堆 74 3.3.3 确保支持当前运行的CPU 75 3.3.4 设置BIOS的x86模式 76 3.3.5 内存的检测 78 3.3.6 设置键盘属性 81 3.3.7 填充系统环境配置表 82 3.3.8 填充IST信息 83 3.3.9 设置Video模式 83 3.4 实模式代码go_to_proteced_mode函数 91 3.4.1 禁止可屏蔽和不可屏蔽中断 92 3.4.2 打开A20地址线 93 3.4.3 安装临时全局描述符表 99 3.4.4 第一次启动保护模式 101 4 保护模式下的内核代码 107 4.1 32位x86保护模式代码 107 4.1.1 内核解压缩的前期工作 108 4.1.2 解压缩内核 111 4.1.3 第次启动保护模式 121 4.1.4 第一次启动分页管理 124 4.1.5 初始化0号进程 128 4.2 向start_kernel进发 131 4.2.1 初始化中断描述符表 132 4.2.2 第三次启动保护模式 137 4.2.3 启动x86虚拟机 141 5 走向现代:start_kernel函数 144 5.1 初始化同步与互斥环境 148 5.1.1 屏蔽中断 148 5.1.2 启动大内核锁 152 5.1.3 注册时钟通知链 153 5.1.4 激活第一个CPU 155 5.1.5 初始化地址散列表 160 5.1.6 打印版本信息 161 5.2 执行setup_arch()函数 166 5.2.1 拷贝可用内存区信息 171 5.2.2 获得总页面数 175 5.2.3 着手建立永久内核页表 177 5.2.4 第次启动分页管理 181 5.2.5 建立内存管理架构 186 5.2.6 添砖加瓦 192 5.3 设置每CPU环境 206 5.4 初始化内存管理区列表 211 5.5 利用early_res分配内存 214 5.6 触碰虚拟文件系统 223 5.7 初始化异常服务 224 5.8 初始化内存管理 230 5.8.1 启用伙伴算法 230 5.8.2 初始化slab分配器 241 5.8.3 初始化非连续内存区 250 5.9 初始化调度程序 251 5.10 初始化中断处理系统 256 5.10.1 设置APIC中断服务 256 5.10.2 初始化本地软时钟 264 5.10.3 软中断初始化 268 5.10.4 初始化定时器中断 271 5.11 走进start_kernel尾声 273 5.11.1 初始化slab的后续工作 273 5.11.2 启动console 275 5.11.3 一些简单的函数 276 5.11.4 校准CPU时钟速度 279 5.11.5 创建一些slab缓存 282 5.12 安装根文件系统 287 5.12.1 创建VFS相关slab缓存 288 5.12.2 安装rootfs 291 5.12.3 安装proc文件系统 296 6 后start_kernel时代 298 6.1 创建1号进程 298 6.2 子系统的初始化 306 6.3 启动shell环境 309
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值