当前位置:首页 > CN2资讯 > 正文内容

linux kernel 架构 linux kernel build

1天前CN2资讯

Linux Kernel Build System

Linux的一个惊人的地方就是仅仅使用同一个代码库就可以应用在无数的计算系统,从超级计算机到嵌入式设备。它可能是目前唯一一个使用同一代码库的操作系统软件。像微软和苹果,他们针对桌面和移动平台都有各自不同的内核(windowns NT/windows CE 和OS X/IOS)。Linux能够使用统一代码库主要归因于两点,第一是丰富的抽象层和间接层,第二点是扩展性极强的编译系统。

 

Linux 内核是一体化架构,这就意味着内核代码运行在内核空间并且共享相同的地址空间。正因如此,你应该编译内核的时候就要确定所需要的features。严格的说,Linux也不完全是一体化架构,这是因为Linux允许在运行时插入模块来进行扩展。内核必须包含模块使用到的所有内核符号才能载入模块。如果内核在编译时不包含这些符号,那么模块会因为缺失依赖而无法载入。模块是一种推迟编译(或执行)某个内核feture的一种手段。一旦模块被载入到内核,它就成为了一体化架构内核的一部分,共享内核运行的地址空间。即使Linux支持模块插入的功能,但是你还是最好在编译内核的时候就确认好需要编译进内核的特性。

 

正因如此, 可以选择哪些代码编译进内核尤为重要。在Linux中使用选择性编译来实现该功能。用来选择Linux特性的配置选项非常多,他们最终会决定内核编译会包含哪些C文件,代码或者数据结构。

 

所以,一种简单高效的编译管理应运而生,它就是内核编译系统,即Kbuild。

Kbuild包含4个主要组成部分:

  • Config symbols:配置符号就是编译选项,用来选择源码中需要编译的代码,并且确定编译进内核的对象。
  • Kconfig files:用来定义Config symbols的文件,其中包含类型、描述、依赖等属性。menuconfig可以读取Kconfig生成图形化配置菜单。
  • .config file:保存config symbols所选值的文件,你可以手动进行修改,或者使用menuconfig来进行修改更新。
  • Makefiles:Makefile表述各个源码之间的关系, 主要用于生成目标,也就是编译内核。

Compilation Options:Configuration Symbols

配置符号决定了哪些特性哪些模块会包含在最终的内核镜像内。kbuild提供两种配置符号,分别是布尔和三态。他们的区别仅仅只是可选值的数量不同,但是这样的区别比看起来更重要。布尔符号可以配置为true和false,两者选其一。三态符号可以选择三种类型值,yes,no,或者module。

 

不是所有代码都能被编译成模块。许多特性非常重要且关键以至于必须在内核编译时就需要确定是否包含。比如我们无法在一个运行的内核插入多对称处理器功能或者内核抢占功能。所以我们需要用布尔配置符号来明确这些特性。大多数的特性是可以被滞后编译为模块,当然也可以在内核编译时增加。所以三态符号就是用来决定你编译的特性是内建(y),模块(m)或者都不是(n)。

 

除了布尔和三态,还有很多其他的配置符号,比如字符串和十六进制。但是由于他们并不是用来选择编译,所以这里就不多述。参考Linux Kernel文档了解更多。

 

Defining Configuration Symbols:Kconfig Files

配置符号被定义在名为Kconfig的文件中。每个Kconfig文件能描述任意多的配置选项,并且可以引用其他Kconfig文件。make menuconfig 指令读取所有的Kconfig文件,来构造图形化的内核配置菜单。在内核源码树中的每个目录都会有一个Kconfig文件,这个Kconfig文件中会包含子所有子目录下的Kconfig。内核源码树根目录的Kconfig就是配置选项的树根。menuconfig(scripts/kconfig/mconf),gconfig(scripts/kconfig/gconf)以及其他的配置编译脚本都会从根目录的Kconfig开始,递归读取所有的Kconfig文件,以此建立配置菜单。哪些目录编译会用到都定义在Kconfig中,同时也要根据用户配置选项来确定。

 

Storing Symbol Values:.config File

所有配置选项的值都保存在名为.config的文件中。每次想要改变内核编译配置,你可以执行make menuconfig指令。它会读取Kconfig文件来创建菜单,并且根据.config文件的定义来更新配置选项。另外这个工具也会按照你设置的新配置来更新.config文件。当然如果.config文件不存在,它也会新创建一个。

 

由于.config文件本身就是文本文件,你也可以直接修改它而不需要使用特殊工具。.config文件的保存和恢复也就相当简单,只需要复制粘贴即可。

 

