2、内核基本学习

一、Linux内核源代码的目录结构学习

    Linux 内核的目录,变化是比较的小,以Linux 2.6 的版本进行分析。

1

    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 文件就组成了:

image

     可以,通过  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 驱动,添加的目录如下:

2

    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 生效。

    因此,全新的生成的文件目录结果是:

3

三、内核的编码风格

    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.
可见,将上面的错误的代码风格都找了出来。
原文地址:https://www.cnblogs.com/qxj511/p/5452398.html