从程序员角度深入计算机系统的底层 内核引导过程

linux内核解析笔记 立即下载
上一篇文章解释了计算机的引导过程,正好讲到引导装载程序把系统内核镜像塞进内存,准备跳转到内核入口点去执行的时刻。作为引导启动系列文章的最后一篇,就让我们深入内核,去看看操作系统是怎么启动的吧。由于我习惯以事实为依据讨论问题,所以文中会出现大量的链接引用Linux 内核2.6.25.6版的源代码(源自Linux Cross Reference)。如果你熟悉C的语法,这些代码就会非常容易读懂;即使你忽略一些细节,仍能大致明白程序都干了些什么。最主要的障碍在于对一些代码的理解需要相关的背景知识,比如机器的底层特性或什么时候、为什么它会运行。我希望能尽量给读者提供一些背景知识。为了保持简洁,许多有趣的东西,比如中断和内存,文中只能点到为止了。在本文的最后列出了Windows的引导过程的要点。
当Intel x86的引导程序运行到此刻时,处理器处于实模式(可以寻址1MB的内存),(针对现代的Linux系统)RAM的内容大致如下:


5b3bc6c16076314931e30f0a3d8c70fc.png



引导装载完成后的RAM内容




引导装载程序通过BIOS的磁盘I/O服务,已经把内核镜像加载到内存当中。这个镜像只是硬盘中内核文件(比如/boot/vmlinuz-2.6.22-14-server)的一份完全相同的拷贝。镜像分为两个部分:一个较小的部分,包含实模式的内核代码,被加载到640KB内存边界以下;另一部分是一大块内核,运行在保护模式,被加载到低端1MB内存地址以上。

如上图所示,之后的事情发生在实模式内核的头部(kernel header)。这段内存区域用于实现引导装载程序与内核之间的Linux引导协议。此处的一些数据会被引导装载程序读取。这些数据包括一些令人愉快的信息,比如包含内核版本号的可读字符串,也包括一些关键信息,比如实模式内核代码的大小。引导装载程序还会向这个区域写入数据,比如用户选中的引导菜单项对应的命令行参数所在的内存地址。之后就到了跳转到内核入口点的时刻。下图显示了内核初始化代码的执行顺序,包括源代码的目录、文件和行号:

55c0b350ac22bd846a38c845ef0d5efe.png




与体系结构相关的Linux内核初始化过程


对于Intel体系结构,内核启动前期会执行arch/x86/boot/header.S文件中的程序。它是用汇编语言书写的。一般说来汇编代码在内核中很少出现,但常见于引导代码。这个文件的开头实际上包含了引导扇区代码。早期的Linux不需要引导装载程序就可以工作,这段代码是从那个时候留传下来的。现今,如果这个引导扇区被执行,它仅仅给用户输出一个“bugger_off_msg”之后就会重启系统。现代的引导装载程序会忽略这段遗留代码。在引导扇区代码之后,我们会看到实模式内核头部(kernel header)最开始的15字节;这两部分合起来是512字节,正好是Intel硬件平台上一个典型的磁盘扇区的大小。

在这512字节之后,偏移量0x200处,我们会发现Linux内核的第一条指令,也就是实模式内核的入口点。具体的说,它在header.S:110,是一个2字节的跳转指令,直接写成了机器码的形式0x3AEB。你可以通过对内核镜像运行hexdump,并查看偏移量0x200处的内容来验证这一点——这仅仅是一个对神志清醒程度的检查,以确保这一切并不是在做梦。引导装载程序运行完毕时就会跳转执行这个位置的指令,进而跳转到header.S:229执行一个普通的用汇编写成的子程序,叫做start_of_setup。这个短小的子程序初始化栈空间(stack),把实模式内核的bss段清零(这个区域包含静态变量,所以用0来初始化它们),之后跳转执行一段又老又好的C语言程序:arch/x86/boot/main.c:122。

main()会处理一些登记工作(比如检测内存布局),设置显示模式等。然后它会调用go_to_protected_mode()。然而,在把CPU置于保护模式之前,还有一些工作必须完成。有两个主要问题:中断和内存。在实模式中,处理器的中断向量表总是从内存的0地址开始的,然而在保护模式中,这个中断向量表的位置是保存在一个叫IDTR的CPU寄存器当中的。与此同时,从逻辑内存地址(在程序中使用)到线性内存地址(一个从0连续编号到内存顶端的数值)的翻译方法在实模式和保护模式中是不同的。保护模式需要一个叫做GDTR的寄存器来存放内存全局描述符表的地址。所以go_to_protected_mode()调用了setup_idt()
和 setup_gdt(),用于装载临时的中断描述符表和全局描述符表。

