作者:shengxin11 | 来源:互联网 | 2023-10-12 16:54
Linux-内核-学习笔记(11):内核配置和编译原理 一、内核简介 1. 操作系统 1.1、什么是操作系统? 操作系统本质上是一个程序,由很多个源文件构成,需要编译连接成操作系统程序。它主要作用就是管理计算机硬件,给应用程序提供一个运行环境 。例如linux、windows、android、ucos就是操作系统。
1.2、操作系统核心功能 操作系统就相当于你的一个管家 ,是一些功能的综合体,他会管理下面这些内容,帮助用户解决一些复杂的分配和调用问题。
内存管理 :如果没有操作系统,内存是需要程序自己来管理的。譬如在uboot中要使用内存时是自己随便用的,自己定义内存的位置和大小,没有注册也没有限制。这时候如果程序自己不小心把同一块内存重复用了就会出现程序逻辑错误(覆盖)。随着系统变大了之后(内存多了),内存管理会非常麻烦。但是有了操作系统之后,操作系统负责管控所有的内存,所有的应用程序需要使用内存时都要向操作系统去申请和注册,由操作系统的内存管理模块来分配内存给你使用 ,这样好处是可以保证内存使用不会冲突。进程调度 :操作系统下支持多个应用程序同时运行(所以可以一边聊QQ一边看电影···),这是宏观上的并行。实际上在单核心CPU上微观上是不能并行的,宏观上的并行就是操作系统提供的分时复用机制,也就是某个时间切换到一个进程下执行,执行完成后再切换到另外一个进程。操作系统的进程调度模块负责在各个进程之间进行切换 。硬件设备管理 :没有操作系统时要控制任何硬件都要自己写代码,有了操作系统后操作系统本身会去控制各个硬件,应用程序就不用考虑硬件的具体细节了。操作系统的硬件设备管理模块就是驱动模块 。文件系统 :文件系统是管理存储设备的一种方式。存储设备是由很多个扇区组成的,每个扇区有512/1024/2048/4096(各种)字节,存储设备要以扇区为单位进行读写。如果没有文件系统,程序要自己去读写扇区,就得记得哪个文件在哪个扇区。有了文件系统之后用户不用再关注扇区,只用关注文件系统中的目录和文件名,而不用管这个文件在物理磁盘的哪个扇区。 同时,操作系统还有一些扩展功能 :(1)协议栈。(2)有用的应用程序包。应用程序本身不属于操作系统内核的一部分,应用程序是给人用的,面向某种功能的。譬如ping程序用来测试网络是否联通,ifconfig程序用来配置网卡。
1.3、内核和发行版区别 区别1:内核是操作系统内核的简称 ,内核负责实现操作系统的核心功能 (资源管理模块,譬如内存管理、调度系统······),内核 不包括应用程序 。所以说只有内核用户是没法用的(就像只有windows系统一样),因为人做任何事情都是通过相应的应用程序来完成的(需要给windows装软件)。所以卖操作系统的人把内核和一些常用的应用程序打包在一起 提供给普通用户,这就是操作系统的发行版(也就是普通意义上的操作系统)。所以 发行版 包含内核和应用程序 。 区别2:内核 只有一个 ,只是版本不同。下载linux内核链接。发行版 有很多 。譬如ubuntu、redhat、suse、centos······都是使用一个内核做出来的,只是应用程序不同。
二、内核的框架 在了解内核时,首先要知道上述这种分层结构:应用层、操作系统层和硬件层 。三者之间由于任务不同,主要操作的对象不同,实现了各层的着重点不同,但是三者之间又紧密联系着。层次之间实现下层被上层所调用 ,即硬件被OS所调用,而OS是给APP用来使用的。 操作系统管理着硬件层,它将硬件抽象成一个个的API,应用层在想要控制某个硬件设备时,通过操作系统层的API对硬件设备进行调用和控制,从而完成三个层次之间的协调配合。
1. 内核和驱动的关联 (1)驱动其实属于内核的一部分。驱动就是内核中的硬件设备管理模块 。 (2)在内核态 运行是管理员权限,而在用户态 则是用户权限,是有一部分限制的。驱动工作在内核态 。这里将工作状态划分的目的主要是为了在内核态时运行一些重要的程序,使其不受权限的束缚。所以驱动程序故障可能导致整个内核崩溃,驱动程序漏洞会使内核不安全。
2. 内核和应用程序的关联 (1)应用程序不属于内核,而是在内核之上的。 (2)应用程序工作在用户态 ,是受限制的。每一个应用程序就是一个进程 ,系统在工作时有许多个应用程序和一个内核 ,但是同一时间CPU只能服务一个。应用程序如果想得到内存需要向CPU去申请(malloc和free),内核的目的是服务应用程序 ,所以应用程序才是最终要实现的目标(重点是要生成能工作的APP),而操作系统只是一个手段(windows系统只是用来工作的)。 (3)应用程序故障不会导致内核崩溃,并且不会影响到其他的进程。因为它们是相互独立的进程 ,你的区域坏了不会影响我的区域。 (4)应用程序通过内核定义的API接口来调用内核工作 。应用层执行应用需要调用硬件,所以需要通过内核来调用驱动从而操作硬件执行相应的动作,也可以理解为内核就是为应用程序提供底层资源管理的服务员 。
3. 内核和根文件系统的关联 (1)根文件系统提供根目录 。整个文件系统是一个树形的。从根目录出发的方式就是绝对路径,相对路径的原理也是通过转成绝对路径去找的。反斜杠“/”就是根目录,也就是最起始的目录。我们常说的挂载mount就是一个连接点,如果说将XX挂载到XX,就是将这个东西的位置指定到某个特定的路径下。 (2)进程1存放在根文件系统中,是用户态和内核态的一个交界处 。是整个系统中的第一个应用程序。 (2)内核启动最后会去装载根文件系统 。刚开始内核启动时没有目录的,整个操作系统的根目录就是从根文件系统中来的。因此根文件系统为操作系统启动提供了很多必备的资源:根目录和进程1 。
4. linux内核的模块化设计 因为linux内核很庞大,代码量很大、东西很多,如果设计时完全设计成一体(各个文件、各个函数之间紧耦合),复杂度超出了人所能理解的范围。所以模块化设计也是一种必要。模块化设计就是内核中各个功能模块在代码上是彼此独立的 ,譬如说调度系统和内存管理系统之间并没有全局变量的互相引用,甚至函数互相调用也很少,就算有也是遵循一个接口规范的。模块化设计的目的就是实现功能模块的松耦合 。
配置时可裁剪 。linux内核在编译之前可以进行配置,配置时可以选择将组成内核的成千上万个模块每一个要或者不要。要了之后还有更多的一些细节的配置。模块化编译和安装 。为了操作方便,逐渐从静态的升级变成了动态的升级 (不需要重启系统,更不需要重新烧录系统)。这种动态的升级也是由模块化来支持的。源码中使用条件编译 。这种在uboot中已经见过了。通过宏开关 来控制编译时候哪个编译哪个不编译,从而影响究竟使用了哪个模块。
这种模块化设计的优点是:(1)功能可裁剪、灵活性;(2)可扩展性(动态安装卸载、新硬件支持);(3)利于协作。
三、内核配置和编译原理 1. 内核源码目录分析 在分析内核源码目录之前,首先要将内核目录解压开,并将kernel目录打包一份发送到windows端方便查看: 接近着分析kernel内核源码目录树的各个目录代表的意思: (1)arch :arch是architecture的缩写,意思是架构。arch目录下是好多个不同架构的CPU的子目录,譬如arm这种cpu的所有文件都在arch/arm目录下,X86的CPU的所有文件都在arch/x86目录下。 (2)block :英文是块的意思,在linux中block表示块设备(以块(多个字节组成的整体,类似于扇区)为单位来整体访问),譬如说SD卡、iNand、Nand、硬盘等都是块设备。你几乎可以认为块设备就是存储设备。block目录下放的是一些linux存储体系中关于块设备管理的代码。 (3)crypto :英文意思是加密。这个目录下放了一些各种常见的加密算法的C语言代码实现。譬如crc32、md5、sha1等。 (4)Documentation :里面放了一些文档。 (5)drivers :驱动目录,里面分门别类的列出了linux内核支持的所有硬件设备的驱动源代码。 (6)firmware :固件。固件其实是软件,不过这个软件是固话到IC里面运行的叫固件。就像S5PV210里的iROM代码。 (7)fs :fs就是file system,文件系统,里面列出了linux支持的各种文件系统的实现。 (8)include :头文件目录,公共的(各种CPU架构共用的)头文件都在这里。每种CPU架构特有的一些头文件在arch/arm/include目录及其子目录下。 (9)init :init是初始化的意思,这个目录下的代码就是linux内核启动时初始化内核的代码。 (10)ipc :ipc就是inter process commuication,进程间通信,里面都是linux支持的IPC的代码实现。 (11)kernel :kernel就是内核,就是linux内核,所以这个文件夹下放的就是内核本身需要的一些代码文件。 (12)lib :lib是库的意思,这里面都是一些公用的有用的库函数,注意这里的库函数和C语言的库函数不一样的。在内核编程中是不能用C语言标准库函数,这里的lib目录下的库函数就是用来替代那些标准库函数的。譬如在内核中要把字符串转成数字用atoi,但是内核编程中只能用lib目录下的atoi函数,不能用标准C语言库中的atoi。譬如在内核中要打印信息时不能用printf,而要用printk,这个printk就是我们这个lib目录下的。 (13)mm :mm是memory management,内存管理,linux的内存管理代码都在这里。 (14)net :该目录下是网络相关的代码,譬如TCP/IP协议栈等都在这里。 (15)scripts :脚本,这个目录下全部是脚本文件,这些脚本文件不是linux内核工作时使用的,而是用来辅助对linux内核进行配置编译生产的。 (16)security :安全相关的代码。不用去管。 (17)sound :音频处理相关的。 (18)tools :linux中用到的一些有用工具。 (19)usr :目录下是initramfs相关的,和linux内核的启动有关,暂时不用去管。 (20)virt :内核虚拟机相关的,暂时不用管。 (21)Kbuild :Kbuild是kernel build的意思,就是内核编译的意思。这个文件就是linux内核特有的内核编译体系需要用到的文件。 (22)Makefile :这个是linux内核的总makefile,整个内核工程用这个Makefile来管理的。 (23)mk :是九鼎在移植时自己添加的,不是linux内核本身的东西。九鼎添加这个文件的作用是用这个文件来整天管理kernel目录的配置和编译,也就是说这个文件有点类似于我们之前移植uboot时自己创建的那个cp.sh。
2. 内核配置第一步(make x210ii_qt_defconfig) 2.1、方法演示 在进行make之前,先要检查Makefile中的交叉编译工具是否正确,并确定ARCH=arm以保证编译时能找到arch/arm目录。
2.2、原理分析 配置第一步的主要目的是为了得到 .config 文件。这个文件是一个隐藏文件,是linux内核在编译过程中很重要的一个文件,其作用类似与uboot中的include/configs/x210_sd.h,内核在编译过程中会读取.config中的配置项,并且用这些配置项去指导整个编译链接过程 。 .config文件的格式类似于脚本文件,其中的每一行都是一个配置项,这些配置项被配置的值最终用来决定内核编译时将哪些内容以什么方式来编译 。
从.config文件的规模可以看出linux内核的可配置项有两三千个。所以linux内核是高度可配置 的,而且linux内核的所有配置项很难全部搞明白。因为linux内核的配置项太多太繁杂超出了人的大脑能够记忆和处理的数量级,因此linux内核不像uboot那样直接手工配置,而是发明了一个图形化的配置工具menuconfig 。
3. 内核配置第二步(make menuconfig) 3.1、方法演示 在make menuconfig之前,要确保ncurses安装。可以使用apt-get install libncurses5-dev 。
3.2、原理分析 配置第二步的主要目的是为了通过menuconfig 来配置.config文件中的内容。.config中的大部分内容通过make x210ii_qt_defconfig 进行了默认的配置,剩下一些细节的内容或者我们想要添加的配置内容都要通过make menuconfig 进行添加。
make xxx_defconfig这一步其实是参考别人已经做好的,这样做有很多好处:减少很多工作量,避开了很多自己不懂的配置项(譬如对内存管理的、调度系统的等模块的配置项),我们只用管自己需要管的。因此make x210ii_qt_defconfig其实相当于:cp arch/arm/configs/x210ii_qt_defconfig .config ,这两个其实是一个文件,只是复制出来。
make menuconfig其实就是读取第一步得到的.config,然后给我们一个图形化的界面 ,让我们可以更加容易的找到自己想要修改的配置项,然后更改配置他。
menuconfig方法详解见第四章:menuconfig原理及使用方法。
4. 内核编译(make) 当执行make时,即内核根据配置好的内容进行编译。我们通过make x210ii_qt_defconfig 对.config文件进行了第一步的配置,也就是完成了大部分默认的配置工作,然后再利用make menuconfig 对.config文件进行一些细微的更改,最终得到的一个完整的.config文件。这个.config文件中记录了我们设定的配置内容(指导内核编译什么不编译什么,以什么方式编译等),然后.config文件会根据内部的内容信息去影响Makefile文件,利用Makefile文件的条件判断来决定编译不编译哪些模块,最终影响启动后的内核。
四、menuconfig原理及使用方法 1. 原理分析 ncurses库是linux中用来实现文字式的图形界面,linux内核中使用了ncurses库 来提供menuconfig。scripts\kconfig\lxdialog目录下的一些c文件就是用来提供menuconfig的那些程序源代码。
我们说过make menuconfig其实就是读取.config文件后并通过图像化方式展示给我们 。,这个图形化界面中间的选择区中有很多个选择项,每个选择项对应.config文件中的一个配置项 ,每一个选择项都可以被选择和配置操作,选择区中的某些项还包含子目录的。选择区太短放不下所有的一个目录层级的选项,可以用箭头按键的向上箭头和向下箭头来上翻和下翻。
那menuconfig是如何显示菜单的目录结构呢? 是由内核源码树各个目录下的Kconfig 文件来支持的。Kconfig文件中按照一定的格式包含了一个又一个的配置项,每一个配置项在make menuconfig中都会成为一个菜单项目。而且menuconfig中显示的菜单目录结构和源码目录中的Kconfig的目录结构是一样的 。(如果在相应的Kconfig文件中删除一个config项,则再次make menuconfig时这个项目已经看不到了)
内核源码目录树中每一个Kconfig都会source 引入其所有子目录下的Kconfig ,从而保证了所有的Kconfig项目都被包含进menuconfig中。这个也告诉我们:如果你自己在linux内核中添加了一个文件夹,一定要在这个文件夹下创建一个Kconfig文件,然后在这个文件夹的上一层目录的Kconfig中source引入这个文件夹下的Kconfig文件。
通过menuconfig设置的配置方式保存到哪了呢? menuconfig或者config后面空格隔开的大写字母表示的类似于 DM9000 的就是这个配置项的配置项名字,这个字符串前面添加CONFIG_后就构成了.config中的配置项名字。 刚才已经知道menuconfig的菜单内容来自于Kconfig文件,但是每一个菜单的选择结果(Y、N、M)却不是保存在Kconfig文件中的。Kconfig文件是不变的,Kconfig文件只是决定有没有这个菜单项 ,并不管这个菜单项的选择结果。 menuconfig工作时在我们make menuconfig打开时,他会读取.config文件,并且用.config文件中的配置选择结果来初始化menuconfig中各个菜单项的选择值。因此我们改变menuconfig的配置方式之后,会自动保存到.config文件中 。
总结:菜单项的项目内容从Kconfig文件来,菜单项的选择值从.config文件来 。
2. 使用方法 箭头按键导航整个菜单,回车按键选择子菜单(注意选项后面有 —>的选项才是有子菜单的,没有这个标识的没有子菜单),高亮的字母是热键(快捷键),键盘按键Y、N、M三个按键的作用分别是将选中模块编入、去除、模块化。双击ESC表示退出,按下?按键可以显示帮助信息,按下/按键可以输入搜索内容来全局搜索信息(类似于vi中的搜索),[]不可以模块化,<>的才可以模块化 。
inux内核中一个功能模块有三种编译方法:一种是编入 、一种去去除 、一种是模块化 。所谓编入 就是将这个模块的代码直接编译连接到zImage中 去,去除 就是将这个模块不编译链接到zImage中 ,模块化 是将这个模块仍然编译,但是不会将其链接到zImage中,会将这个模块单独链接成一个内核模块.ko文件 ,将来linux系统内核启动起来后可以动态的加载或卸载 这个模块。在menuconfig中选项前面的括号里,*表示编入,空白表示去除,M表示模块化 。 (1)tristate 意思是三态(3种状态,对应Y、N、M三种选择方式),bool 是要么真要么假(对应Y和N)。所以tristate的意思就是这个配置项可以被三种选择,bool的意思是这个配置项只能被2种选择。
(2)depends 中文意思是“取决于”或者“依赖于”,所以depends在这里的意思是:本配置项依赖于另一个配置项。如果那个依赖的配置项为Y或者M,则本配置项才有意义;如果依赖的哪个配置项本身被设置为N,则本配置项根本没有意义。 depends并不要求依赖的配置项一定是一个,可以是多个,而且还可以有逻辑运算。这种时候只要依赖项目运算式子的裸机结果为真则依赖就成立。
(3)select 表示当该配置被选中时,对应的哪些配置也会被选中。
(4)help 帮助信息,告诉我们这个配置项的含义,以及如何去配置他。