Compiling the Kernel:Makefiles

Kbuild中的最后一个组件是Makefile文件。它是用来编译内核镜像和模块的。同Kconfig文件一样,每一个子目录都有一个Makefile文件,用来编译当前目录下的源文件。整个编译过程是递归的,根目录的Makefile文件会调用并执行子目录的Makefile,各个子目录的源代码都被编译成二进制对象。所有这些二进制对象被链接生成最终的内核镜像文件。

 

Sample: Adding Coin driver

Linux内核的kbuild系统已经基本了解,现在我们通过向内核增加一个设备驱动来举个例子。这个例子是一个简单的字符串驱动设备,名叫coin。他的功能是模拟硬币的翻转,并反馈正面(head)或者反面(tail)的其中一个值。这个驱动还提供一个可选特性,就是通过一个虚拟文件可以读取之前硬币翻转的统计结果。

以下代码展示coin设备的使用方法。

1 root@localhost:~# cat /dev/coin 2 tail 3 root@localhost:~# cat /dev/coin 4 head 5 root@sauron:/# cat /sys/kernel/debug/coin/stats 6 head=6 tail=4

向Linux内核增加特性需要三步:

1.首先需要将源码保存在正确目录下,比如将wifi设备保存到drivers/net/wireless

2.然后修改源码所在目录的Kconfig文件,添加新增特性的配置选项

3.最后修改源码所在目录的Makefile文件,添加需要编译的目标对象

 

coin驱动是字符设备驱动,所以应把源码coin.c保存到drivers/char

 

下一步是编写用户进行coin驱动的编译配置选项。我们需要在driver/char/Kconfig文件添加2个配置符号。一个符号用来选择是否将驱动加入到内核,另一个符号决定是否提供统计功能。

 

和大多数驱动一样,coin驱动可以直接编译进内核,或者编译成一个模块,或者什么都不是。所以第一个配置符号名为COIN,是一个三态符号(y/n/m)。第二个符号名为COIN_STAT用来决定是否提供统计信息,显然这是个二值决定,所以就是布尔符号(y/n)。如果你并没有选择coin驱动,也就是COIN为n,那么COIN_STAT的配置就没有任何意义。这是内核中常见的概念,比如,如果你没有先使能块设备功能,那么基于块设备的文件系统(ext3 或者 fat32)就无法添加到内核。显然,在配置符号之间存在依赖关系。在Kconfig文件中,我们可以使用depends关键词来实现配置符号之间的依赖关系。当我们使用make menuconfig构建编译配置菜单时,那些依赖没有满足的配置项都会被隐藏起来。depends是Kconfig文件中众多关键词的其中之一。想要完成的Kconfig语法介绍,可以参考内核源码树中的kbuild/kconfig-languate.txt

以下代码片段展示drivers/char/Kconfig文件中添加的的coin驱动部分

# # Character device configuration # menu "Character devices" config COIN tristate "Coin char device support" help Say Y here if you want to add support for the coin char device. If unsure, say N. To compile this driver as a module, choose M here: the module will called coin. config COIN_STAT bool "flipping statistics" depends on COIN help Say Y here if you want to enable statistics about the coin char device.

 

我们要如何查看刚刚添加的配置选项呢?

 

正如之前所提到的,使用make menuconfig来构建一个图形化的配置菜单,然后我们就可以选择哪些编译进内核,哪些编译成模块。

 

命令行工具scripts/kconfig/mconf 执行会读取所有Kconfig文件来构造图形化配置菜单接口。我们通过这个工具来更新COIN 和COIN_STAT的编译选项。下图展示了图形化的配置菜单界面。

 

一旦你结束了编译配置并准备退出菜单,如果你修改了不同的配置,那么配置工具会询问是否要保存新的配置。保存的话就会把配置选项更新并保存到.config文件中。针对每个配置项,固定的前缀CONFIG_被添加。比如,一个布尔配置项如果被选择上,那么在.config文件中会被保存如下

CONFIG_COIN_STA==y

 

如果不选择上,那么保存如下

# CONFIG_COIN_STAT is not set

 

三态的配置项yes或者no的保存结果和布尔的一样,但是,请记住三态的另外一种配置是将特性编译为模块。举个例子,如果你选择将COIN驱动配置为模块,在.config文件中的结果如下

CONFIG_COIN=m

 

下面的片段展示coin驱动的配置情况之一,coin被编译为模块,使能翻转统计功能

CONFIG_COIN=m CONFIG_COIN_STAT=y

 

如果我们想直接编译进内核并且屏蔽翻转统计功能,我们会看到如下配置