现在我们可以转入保护模式啦,这是由另一段汇编子程序protected_mode_jump来完成的。这个子程序通过设定CPU的CR0寄存器的PE位来使能保护模式。此时,分页功能还处于关闭状态;分页是处理器的一个可选的功能,即使运行于保护模式也并非必要。真正重要的是,我们不再受制于640K的内存边界,现在可以寻址高达4GB的RAM了。这个子程序进而调用压缩状态内核的32位内核入口点startup_32。startup32会做一些简单的寄存器初始化工作,并调用一个C语言编写的函数decompress_kernel(),用于实际的解压缩工作。

decompress_kernel()会打印一条大家熟悉的信息“Decompressing Linux…”(正在解压缩Linux)。解压缩过程是原地进行的,一旦完成内核镜像的解压缩,第一张图中所示的压缩内核镜像就会被覆盖掉。因此解压后的内核也是从1MB位置开始的。之后,decompress_kernel()会显示“done”(完成)和令人振奋的“Booting the kernel”(正在引导内核)。这里“Booting”的意思是跳转到整个故事的最后一个入口点,也是保护模式内核的入口点,位于RAM的第二个1MB开始处(偏移量0x100000,此值是由芬兰Halti山巅之上的神灵授意给Linus的)。在这个神圣的位置含有一个子程序调用,名叫…呃…startup_32。但你会发现这一位是在另一个目录中的。

这位startup_32的第二个化身也是一个汇编子程序,但它包含了32位模式的初始化过程:

  • 它清理了保护模式内核的bss段。(这回是真正的内核了,它会一直运行,直到机器重启或关机。)

  • 为内存建立最终的全局描述符表。

  • 建立页表以便可以开启分页功能使能分页功能。

  • 初始化栈空间。

  • 创建最终的中断描述符表。

  • 最后,跳转执行一个体系结构无关的内核启动函数:start_kernel()。


下图显示了引导最后一步的代码执行流程:

9ed004eb570a51908a8c12742b6bdcb5.png




与体系结构无关的Linux内核初始化过程



start_kernel()看起来更像典型的内核代码,几乎全用C语言编写而且与特定机器无关。这个函数调用了一长串的函数,用来初始化各个内核子系统和数据结构,包括调度器(scheduler),内存分区(memory zones),计时器(time keeping)等等。之后,start_kernel()调用rest_init(),此时几乎所有的东西都可以工作了。rest_init()会创建一个内核线程,并以另一个函数kernel_init()作为此线程的入口点。之后,rest_init()会调用schedule()来激活任务调度功能,然后调用cpu_idle()使自己进入睡眠(sleep)状态,成为Linux内核中的一个空闲线程(idle thread)。cpu_idle()会在0号进程(process zero)中永远的运行下去。一旦有什么事情可做,比如有了一个活动就绪的进程(runnable process),0号进程就会激活CPU去执行这个任务,直到没有活动就绪的进程后才返回。

但是,还有一个小麻烦需要处理。我们跟随引导过程一路走下来,这个漫长的线程以一个空闲循环(idle loop)作为结尾。处理器上电执行第一条跳转指令以后,一路运行,最终会到达此处。从复位向量(reset vector)->BIOS->MBR->引导装载程序->实模式内核->保护模式内核,跳转跳转再跳转,经过所有这些杂七杂八的步骤,最后来到引导处理器(boot processor)中的空闲循环cpu_idle()。看起来真的很酷。然而,这并非故事的全部,否则计算机就不会工作。

在这个时候,前面启动的那个内核线程已经准备就绪,可以取代0号进程和它的空闲线程了。事实也是如此,就发生在kernel_init()开始运行的时刻(此函数之前被作为线程的入口点)。kernel_init()的职责是初始化系统中其余的CPU,这些CPU从引导过程开始到现在,还一直处于停机状态。之前我们看过的所有代码都是在一个单独的CPU上运行的,它叫做引导处理器(boot processor)。当其他CPU——称作应用处理器(application processor)——启动以后,它们是处于实模式的,必须通过一些初始化步骤才能进入保护模式。大部分的代码过程都是相同的,你可以参考startup_32,但对于应用处理器,还是有些细微的不同。最终,kernel_init()会调用init_post(),后者会尝试启动一个用户模式(user-mode)的进程,尝试的顺序为:/sbin/init,/etc/init,/bin/init,/bin/sh。如果都不行,内核就会报错。幸运的是init经常就在这些地方的,于是1号进程(PID 1)就开始运行了。它会根据对应的配置文件来决定启动哪些进程,这可能包括X11 Windows,控制台登陆程序,网络后台程序等。从而结束了引导进程,同时另一个Linux程序开始在某处运行。至此,让我祝福您的电脑可以一直正常运行下去,不出毛病。

