- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
我个人是软件工程专业的学生,但是对计算机是怎么运行的一直都有疑惑。 为什么一通电计算机就能运行了,处理各种任务。 如果对计算机底层知识的不熟悉和不理解的话,这就造成了在了解各种中间件或者软件的底层原理的时候,会比较吃力,对于程序运行的具体情况也不太清楚。 我个人目前认为只有对程序在计算机中的具体执行过程有着深入的了解,才能写出性能更好、更健壮性的代码,所以开始学习Linux内核。
资料
《趣谈Linux内核 》 刘超 极客时间专栏
Linux源码
路线
学习Linux内核切记不要太过深入死扣代码,而是要先在宏观上了解Linux的运行原理,然后再深入了解具体实现原理
相信很多人都有这样的疑惑:为什么通电后,按下启动按钮,操作系统就运行了?
BIOS初始化
当通电后,CPU就开始执行指令了,执行的是什么指令呢?主板上有一块ROM,ROM是只读存储器,里面存储了BIOS程序。CPU会执行BIOS程序来加载操作系统。
BIOS的全称是 basic input and output system,也就是基本输入输出系统。
上述图片是bios时期的CPU地址空间的映射布局。
此时CPU处于实模式,寄存器的位数是16位,然后地址线是20位,CPU的寻址空间是2^20,也就是1MB,即使是更先进的CPU,支持64位寄存器或者超过20根的地址线,同样只能使用寄存器中的16位和其中的20根地址线。
地址的转换规则为 DS寄存器左移4位 + IP寄存器。此时CPU处于ring0特权,可以执行所有指令,操作硬件。
其中0X F0000 到 0X FFFFF 这 64kb的地址空间映射到 ROM中。
当CPU通电后,会做一些重置操作,讲DS寄存器赋值为0X FFFF , IP寄存器为 0X 0000,所以访问地址就对应着 0X FFFF0,地址0X FFFF0对应的空间 处于ROM的空间中,也就是开始执行bios程序。
1、初始化中断向量表等数据,因为用户可以通过鼠标、键盘等操作方式来操作BIOS程序,所以
2、初始化硬件资源:显示器、内存、磁盘等资源
3、加载操作系统,也就是进入BootLoader阶段
BootLoader阶段
BootLoader阶段,BIOS程序会记载操作系统内核,怎么加载呢?
检测存储设备的第一个扇区是不是以0xAA55结尾的。 如果是以0xAA55结尾就说明这是一个操作系统的启动区。我们都知道一个扇区的大小一般是512字节,一个操作系统编译后的代码一般都是好几百MB,第一个扇区是无法装下操作系统所有的代码的,所以只会将操作系统的启动代码存储到第一个扇区内,这个扇区也叫做引导扇区,存储的内容叫做boot.img。
boot.img是grub2工具写入到磁盘中的。
bios程序会将boot.img 加载到 内存中的 0x 7C00,然后利用jump指令,跳转到boot.img,至此BIOS程序就算功德圆满了,接下来就看grub2的了。
grub2加载内核过程
grub2是Grand Unified Bootloader Version 2,是一个多系统启动程序,用来一个硬盘上存在多个系统的启动,多个系统的相关配置信息被写入到了一个配置文件grub.cfg中。grub.cfg中写入了操作系统的一些配置,包括系统的版本、系统镜像的位置等信息
menuentry 'CentOS Linux (3.10.0-862.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-862.el7.x86_64-advanced-b1aceb95-6b9e-464a-a589-bed66220ebee' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod ext2
set root='hd0,msdos1'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' b1aceb95-6b9e-464a-a589-bed66220ebee
else
search --no-floppy --fs-uuid --set=root b1aceb95-6b9e-464a-a589-bed66220ebee
fi
linux16 /boot/vmlinuz-3.10.0-862.el7.x86_64 root=UUID=b1aceb95-6b9e-464a-a589-bed66220ebee ro console=tty0 console=ttyS0,115200 crashkernel=auto net.ifnames=0 biosdevname=0 rhgb quiet
initrd16 /boot/initramfs-3.10.0-862.el7.x86_64.img
}
grub2的程序主要包括 boot.img core.img
boot.img会加载core.img,core.img的一个扇区中存的是disk.img。
disk.img用来加载core.img中的其他模块,比如lzma_decompress.img、kernel.img等。这里的kernel是指grub2的内核,而不是操作系统的。
disk.img 加载lzma_decompress.img,lzma_decompress.img会对kernel.img进行解压,实模式的支持的内容空间不到1MB,而kernel.img占的内存比较大,所以要从实模式切换到保护模式。
保护模式的做法:
1、打开Gate20地址线,CPU可以使用其他的地址线了,意味着更大的寻址空间
2、打开分段和分页,之前的映射方式是地址的翻译方式为 DS寄存器左移4位 + IP寄存器,这种访问方式是不安全的,一个进程可以访问另一个进程的数据。所以引入了逻辑地址的方法,通过页表将逻辑地址转化为物理地址,而页表由操作系统内核维护,可以做到进程之间的安全隔离。
切换到保护模式后,lzma_decompress.img会执行kernel.img开始真正的加载操作系统。
kernel.img会调用grub_load_config() 方法来解析grub.cfg, 里面有一个grub_show_menu()会让用户选择操作系统
选择完操作系统后,就会根据grub.cfg里面的配置信息来加载操作系统啦。
初始化Linux
1、初始化中断向量表
2、初始化内存管理模块
3、初始化进程调度模块
4、初始化VFS,虚拟文件系统
5、 初始化进程
kernel_init的大概定义如下,是要执行init文件,来进行初始化工作
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
init文件是存储在Linux的根文件系统中的,根文件系统是一个内存文件系统。因为市面上由太多的文件系统,无法将所有的文件系统的驱动程序都加入到内核中,所以就引入了一个根文件内存系统,然后在1号进程中系统调用开始初始化内核。
在执行1号进程之前,会将CPU从内核态切换到用户态。
void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
set_user_gs(regs, 0);//切换到用户态
//设置寄存器的值
regs->fs = 0;
regs->ds = __USER_DS;
regs->es = __USER_DS;
regs->ss = __USER_DS;
regs->cs = __USER_CS;
regs->ip = new_ip;
regs->sp = new_sp;
regs->flags = X86_EFLAGS_IF;
force_iret();
}
EXPORT_SYMBOL_GPL(start_thread);
我是 Spring 新手,这就是我想要做的事情: 我正在使用一个基于 Maven 的库,它有自己的 Spring 上下文和 Autowiring 字段。 它的bean配置文件是src/test/res
我在我的测试脚本中有以下列表初始化: newSequenceCore=["ls", "ns", "*", "cm", "*", "ov", "ov", "ov", "ov", "kd"] (代表要在控
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Class construction with initial values 当我查看 http://en.
我得到了成员变量“objectCount”的限定错误。编译器还返回“ISO C++ 禁止非常量静态成员的类内初始化”。这是主类: #include #include "Tree.h" using n
我有如下所示的a.h class A { public: void doSomething()=0; }; 然后我有如下所示的b.h #include "a.h" class b: publi
我需要解析 Firebase DataSnapshot (一个 JSON 对象)转换成一个数据类,其属性包括 enum 和 list。所以我更喜欢通过传递 DataSnapshot 来手动解析它进入二
我使用 JQuery 一段时间了,我总是使用以下代码来初始化我的 javascript: $(document).ready( function() { // Initalisation logic
这里是 Objective-C 菜鸟。 为什么会这样: NSString *myString = [NSString alloc]; [myString initWithFormat:@"%f", s
我无法让核心数据支持的 NSArrayController 在我的代码中正常工作。下面是我的代码: pageArrayController = [[NSArrayController alloc] i
我对这一切都很陌生,并且无法将其安装到我的后端代码中。它去哪里?在我的页脚下面有我所有的 JS? 比如,这是什么意思: Popup initialization code should be exec
这可能是一个简单的问题,但是嘿,我是初学者。 所以我创建了一个程序来计算一些东西,它目前正在控制台中运行。我决定向其中添加一个用户界面,因此我使用 NetBeans IDE 中的内置功能创建了一个 J
我有 2 个 Controller ,TEST1Controller 和 TEST2Controller 在TEST2Controller中,我有一个initialize()函数设置属性值。 如果我尝
据我所知, dependentObservable 在声明时会进行计算。但如果某些值尚不存在怎么办? 例如: var viewModel ={}; var dependentObservable1 =
我正在阅读 POODR 这本书,它使用旧语法进行默认值初始化。我想用新语法实现相同的功能。 class Gear attr_reader :chainring, :cog, :wheel de
我按照 polymer 教程的说明进行操作: https://www.polymer-project.org/3.0/start/install-3-0 (我跳过了可选部分) 但是,在我执行命令“po
很抱歉问到一个非常新手的Kotlin问题,但是我正在努力理解与构造函数和初始化有关的一些东西。 我有这个类和构造函数: class TestCaseBuilder constructor(
假设我们有一个包含 30 列和 30 行的网格。 生命游戏规则简而言之: 一个小区有八个相邻小区 当一个细胞拥有三个存活的相邻细胞时,该细胞就会存活 如果一个细胞恰好有两个或三个活的相邻细胞,那么它就
我是 MQTT 和 Android 开放附件“AOA” 的新手。在阅读教程时,我意识到,在尝试写入 ByteArrayOutputStream 类型的变量之前,应该写入 0 或 0x00首先到该变量。
我有 2 个 Controller ,TEST1Controller 和 TEST2Controller 在TEST2Controller中,我有一个initialize()函数设置属性值。 如果我尝
我有一个inotify /内核问题。我正在使用“inotify” Python项目进行观察,但是,我的问题仍然是固有的关于inotify内核实现的核心。 Python inotify项目处理递归ino
我是一名优秀的程序员,十分优秀!