CONFIG_COIN=y #CONFIG_COIN_STAT is not set

 

一旦我们得到配置好的.config文件,我们就可以开始编译了。当我们执行编译目标进行内核编译时,首先kbuild会执行一个二进制软件读取所有Kconfig文件和.config文件,如下所示

scripts/kconfig/conf Kconfig

 

这个二进制软件会更新(或者创建)一个C头文件,这个头文件包含我们之前设置的所有配置项和它的值。这个头文件保存在include/generated/autoconf.h。每个GCC编译指令都包含了这个头文件,所以在内核源码中可以引用这些配置符号。

 

这个头文件是由几千个#define的宏定义组成,这些宏定义描述每一个配置选项和对应的值。我们来看看具体如何定义的。

 

为TRUE的布尔类型配置和为Yes的三态类型配置是一样的,举个例子,布尔类型的CONFIG_COIN_STAT为TRUE和三态类型的CONFIG_COIN为Yes的时候,头文件的结果如下

#define CONFIG_COIN_STAT 1 #define CONFIG_COIN 1

 

为FALSE的布尔类型配置和为No的三态类型配置是一样的,举个例子

#define CONFIG_COIN_STAT 0 #define CONFIG_COIN 0

 

对于三态类型,如果COIN被配置为模块,则在头文件中保存如下

#define CONFIG_COIN_MODULE 1

 

第三也是最后一步需要更新对应源码所在目录的Makefile文件,然后kbuild就能编译你选择的驱动。

 

但是,我们要如何命令kbuild选择性编译我们的源码呢?

 

内核编译系统包含两个主要任务:创建内核二进制镜像和内核模块。为了实现这两个主要任务,makefile需要维护两个列表对象:obj-y和obj-m。前一个列表对象是用来内建内核镜像,后者是用来编译为模块。

 

.config文件中的配置符号和autoconf.h头文件中的宏定义被用来扩展需要编译的对象列表。Kbuild会递归进入每一个目录,然后编译各个目录下makefile所列举的对象。更多makefile的用法和信息,请参考Document/kbuild/makefiles.txt.

 

对于coin驱动,我们只需要在drivers/char/Makefile中添加如下语句。

obj-$(CONFIG_COIN) += coin.o

 

这条语句会告诉kbuild去创建一个coin.c源码的编译对象,并且把它加入到对象列表。coin.o对象会被增加到obj-y还是obj-m还是要取决于CONFIG_COIN的值。如果CONFIG_COIN配置符号没有定义,那么coin.o根本就不会被编译。

 

好了, 现在我们知道如何选择性编译源文件了。接下来就要知道如何选择性编译代码段。这其实很简单,只需要使用autoconf.h头文件中的宏定义即可。

下面代码展示完整coin字符设备驱动

1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/fs.h> 4 #include <linux/uaccess.h> 5 #include <linux/device.h> 6 #include <linux/random.h> 7 #include <linux/debugfs.h> 8 9 #define DEVNAME "coin" 10 #define LEN 20 11 enum values {HEAD, TAIL}; 12 13 struct dentry *dir, *file; 14 int file_value; 15 int stats[2] = {0, 0}; 16 char *msg[2] = {"head\n", "tail\n"}; 17 18 static int major; 19 static struct class *class_coin; 20 static struct device *dev_coin; 21 22 static ssize_t r_coin(struct file *f, char __user *b, 23 size_t cnt, loff_t *lf) 24 { 25 char *ret; 26 u32 value = random32() % 2; 27 ret = msg[value]; 28 stats[value]++; 29 return simple_read_from_buffer(b, cnt, 30 lf, ret, 31 strlen(ret)); 32 } 33 34 static struct file_operations fops = { .read = r_coin }; 35 36 #ifdef CONFIG_COIN_STAT 37 static ssize_t r_stat(struct file *f, char __user *b, 38 size_t cnt, loff_t *lf) 39 { 40 char buf[LEN]; 41 snprintf(buf, LEN, "head=%d tail=%d\n", 42 stats[HEAD], stats[TAIL]); 43 return simple_read_from_buffer(b, cnt, 44 lf, buf, 45 strlen(buf)); 46 } 47 48 static struct file_operations fstat = { .read = r_stat }; 49 #endif 50 51 int init_module(void) 52 { 53 void *ptr_err; 54 major = register_chrdev(0, DEVNAME, &fops); 55 if (major < 0) 56 return major; 57 58 class_coin = class_create(THIS_MODULE, 59 DEVNAME); 60 if (IS_ERR(class_coin)) { 61 ptr_err = class_coin; 62 goto err_class; 63 } 64 65 dev_coin = device_create(class_coin, NULL, 66 MKDEV(major, 0), 67 NULL, DEVNAME); 68 if (IS_ERR(dev_coin)) 69 goto err_dev; 70 71 #ifdef CONFIG_COIN_STAT 72 dir = debugfs_create_dir("coin", NULL); 73 file = debugfs_create_file("stats", 0644, 74 dir, &file_value, 75 &fstat); 76 #endif 77 78 return 0; 79 err_dev: 80 ptr_err = class_coin; 81 class_destroy(class_coin); 82 err_class: 83 unregister_chrdev(major, DEVNAME); 84 return PTR_ERR(ptr_err); 85 } 86 87 void cleanup_module(void) 88 { 89 #ifdef CONFIG_COIN_STAT 90 debugfs_remove(file); 91 debugfs_remove(dir); 92 #endif 93 94 device_destroy(class_coin, MKDEV(major, 0)); 95 class_destroy(class_coin); 96 return unregister_chrdev(major, DEVNAME); 97 }

 