在同样的体系结构下,Windows的启动过程与Linux有很多相似之处。它也面临同样的问题,也必须完成类似的初始化过程。当引导过程开始后,一个最大的不同是,Windows把全部的实模式内核代码以及一部分初始的保护模式代码都打包到了引导加载程序(C:\NTLDR)当中。因此,Windows使用的二进制镜像文件就不一样了,内核镜像中没有包含两个部分的代码。另外,Linux把引导装载程序与内核完全分离,在某种程度上自动的形成不同的开源项目。下图显示了Windows内核主要的启动过程:

1de8b760e86302f0c378609130f32e0b.png



Windows内核初始化过程



自然而然的,Windows用户模式的启动就非常不同了。没有/sbin/init程序,而是运行Csrss.exe和Winlogon.exe。Winlogon会启动Services.exe(它会启动所有的Windows服务程序)、Lsass.exe和本地安全认证子系统。经典的Windows登陆对话框就是运行在Winlogon的上下文中的。
本文是引导启动系列话题的最后一篇。感谢每一位读者,感谢你们的反馈。我很抱歉,有些内容只能点到为止;我打算把它们留在其他文章中深入讨论,并尽量保持文章的长度适合blog的风格。下次我打算定期的撰写关于“Software Illustrated”的文章,就像本系列一样。最后,给大家一些参考资料:

  • 最好也最重要的资料是实际的内核代码,Linux或BSD的都成。


  • 《理解Linux内核》是本好书,其中讨论了大量的Linux内核代码。这书也许有点过时有点枯燥,但我还是将它推荐给那些想要与内核心意相通的人们。《Linux设备驱动程序》读起来会有趣得多,讲的也不错,但是涉及的内容有些局限性。最后,网友Patrick Moroney推荐Robert Love所写的《Linux内核开发》,我曾听过一些对此书的正面评价,所以还是值得列出来的。

  • 对于Windows,目前最好的参考书是《Windows Internals》,作者是David Solomon和Mark Russinovich,后者是Sysinternals的知名专家。这是本特棒的书,写的很好而且讲解全面。主要的缺点是缺少源代码的支持。
深入理解计算机系统(程序员角度的计算机系统)(原书第2版).part07 本书从程序员的视角详细阐述计算机系统的本质概念,并展示这些概念如何实实在在地影响应用程序的正确性、性能和实用性。这是其中第七章 立即下载

相关推荐

从程序员角度深入计算机系统的底层

从程序员角度深入计算机系统的底层,非原创,但是不知道作者是谁,如果侵犯到您的权益请告知。写得不错,系统总结了系统引导的过程。从硬件自举--MBR--Bootloader--引导分区---内核装载。

内核引导过程

原文标题:The Kernel Boot Process原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。] 上一篇文章解释了计算机的引导过程,正好讲到引导装载程序把系统内核镜像塞进内存,准备跳转到内核入口点去执行的时刻。作为引导启动系列文章的最后一篇,就让我们深入

沈雷的专栏 5512

【课程设计】TCP/IP 应用环境中服务器的部署与实现

本项目旨在通过搭建一个虚拟网络环境,实现软路由系统的配置和服务器的搭建与测试。具体包括安装虚拟机 VMWare Workstation,安装 Windows Server 2003 操作系统,配置软路由系统(包括双网卡安装、协议配置和测试),以及搭建并测试 DHCP 服务、DNS 服务、WWW 服务、FTP 服务和 SMTP/POP3 服务。

weixin_74907236的博客 1524

Linux 引导过程的 6 个阶段(启动顺序)

按下电脑上的电源按钮,片刻之后您就会看到 Linux 登录提示。 你有没有想过从按下电源按钮到 Linux 登录提示出现在幕后发生了什么? 以下是典型 Linux 引导过程的 6 个高级阶段。 1. BIOS BIOS 代表基本输入/输出系统 执行一些系统完整性检查 搜索、加载和执行引导加载程序。 它在软盘、CD-ROM 或硬盘驱动器中查找引导加载程序。您可以在 BIOS 启动期间按一个键(通常是 F12 或 F2,但这取决于您的系统)来更改启动顺序。 一旦检测到引导加载程序并将.

