집 >운영 및 유지보수 >리눅스 운영 및 유지 관리 >Linux 커널 구성 시스템은 여러 부분으로 구성됩니다.
리눅스 커널 구성 시스템은 3가지 부분으로 구성됩니다. 1. Linux 커널 소스 코드 루트 디렉터리와 각 계층 디렉터리에 배포되며 Linux 커널의 컴파일 규칙을 정의하는 Makefile 2. 구성 파일(config.in) 3. 구성 명령 해석기(구성 스크립트에 사용되는 구성 명령을 해석함) 및 구성 사용자 인터페이스를 포함한 구성 도구.
이 튜토리얼의 운영 환경: linux7.3 시스템, Dell G3 컴퓨터.
Linux 커널 구성 시스템은 Makefile, 구성 파일(config.in) 및 구성 도구의 세 부분으로 구성됩니다.
Makefile: Linux 커널 소스 코드 루트 디렉터리와 각 계층 디렉터리에 배포되며 Linux 커널의 컴파일 규칙을 정의합니다.
구성 파일(config.in): 사용자에게 구성을 만드는 기능을 제공합니다.
구성 도구: 구성 명령 해석기(구성 스크립트에 사용되는 구성 명령 해석) 및 구성 사용자 인터페이스(문자 기반 인터페이스, Ncurses 기반 그래픽 인터페이스 및 Xwindows 기반 그래픽 인터페이스 사용자 구성 인터페이스 제공) 포함 각각은 Make config, Make menuconfig 및 make xconfig에 해당합니다.
이러한 구성 도구는 Tcl/TK, Perl과 같은 스크립트 언어를 사용하여 작성되었습니다(C로 작성된 일부 코드도 포함). 이 글은 구성 시스템 자체를 분석한 것이 아니라 구성 시스템을 사용하는 방법에 대한 소개입니다. 따라서 구성 시스템의 관리자가 아닌 이상 일반 커널 개발자는 해당 원칙을 이해할 필요가 없으며 Makefile 및 구성 파일을 작성하는 방법만 알면 됩니다. 따라서 이 기사에서는 Makefile과 구성 파일에 대해서만 설명합니다. 또한 특정 CPU 아키텍처와 관련된 내용의 경우 ARM을 예로 들어 설명하겠습니다. 이는 논의된 문제를 명확하게 할 뿐만 아니라 내용 자체에는 영향을 미치지 않습니다.
Makefile 개요
Makefile의 기능은 구성에 따라 컴파일해야 할 소스 파일 목록을 구성한 다음 별도로 컴파일하고 대상 코드를 함께 링크하는 것입니다. 궁극적으로 Linux 커널 바이너리 문서를 형성합니다.
리눅스 커널 소스코드는 트리 구조로 구성되어 있기 때문에 Makefile도 디렉토리 트리에 배포됩니다. Linux 커널의 Makefile 및 Makefile과 직접 관련된 파일은 다음과 같습니다.
Makefile: 최상위 Makefile은 전체 커널 구성 및 컴파일을 위한 전체 제어 파일입니다.
.config: 사용자가 선택한 구성 옵션이 포함된 커널 구성 파일로, 커널 구성 결과(예: make config)를 저장하는 데 사용됩니다.
arch/*/Makefile: Arch/arm/Makefile과 같은 다양한 CPU 시스템 디렉터리에 있는 Makefile은 특정 플랫폼용 Makefile입니다.
drivers/Makefile 등 각 하위 디렉터리의 Makefile은 하위 디렉터리의 소스 코드 관리를 담당합니다.
Rules.make: 모든 Makefile에서 사용되는 규칙 파일입니다.
사용자가 make config를 통해 구성한 후 .config가 생성됩니다. 최상위 Makefile은 .config의 구성 선택 사항을 읽습니다. 최상위 Makefile에는 vmlinux 파일 생성과 커널 모듈 생성이라는 두 가지 주요 작업이 있습니다. 이 목표를 달성하기 위해 최상위 Makefile은 커널의 각 하위 디렉터리에 재귀적으로 들어가고 이러한 하위 디렉터리에 있는 Makefile을 각각 호출합니다. 어떤 하위 디렉터리에 들어갈지는 커널 구성에 따라 다릅니다. 최상위 Makefile에는 다음과 같은 문장이 있습니다. 특정 CPU 아키텍처의 Makefile을 포함하는 archive/$(ARCH)/Makefile 이 Makefile에는 플랫폼 관련 정보가 포함되어 있습니다.
각 하위 디렉터리에 위치한 Makefile도 .config에서 제공하는 구성 정보를 기반으로 현재 구성에 필요한 소스 파일 목록을 구성하며 파일 끝에 $(TOPDIR)/Rules.make를 포함합니다.
Rules.make 파일은 매우 중요한 역할을 하며 모든 Makefile에 공통적인 컴파일 규칙을 정의합니다. 예를 들어, 이 디렉터리의 모든 C 프로그램을 어셈블리 코드로 컴파일해야 하는 경우 Makefile에 다음 컴파일 규칙이 있어야 합니다.
%.s: %.c
(CC) (CFLAGS) -S 374fb0688f1c98421fbfc33272e11137@
동일한 요구사항을 가진 하위 디렉터리가 많으므로 추가해야 합니다. 각 Makefile에는 이 컴파일 규칙이 포함되어 있어 더 문제가 됩니다. Linux 커널에서는 이러한 컴파일 규칙이 Rules.make로 통합되고 해당 Makefile의 Rules.make(Rules.make 포함)에 포함되어 여러 Makefile 규칙에서 동일한 규칙이 중복되는 것을 방지합니다. 위의 예에서 Rules.make의 해당 규칙은 다음과 같습니다.
%.s: %.c
(CC) (CFLAGS) (EXTRACFLAGS) (CFLAGS_ (∗F)) (CFLAGS_ @)−S < -o $@
Makefile中的变量
顶层 Makefile 定义并向环境中输出了许多变量,为各个子目录下的 Makefile 传递一些信息。有些变量,比如 SUBDIRS,不仅在顶层 Makefile 中定义并且赋初值,而且在 arch/*/Makefile 还作了扩充。
常用的变量有以下几类:
1) 版本信息
版本信息有:VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSION,KERNELRELEASE。 版本信息定义了当前内核的版本,比如 VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它们共同构成内核的发行版本KERNELRELEASE:2.4.18-rmk7
2) CPU 体系结构:ARCH
在顶层 Makefile 的开头,用 ARCH 定义目标 CPU 的体系结构,比如 ARCH:=arm 等。许多子目录的 Makefile 中,要根据 ARCH 的定义选择编译源文件的列表。
3) 路径信息:TOPDIR, SUBDIRS
TOPDIR 定义了 Linux 内核源代码所在的根目录。例如,各个子目录下的 Makefile 通过 $(TOPDIR)/Rules.make 就可以找到 Rules.make 的位置。
SUBDIRS 定义了一个目录列表,在编译内核或模块时,顶层 Makefile 就是根据 SUBDIRS 来决定进入哪些子目录。SUBDIRS 的值取决于内核的配置,在顶层 Makefile 中 SUBDIRS 赋值为 kernel drivers mm fs net ipc lib;根据内核的配置情况,在 arch/*/Makefile 中扩充了 SUBDIRS 的值,参见4)中的例子。
4) 内核组成信息:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
Linux 内核文件 vmlinux 是由以下规则产生的:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ --start-group \ $(CORE_FILES) \ $(DRIVERS) \ $(NETWORKS) \ $(LIBS) \ --end-group \ -o vmlinux
可以看出,vmlinux 是由 HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS 和 LIBS 组成的。这些变量(如 HEAD)都是用来定义连接生成 vmlinux 的目标文件和库文件列表。其中,HEAD在arch/*/Makefile 中定义,用来确定被最先链接进 vmlinux 的文件列表。比如,对于 ARM 系列的 CPU,HEAD 定义为:
HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ arch/arm/kernel/init_task.o
表明 head-$(PROCESSOR).o 和 init_task.o 需要最先被链接到 vmlinux 中。PROCESSOR 为 armv 或 armo,取决于目标 CPU。 CORE_FILES,NETWORK,DRIVERS 和 LIBS 在顶层 Makefile 中定义,并且由 arch/*/Makefile 根据需要进行扩充。 CORE_FILES 对应着内核的核心文件,有 kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,这些是组成内核最为重要的文件。同时,arch/arm/Makefile 对 CORE_FILES 进行了扩充:
# arch/arm/Makefile # If we have a machine-specific directory, then include it in the build. MACHDIR := arch/arm/mach-$(MACHINE) ifeq ($(MACHDIR),$(wildcard $(MACHDIR))) SUBDIRS += $(MACHDIR) CORE_FILES := $(MACHDIR)/$(MACHINE).o $(CORE_FILES) endif HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ arch/arm/kernel/init_task.o SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.a $(LIBS)
5) 编译信息:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
在 Rules.make 中定义的是编译的通用规则,具体到特定的场合,需要明确给出编译环境,编译环境就是在以上的变量中定义的。针对交叉编译的要求,定义了 CROSS_COMPILE。比如:
CROSS_COMPILE = arm-linux- CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld
CROSS_COMPILE 定义了交叉编译器前缀 arm-linux-,表明所有的交叉编译工具都是以 arm-linux- 开头的,所以在各个交叉编译器工具之前,都加入了 $(CROSS_COMPILE),以组成一个完整的交叉编译工具文件名,比如 arm-linux-gcc。
CFLAGS 定义了传递给 C 编译器的参数。
LINKFLAGS 是链接生成 vmlinux 时,由链接器使用的参数。LINKFLAGS 在 arm/*/Makefile 中定义,比如:
# arch/arm/Makefile LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds
Rules.make变量
前面讲过,Rules.make 是编译规则文件,所有的 Makefile 中都会包括 Rules.make。Rules.make 文件定义了许多变量,最为重要是那些编译、链接列表变量。
O_OBJS,L_OBJS,OX_OBJS,LX_OBJS:本目录下需要编译进 Linux 内核 vmlinux 的目标文件列表,其中 OX_OBJS 和 LX_OBJS 中的 “X” 表明目标文件使用了 EXPORT_SYMBOL 输出符号。
M_OBJS,MX_OBJS:本目录下需要被编译成可装载模块的目标文件列表。同样,MX_OBJS 中的 “X” 表明目标文件使用了 EXPORT_SYMBOL 输出符号。
O_TARGET,L_TARGET:每个子目录下都有一个 O_TARGET 或 L_TARGET,Rules.make 首先从源代码编译生成 O_OBJS 和 OX_OBJS 中所有的目标文件,然后使用 $(LD) -r 把它们链接成一个 O_TARGET 或 L_TARGET。O_TARGET 以 .o 结尾,而 L_TARGET 以 .a 结尾。
子目录Makefile
目录 Makefile 用来控制本级目录以下源代码的编译规则。我们通过一个例子来讲解子目录 Makefile 的组成:
# Makefile for the linux kernel. # # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. export-objs := tc.o # Object file lists. obj-y := obj-m := obj-n := obj- := obj-$(CONFIG_TC) += tc.o obj-$(CONFIG_ZS) += zs.o obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.o # Files that are both resident and modular: remove from modular. obj-m := $(filter-out $(obj-y), $(obj-m)) # Translate to Rules.make lists. L_TARGET := tc.a L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) include $(TOPDIR)/Rules.make
a) 注释
对 Makefile 的说明和解释,由#开始。
b) 编译目标定义
类似于 obj-(CONFIGTC)+=tc.o的语句是用来定义编译的目标,是子目录Makefile中最重要的部分。编译目标定义那些在本子目录下,需要编译到Linux内核中的目标文件列表。为了只在用户选择了此功能后才编译,所有的目标定义都融合了对配置变量的判断。前面说过,每个配置变量取值范围是:y,n,m和空,obj−(CONFIG_TC) 分别对应着 obj-y,obj-n,obj-m,obj-。如果 CONFIG_TC 配置为 y,那么 tc.o 就进入了 obj-y 列表。obj-y 为包含到 Linux 内核 vmlinux 中的目标文件列表;obj-m 为编译成模块的目标文件列表;obj-n 和 obj- 中的文件列表被忽略。配置系统就根据这些列表的属性进行编译和链接。
export-objs 中的目标文件都使用了 EXPORT_SYMBOL() 定义了公共的符号,以便可装载模块使用。在 tc.c 文件的最后部分,有 “EXPORT_SYMBOL(search_tc_card);”,表明 tc.o 有符号输出。
这里需要指出的是,对于编译目标的定义,存在着两种格式,分别是老式定义和新式定义。老式定义就是前面 Rules.make 使用的那些变量,新式定义就是 obj-y,obj-m,obj-n 和 obj-。Linux 内核推荐使用新式定义,不过由于 Rules.make 不理解新式定义,需要在 Makefile 中的适配段将其转换成老式定义。
c) 适配段
适配段的作用是将新式定义转换成老式定义。在上面的例子中,适配段就是将 obj-y 和 obj-m 转换成 Rules.make 能够理解的 L_TARGET,L_OBJS,LX_OBJS,M_OBJS,MX_OBJS。
L_OBJS := (sort(filter-out (export−objs),(obj-y))) 定义了 L_OBJS 的生成方式:在 obj-y 的列表中过滤掉 export-objs(tc.o),然后排序并去除重复的文件名。这里使用到了 GNU Make 的一些特殊功能,具体的含义可参考 Make 的文档(info make)。
d) include $(TOPDIR)/Rules.make
配置文件功能概述
除了 Makefile 的编写,另外一个重要的工作就是把新功能加入到 Linux 的配置选项中,提供此项功能的说明,让用户有机会选择此项功能。所有的这些都需要在 config.in 文件中用配置语言来编写配置脚本,
在 Linux 内核中,配置命令有多种方式:
配置命令 | 解释脚本 |
---|---|
Make Config,make oldconfig | scripts/Configure |
Make menuconfig | scripts/Menuconfig |
Make xconfig | scripts/tkparse |
以字符界面配置(make config)为例,顶层 Makefile 调用 scripts/Configure, 按照 arch/arm/config.in 来进行配置。命令执行完后产生文件 .config,其中保存着配置信息。下一次再做 make config 将产生新的 .config 文件,原 .config 被改名为 .config.old
对于一个开发者来说,将自己开发的内核代码加入到 Linux 内核中,需要有三个步骤。首先确定把自己开发代码放入到内核的位置;其次,把自己开发的功能增加到 Linux 内核的配置选项中,使用户能够选择此功能;最后,构建子目录 Makefile,根据用户的选择,将相应的代码编译到最终生成的 Linux 内核中去。下面,我们就通过一个简单的例子–test driver,结合前面学到的知识,来说明如何向 Linux 内核中增加新的功能。
目录结构
test driver 放置在 drivers/test/ 目录下:
cddrivers/testtree
.
|– Config.in
|– Makefile
|– cpu
| |– Makefile
| -- cpu.c <br> |-- test.c <br> |-- test_client.c <br> |-- test_ioctl.c <br> |-- test_proc.c <br> |-- test_queue.c <br>
– test
|– Makefile
配置文件
# TEST driver configuration # mainmenu_option next_comment comment 'TEST Driver' bool 'TEST support' CONFIG_TEST if [ "$CONFIG_TEST" = "y" ]; then tristate 'TEST user-space interface' CONFIG_TEST_USER bool 'TEST CPU ' CONFIG_TEST_CPU fi endmenu
由于 test driver 对于内核来说是新的功能,所以首先创建一个菜单 TEST Driver。然后,显示 “TEST support”,等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y),则进一步显示子功能:用户接口与 CPU 功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了 tristate(因为 tristate 的取值范围包括 y、n 和 m,m 就是对应着模块)。
2) arch/arm/config.in
在文件的最后加入:source drivers/test/Config.in,将 TEST Driver 子功能的配置纳入到 Linux 内核的配置中。
Makefile
1)drivers/test/Makefile
# drivers/test/Makefile # # Makefile for the TEST. # SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) cpu L_TARGET := test.a export-objs := test.o test_client.o obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o obj-$(CONFIG_TEST_USER) += test_ioctl.o obj-$(CONFIG_PROC_FS) += test_proc.o subdir-$(CONFIG_TEST_CPU) += cpu include $(TOPDIR)/Rules.make clean: for dir in $(ALL_SUB_DIRS); do make -C $$dir clean; done rm -f *.[oa] .*.flags
drivers/test 目录下最终生成的目标文件是 test.a。在 test.c 和 test-client.c 中使用了 EXPORT_SYMBOL 输出符号,所以 test.o 和 test-client.o 位于 export-objs 列表中。然后,根据用户的选择(具体来说,就是配置变量的取值),构建各自对应的 obj-* 列表。由于 TEST Driver 中包一个子目录 cpu,当 CONFIG_TEST_CPU=y(即用户选择了此功能)时,需要将 cpu 目录加入到 subdir-y 列表中。
2)drivers/test/cpu/Makefile
# drivers/test/test/Makefile # # Makefile for the TEST CPU # SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) L_TARGET := test_cpu.a obj-$(CONFIG_test_CPU) += cpu.o include $(TOPDIR)/Rules.make clean: rm -f *.[oa] .*.flags
3)drivers/Makefile
…… subdir-$(CONFIG_TEST) += test …… include $(TOPDIR)/Rules.make
在 drivers/Makefile 中加入 subdir-$(CONFIG_TEST)+= test,使得在用户选择 TEST Driver 功能后,内核编译时能够进入 test 目录。
4)Makefile
…… DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o DRIVERS-$(CONFIG_TEST) += drivers/test/test.a DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a DRIVERS := $(DRIVERS-y) ……
在顶层 Makefile 中加入 DRIVERS-(CONFIGTEST)+=drivers/test/test.a和DRIVERS−(CONFIGTEST)+=drivers/test/test.a。如何用户选择了 TEST Driver,那么 CONFIG_TEST 和 CONFIG_TEST_CPU 都是 y,test.a 和 test_cpu.a 就都位于 DRIVERS-y 列表中,然后又被放置在 DRIVERS 列表中。在前面曾经提到过,Linux 内核文件 vmlinux 的组成中包括 DRIVERS,所以 test.a 和 test_cpu.a 最终可被链接到 vmlinux 中。
相关推荐:《Linux视频教程》
위 내용은 Linux 커널 구성 시스템은 여러 부분으로 구성됩니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!