Conclusion

Linux, despite being a monolithic kernel, is highly modular and customizable. You can use the same kernel in a varied range of devices from high-performance clusters to desktops all the way to mobile phones. This makes the kernel a very big and complex piece of software. But, even when the kernel has millions of lines of code, its build system allows you to extend it with new features easily. In the past, to have access to an operating system's source code, you had to work for a big company and sign large NDA agreements. Nowadays, the source of probably the most modern operating system is publicly available. You can use it, study its internals and modify it in any creative way you want. The best part is that you even can share your work and get feedback from an active community. Happy hacking!

 

 

 

Articles by Javier Martinez Canillas

Link:Kbuild: the Linux Kernel Build System | Linux Journal

Translated by Joey

 



    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/32348.html

    分享给朋友:

    “linux kernel 架构 linux kernel build” 的相关文章

    樱花VPS:高性价比的日本虚拟专用服务器推荐

    樱花VPS是一个由日本知名主机提供商Sakura Internet Inc.推出的虚拟专用服务器产品。在选择VPS时,用户常常关注服务的稳定性、速度和价格等方面,而樱花VPS正是在这些核心领域表现出色,吸引了一大批用户的关注。 首先,樱花VPS以其高性价比闻名。作为国内前三强的VPS提供商。其规模化...

    使用newcom598优惠码注册域名,享受超值价格

    什么是newcom598优惠码? 我想和大家分享一个超值的优惠信息,那就是newcom598优惠码。这是一个专为Namecheap的新用户设计的优惠码,意在帮助刚开始建立自己在线业务的人以超优惠的价格注册.COM域名。通过这个优惠码,新用户可以以仅$5.98的价格获得首年的.COM域名,这样算下来在...

    RackNerd虚拟主机评测:高性价比的VPS解决方案及优质支持

    RackNerd概述 在我接触虚拟主机服务的过程中,RackNerd总是令我印象深刻。这是一家美国公司,自2012年成立以来,它便专注于提供多种虚拟主机服务,包括KVM VPS、Hybrid Dedicated Servers与独立服务器租用等。对于许多需要高性价比服务的用户而言,RackNerd无...

    KVM是什么?深入了解KVM的定义、工作原理及应用场景

    KVM的定义与概念 谈到KVM时,首先想到的就是“键盘、显示器和鼠标”的组合,它让我们可以用一组设备控制多台计算机。这种技术非常适合在数据中心或需要远程管理的环境中使用。我对这个系统产生了兴趣,因为它显著提高了管理效率,节省了空间,还所有的操作都变得更简便。想象一下,如果有多台服务器,你需要同时监控...

    HostYun:高性价比VPS服务的理想选择

    HostYun,最早被称作主机分享,成立于2008年,专注于提供性价比极高的VPS服务。在众多IDC品牌中,HostYun凭借其低价策略迅速占领了一席之地。作为一个以KVM和XEN虚拟化技术为基础的平台,HostYun不仅满足了用户对低成本服务的需求,也为学习、测试和小型项目的部署提供了理想的选择。...

    买域名的全流程攻略:从选择到安全管理

    购买域名是一项非常重要的投资,尤其对于那些希望在网上建立品牌或个人网站的人来说。域名不仅仅是一个网址,它是你在线身份的象征。因此,了解如何选择和购买域名显得极为关键。 选择域名时,需要考虑多个因素。首先,域名应该简洁易记,便于用户输入和分享。其次,关键字的相关性也非常重要,因为一个包含行业相关关键词...