qq_39652397的博客 1704

别再死记硬背了!用LabVIEW串口通信的3个真实项目案例,手把手教你从调试到部署

本文通过3个真实项目案例,详细讲解LabVIEW串口通信从调试到部署的全过程。涵盖Arduino温湿度监控、西门子PLC Modbus通信以及高可靠性通信框架设计,提供实用技巧和常见问题解决方案,帮助工程师快速掌握LabVIEW串口通信的实战应用。

weixin_30485379的博客 524

Codesys—Modbus RTU称重变送器通讯案例

Codesys控制器使用标准的Modbus库,与支持ModbusRTU的称重变送器进行通讯的案例

原来你也__的博客 2741

Linux引导过程

操作系统的启动分为两个阶段:引导和启动。引导阶段开始于打开电源开关,结束于内核初始化完成和 systemd 进程成功运行。启动阶段接管了剩余工作,直到操作系统进入可操作状态。

g_l_d_y的博客 723

source tree clone error:fatal remote error: Repository not found

source tree clone error:fatal remote error: Repository not found

Linda的前端开发路 3077

内核启动顺序

Linux 开机脚本启动顺序: 第一步:启动内核 第二步:执行init (配置文件/etc/inittab) 第三步:启动相应的脚本,执行inittab脚本,并且执行里面的脚本/etc/init.d rc.sysinit rc.d rc.local。。。 第四步:启动login登录界面 login 第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的/etc/pr...

u014426028的博客 399

Amesim应用篇-电芯等效电路模型标定

为了使计算模型更加准确,在有电芯实验测试数据的情况下,依据现有的实验数据对Amesim中的电池等效电路模型进行标定。标定的目的是为了获得更加符合项目实际情况的电芯等效电路模型,标定完的电芯可以用于搭建PACK模型,也可以用于其他虚拟实验。本文以充电标定为例,进行介绍。放电标定与充电一致。

2201_75375391的博客 1213

Python爬虫实战:新浪财经股票金融数据全方位抓取教程

本教程系统介绍了新浪财经股票数据爬取技术,从静态网页解析、实时接口请求到异步爬取和数据存储,帮助你搭建了一个完整的金融数据采集流程。未来,可以结合深度学习模型进行行情预测,或者将数据接入量化交易系统,实现自动化投资。

2201_76125261的博客 2210

linux服务器开机进程启动顺序,linux系统启动流程相关题目

文章目录一. 面试考核理论题1.linux系统启动流程:2.grub中的bootloader损坏如何修复,步骤是什么3.initramfs文件的作用4.grub的各个阶段功能5.grub2的引导过程简述6.grub和grub2的区别7.内核的功能是什么,如何修改内核的参数8.简述centos7的systemd初始化过程二. 实操题1.删除grub.conf,然后重启恢复2.进单用户模式恢复密码3....

weixin_39597262的博客 557

大学计算 - 方程求解 实训答案 第3关

【代码】大学计算 - 方程求解 实训答案 第3关。

2403_89314916的博客 107

java程序员应该掌握的底层知识

文章目录java程序员应该掌握的底层知识相关书籍推荐硬件基础知识CPU的制作过程CPU的原理计算机组成汇编语言(机器语言)的执行过程量子计算机cpu的基础组成存储器的层次结构缓存cpu的乱序执行合并写UMA与NUMA操作系统基本知识启动什么是操作系统内核分类用户态与内核态进程 线程 纤程的基本概念(面试高频)纤程纤程的实现纤程的应用场景内核线程进程的创建和启动僵尸进程孤儿进程进程调度中断与系统调用(软中断)从汇编角度理解软中断内存管理内存管理的发展历程ZGCCPU如何区分一个立即数 和 一条指令内核同步机制

qq_37892401的博客 1549

数字电路与逻辑设计实验二 组合逻辑电路

1.组合逻辑电路功能测试 (1)用 2 片 74LS00 组成图 2.1 所示逻辑电路。为便于接线和检查,在图中要注明 芯片编号及各引脚对应的编号。 (2)图中 A、B、C 接逻辑开关,Y1,Y2 接发光管电平显示。 (3)按表 2.3 要求,改变 A、B、C 输入的状态,填表并写出 Y1,Y2 的逻辑表达式。 (4)将运算结果与实验结果进行比较。 2.测试用异或门(74LS86)和与非门组成的半加器的逻辑功能。 根据半加器的逻辑表达式可知,半加 器 Y 是 A、B 的异或,而进位 Z 是 A

