2、内核基本学习
一、Linux内核源代码的目录结构学习
Linux 内核的目录,变化是比较的小,以Linux 2.6 的版本进行分析。
arch : 架构体系相关的代码,不同平台都在 arch 里面有相应的一个目录,比如 arm、powerpc、mips。在 arch 目录,存放的是各个平台以及各个平台的芯片对 Linux 内核进程调度、内存管理、中断等代码。
block : 块设备驱动程序的 I/O 调度、
crypto : 一些加密实现算法
document : 内核各个部分帮助的文档,比如内核编写代码的风格、等等。
fs : 文件系统。部分 NTFS、JFFS2
include : 头文件,与系统习惯的头文件,是被放在 include/linux 里面
init : 内核的初始化代码
ipc : 进程通信的代码
kernel : 内核最核心的部分,进程调度、定时器,等等,与平台相关的代码则在 arch/xxx/kernel 里面
lib : 库文件
mm : 内存管理代码,与平台相关的代码 在 arch/xxx/mm 目录里面
net : 网络相关的代码
scripts : 脚本,内核 执行 make menuconfig 的时候,这些脚本就会被运行,去查找 目录里面的 kconfig 文件,
security : 安全的模块
sound : ALSA 音频驱动核心代码,以及常用取用核心代码。
usr : 实现打包、压缩 等
二、内核的编译以及加载
2.1、Linux内核的编译
编译内核的时候,一般是需要对内核进行配置,一般的指令有:
make config // 基于文本,最古董级别的方式 make menuconfig // 文本菜单,还有图形界面 make xconfig // 基于 QT make gconfig // 基于 GTK所以, xconfig 和 gconfig 是依赖 QT 或者 GTK 实现的,所以一般是使用 make menocinfig
当执行 make menuconfig 之后,内核的脚本(scripts) 就会去内核的目录,根获取Kconfig 文件 里面的配置信息, 多个层级目录的 kconfig 文件就组成了:
可以,通过 space 去选择,是否编译进内核,还是编译为模块、更或者是不编译。
当执行完 make menuconfig 的时候,就会在顶层生成 .config(隐藏文件),它 是整个 Linux 所有的配置信息(记录了那些驱动要编译进内核,还是模块),
编译的: make zImage ,的时候,顶层的 MakeFile 就会更具 .config 记录的信息,去编译。 注意到 Linux 顶层 还有 Kbuild 文件,因为 Linux 是借助 Kbuild 完成这个系统的编译,这个知识点以后学习。
2.1.1、Kconfig 学习
Kconfig 文件,记录了make menuconfig 时候,用来配置内核,当前目录下的配置信息,就全记载在当前的 Kconfig 文件里面,以后自己添加驱动的时候,就要修改这一些。
Kconfig 学习: 可以参考内核提供的参考资料,“Documentation/kbuild/kconfig-language.txt”,
(1)基本菜单的实现
menu "S3C2440 Machines" config SMDK2440_CPU2440 bool "SMDK2440 with S3C2440 CPU module" depends on ARCH_S3C2440 select XXXXXX default y if ARCH_S3C2440 select CPU_S3C2440 config MACH_MINI2440 bool "MINI2440 development board" select CPU_S3C2440 select EEPROM_AT24 select LEDS_TRIGGER_BACKLIGHT select SND_S3C24XX_SOC_S3C24XX_UDA134X help Say Y here to select support for the MINI2440. Is a 10cm x 10cm board available via various sources. It can come with a 3.5" or 7" touch LCD. endmenu
menn xxxxxxxx endmenu : 实现菜单定制的功能,make menuconfig 显示的菜单,就是使用者这样的命令实现的
config SMDK2440_CPU2440 : config 是关键字,表示一个配置选项,其实是默认缺少了 CONFIG_, 全文的配置是 CONFIG_SMDK2440_CPU2440,
bool "SMDK2440 with S3C2440 CPU module" :
bool ,是类型,一般是 Y 和 N,
tristate变量的值:y、n和m
string变量的值: 字符串
也就是实现将当前的模块(SMDK2440 with S3C2440 CPU module) 编译进内核,还是以模块的形式存在,"SMDK2440 with S3C2440 CPU module" 表示 配置界面显示的信息(显示的菜单),可以在当前行 按住 空格键,进行选取,其实也是可以通过 prompt 实现 :
bool prompt “SMDK2440 with S3C2440 CPU module”depends on ARCH_S3C2440 : depends on 是关键字,也就是 当前的配置,是依赖于 ARCH_S3C2440 ,只有当 ARCH_S3C2440 被选中的时候,当前才会显示配置信息。
default y if ARCH_S3C2440 : default 和 if 是关键字,也就是默认的状况下是 “y”,也就是选中,编译进内核;同时,需要 ARCH_S3C2440 被选中,其实这个 if 条件,也是可以没有的。
select CPU_S3C2440 : select ,反依赖,也就是当当前的配置信息被选上的时候,那么这个反依赖 CPU_S3C2440 ,也会被选上,
help : 关键字,帮助信息,
(2)依赖菜单的实现
依赖菜单的实现,是如果后面的菜单依赖于前面的菜单的话,那么后面的就是前面菜单的子菜单,如果 父菜单设置为 y 的话,那么后续的子菜单就都是可见的;当父菜单是 n 的时候,,子菜单就是被隐藏。
config parent-caidan bool “ enable load XXX” config child-caidan depends on parent-caidan只有当 parent-caidan 被选上的时候,child-caidan 菜单选项才会被显示出来,以供选择。
(3)choice 条目
以多个条目的方式显示出来,以供用户的选择。
choice xxxxx xxxx xxxx endchoice
例子:
choice prompt “choice list” config aaaaaa bool prompt “xxxxxxxxx” help “xxxxx” config bbbbb bool prompt “xxxxx” help “xxxxxx” endchoice
当 光标选中 “choice list” 菜单之后,enter 之后,就会出现 aaa 和 bbb 的两个菜单,以供选择使用。
(4)引入下级目录的 Kconfig
source ” /绝对路径/Kconfig“这样就引入了新的 Kconfig文件
2.1.2、MakeFile 学习
主要介绍了子目录下的kbuild MakeFile进行基本的介绍,因为子目录的 MakeFile 和kconfig 才需要程序要去改动。
(1)单个目标编译
将指定的目标文件编译进去内核,或者是模块的形式,文件是 test.c
obj-y += test.o obj-m +=test.o
(2)多文件模块
一个模块 qqq,由多个文件,比如 test1.c、test2.c、test3、c
obj-$(CONFIG_QQQ) += qqq.o qqq-y := test1.o test2.o test3.o
编译模块 QQQ,是在 Kconfig 里面被配置过的话,当配置菜单设置为 y 的时候,那么,就会将 qqq.o 编译进内核,而模块包含了 test 三个文件,所以会江北test0.o、test1.o、test2.o v 编译进去 qqq 模块。
(3)目录层次的问题
当 MakeFile 文件需要编译下层的文件的时候,那么
obj-$(CONFIG_QQQ) += qqq/当 CONFIG_QQQ 在菜单被设置为 y 或者 m 的时候,kbuild 就会把 qqq 目录列入到向下迭代的目标当中,也就是后续会继续进行 MakeFile。
2.1.3、自己添加驱动代码目录和子目录
假如要遭 /driver 目录为 arm 添加 test 驱动,添加的目录如下:
test 目录是在 /driver 目录下,添加新的目录和子目录,因此都需要添加 MakeFile和 Kconfig 文件。而 test 目录下新增的了 MakeFile 和 Kconfig 了,它的父目录必须做一些修改,使得后续添加的 Kconfig 和 MakeFile 能被引用。
(1)新增 test 目录下的 Kconfig:
menu "TEST DRIVER" config TEST bool "test support" defatly n config TEST_FOR_USER bool "test for user interface" depends on CONFIG_TEST endmenu
创建 TEST_DRIVER 菜单,菜单里面有 test support 和 test for user interface 两个菜单选项,等待用户的选择,当 test support 被选为 y 的时候,test for user interface 才会显示出来。
(2)修改 arch/arm/Kconfig
为了使得这个 Kconfig 能起作用,就要 修改 arch/arm/Kconfig 文件,对 arch/arm/Kconfig 增加:
source "drivers/test/Kconfig"
也就是说,这个 source 使用新的 Kconfig 可以被引用。这样,自己定义的 Kconfig 文件,正式生效。、
(3)test 目录下增加 MakeFile 文件
test 目录下的 MakeFile文件,实现对当前目录文件的指导编译,和下一个目录下的文件编译:
# /driver/test/Makefile obj-$(CONFIG_TEST) += test.o test_client.o test_queue.o obj-$(CONFIG_TEST_USER) += test_ioctl.o obj-$(CONFIG_TEST_CPU) += cpu/当在菜单选中 CONFIG_TEST 为 y 或者 为 m 的时候,就会编译后面指定的文件,后面的也是一样。因为砸他 test 目录下,是还有 CPU 子目录的,我们设定的时候,这个是为 arch/arm 下设定的平台驱动,所以假定 CONFIG_TEST_CPU 在其他地方被设置之后,就会编译 CPU 这个子目录。
(4)cpu 子目录也是需要添加 MakeFile 文件
只需要添加 MakeFile 指导编译文件就可以,不用添加 KCconfig 文件,因为这个选项是假定 arm 下面的结构CONFIG_TEST_CPU 被选中之后,就会跳转到 CPU 目录下进行编译;而不是通过 menuconfig 界面,指定编译。
MakeFile 文件如下,
# /driver/test/CPU/Makefile obj-$(CONFIG_TEST_CPU) += cpu.o
(5)test 的父母的 MakeFile 添加脚本
test 下添加了 MakeFile文件,问了使这个MakeFile 文件生效,所以需要在 test 的父目录,也就是 driver 目录下的 MakeFile 文件,添加脚本。
# /driver/MakeFile obj-$(CONFIG_TEST) += test/
通过在父母路添加的这句脚本,使得父目录的 MakeFile 遍历 MakeFile 的时候,就会走进 test 进行编译,使得 test MakeFile 生效。
因此,全新的生成的文件目录结果是:
三、内核的编码风格
Linux 的编码风格 ,和一般的 window 风格不一样,这些风格是在 document /CodingStyle 文件里面,有比较详细的描述。
3.1、命名风格的差异
在window 中,一般是这样方式命名宏、变量和函数
#define PI 3.1415926 int minValue, maxValue; void SendData();
宏代码上,使用全部大写的方式;而变量,第一个单词是全部小写,后面单词的第一个字母是大写;函数则所有的单词第一个字母,都是大写。
在 Linux 中,则是这样命名:
#define PI 3.1415926 int min_value, max_value; int send_data();可见,在宏命令是是一样的,都是大写。但是在变量和函数命名上,下划线是大行其道,而且,也不再用字母的大写。
3.2、括号
缩进使用的是 TAB ,而 {} 使用的原则如下,
(1)对于结构体、if、for、while、switch,{ 不另起一行,比如
struct var_data { int data; char data[10]; } if (a == b){ a = c; d = a; } // 当 if 后面之后一行的时候,就不要爱括号了 for (i = 0; i < 10; i++ ){ XXXX; XXXX } // for 西面只有一行代码的时候,就不要括号了显然,在 if、for、while、switch 后面,是马上接的空格。
同时,else 是不另起一行的,
if (){ XXX XXX } else { XXXXX }但是,函数的括号,一直都是另起一行的,
int add(int a, int b) { XXX XXX }
(2)switch 与 case 对于
switch (*buf) { case 0 : XXXX; break; case 1; xxxx brea; default : break; }
(3)代码的检查
为符合Linux 内核对编码的风格,内核的 scripts/checkpatch.pl 提供了检查代码风格的脚本,
chmod a+x checkpatch.pl ./checkpatch.pl --no-tree -file /路径/1.c
这里需要注意两个命令,一个是 –no-tree 和 - file,
-file : 指定文件, --no-tree : 修改路径,因为 checkpatch.pl 默认都是从顶层的目录开始,当使用这个命令的时候,就可以从任意的目录,进行代码风格的校验
所以,针对下面代码进行校验:
for(i = 0; i< 10; i++) { i++; i++; } 执行命令: ./checkpatch.pl --no-tree -file /work/nfs_root/1.c 结果为: #12: FILE: 1.c:12: + for(i = 0; i< 10; i++) + { ERROR: spaces required around that '<' (ctx:VxW) #12: FILE: 1.c:12: + for(i = 0; i< 10; i++) ^ ERROR: space required before the open parenthesis '(' #12: FILE: 1.c:12: + for(i = 0; i< 10; i++) total: 3 errors, 0 warnings, 20 lines checked 1.c has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.
可见,将上面的错误的代码风格都找了出来。