欢迎交流学习心得 1280

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

从程序员角度深入计算机系统的底层 计算机的引导过程

前一篇文章介绍了Intel计算机的主板与内存映射,从而为本文设定了一个系统引导阶段的场景。引导(Booting)是一个复杂的,充满技巧的,涉及多个阶段,又十分有趣的过程。下图列出了此过程的概要: 引导过程概要 当你按下计算机的电源键后(现在别按!),机器就开始运转了。一旦主板上电,它就会初始化自身的固件(firmware)——芯片组和其他零零碎碎的东

414

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Java需要掌握的底层知识_Java程序员应该掌握的底层知识

CPU的制作过程CPU是如果制作的CPU的原理汇编语言的执行过程汇编语言的本质:机器语言的助记符 其实就是机器语言执行过程 :计算机通电->cpu读取内存中的程序(电信号输入)->时钟发生器不断震荡通电->推动cpu内部一步一步执行(执行多少取决于指令需要的时钟周期)->计算机完成->写回(信号)->写给显卡输出(图形)计算机的组成CPU的组成PC ->...

weixin_29098391的博客 248

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

作为现在程序员必备的Linux内核(让你十分钟快速了解)

前言:在程序员成长过程中,相对于硬技能的提升,软技能往往被忽略,但要往更高处走,软技能启着至关重要的作用。作为从业十多年的大龄程序中的角度,解读那些有利于个人成长的点,连点成线,连线成面,伴你向上生长。 Linux内核代码的庞大令不少人“望而却步”,究其原因可以总结为以下几点: 1、认为内核源码“高不可攀”。其实内核源码分析的难度不在于源码本身,而在于如何使用更合适的分析代码的方式和手段。内核的庞大致使我们不能按照分析一般的demo程序那样从主函数开始按部就班的分析,我们需要一种从中间介入的手段对内.

Shen_Linux的博客 766

从程序员角度深入计算机系统的底层 内存地址转换与分段

本文是Intel兼容计算机(x86)的内存与保护系列文章的第一篇,延续了启动引导系列文章的主题,进一步分析操作系统内核的工作流程。与以前一样,我将引用Linux内核的源代码,但对Windows只给出示例(抱歉,我忽略了BSD,Mac等系统,但大部分的讨论对它们一样适用)。文中如果有错误,请不吝赐教。在支持Intel的主板芯片组上,CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在...

www.v5qq.com的技术博客 184

古法编程:程序员必备的底层原理与系统思维修炼指南

在软件开发领域,理解计算机系统原理是构建高性能、高可靠性应用的基础。从内存管理、CPU缓存到操作系统进程调度,这些底层机制决定了程序的执行效率与资源利用。掌握这些核心原理,不仅能提升深度调试与性能优化的能力,更能帮助开发者正确理解并高效运用现代抽象技术,如容器化与并发框架。在数据库查询优化、内存泄漏排查等常见工程实践中,系统思维能引导开发者从现象直抵本质。本文聚焦于古法编程的价值,探讨如何通过C语言实践、操作系统核心概念学习及工具链掌握,系统性地构建可迁移的解决问题能力,从而在快速迭代的技术浪潮中保持核心竞

weixin_33908217的博客 350

Linux内核的概念

Linux内核概念

qq_65529772的博客 155

从一个ELF程序的加载窥探操作系统内核-(1)

从一个ELF程序的加载窥探操作系统内核-(1) 操作系统加载一个ELF程序看似一个EASY的动作,其实下面隐藏了很多很多OS内核的关键实现,让我们一起来解密其中的流程 作者是一个micro kernel的开发者,在设计动态链接器的时候,在此留下一些笔记,重点参考了以下资料文献 《程序员的自我修养》 《深入理解计算机系统》 《现代操作系统-原理与实现》 《深入理解LINUX内核》 《设计模式/JAVA》 一个ELF程序的构成与运行 这里的ELF程序指的是可执行类型的文件,动态库和静态库也是ELF

GoolyOh的专栏 1161

Linux内核(1)——结构

Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。 Linux最早是由芬兰黑客 Linus Torvalds为尝试在英特尔x86架构上提供自由免费的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。内核结构 操作系统是一个用来和硬件打交道并为用户程序提供

prophet10086的博客 248
上一篇: 从程序员角度深入计算机系统的底层 计算机的引导过程
下一篇: 从程序员角度深入计算机系统的底层 内存地址转换与分段
zxycode007
博客等级 码龄18年 53粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值