kernel初体验

来源:互联网 发布:阿里云怎么收费标准 编辑:程序博客网 时间:2024/05/22 09:44

以下内容主要源于朱有鹏《物联网大讲堂》课程的学习,已取得朱老师本人同意。若本文还引用了一些博文的内容,将在引用处已注明出处,如有侵权,请告知删除。

本文学习所使用的kernel为2.6.35.7版本的内核。源码来自九鼎的X210V3S开发板的配套源码,qt_x210v3s_160307.tar.bz2。
使用tar -jxvf qt_x210v3s_160307.tar.bz2解压后进入kernel目录。

分析kernel的源码目录

kernel的源码目录

分析源码目录下的单个文件

  1. Kbuild,Kbuild是kernel build的意思,就是内核编译的意思。这个文件就是linux内核特有的内核编译体系需要用到的文件。
  2. Makefile,这个是linux内核的总makefile,整个内核工程用这个Makefile来管理的。
  3. mk,是九鼎在移植时自己添加的,不是linux内核本身的东西。九鼎添加这个文件的作用是用这个文件来整天管理kernel目录的配置和编译,也就是说这个文件有点类似于我们之前移植uboot时自己创建的那个cp.sh。
  4. COPYING 和COPYING.txt,版权声明。
  5. CREDITS,感谢。
  6. MAINTAINERS,List of maintainers and how to submit kernel changes
  7. REPORTING-BUGS,BUG反馈途径。

linux内核源码目录结构

(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,file system,文件系统。
里面列出了linux支持的各种文件系统的实现。

(8)include,头文件目录。
公共的(各种CPU架构共用的)头文件都在这里。每种CPU架构特有的一些头文件在arch/arm/include目录及其子目录下。

(9)init,是初始化的意思。
这个目录下的代码就是linux内核启动时初始化内核的代码。

(10)ipc,inter proccess commuication,进程间通信。
里面都是linux支持的IPC的代码实现。

(11)kernel,就是linux内核。
这个文件夹下放的就是内核本身需要的一些代码文件。

(12)lib,库。
这里面都是一些公用的有用的库函数,注意这里的库函数和C语言的库函数不一样的。在内核编程中是不能用C语言标准库函数,这里的lib目录下的库函数就是用来替代那些标准库函数的。
譬如在内核中要把字符串转成数字用atoi,但是内核编程中只能用lib目录下的atoi函数,不能用标准C语言库中的atoi。譬如在内核中要打印信息时不能用printf,而要用printk,这个printk就是我们这个lib目录下的。

(13)mm,memory management,内存管理。
linux的内存管理代码都在这里。

(14)net。
该目录下是网络相关的代码,譬如TCP/IP协议栈等都在这里。

(15)scripts,脚本。
这个目录下全部是脚本文件,这些脚本文件不是linux内核工作时使用的,而是用来辅助对linux内核进行配置编译生产的。我们并不会详细进入分析这个目录下的脚本,重点学会配置和编译linux内核即可。

(16)security。安全相关的代码。

(17)sound。音频处理相关的。

(18)tools。linux中用到的一些有用工具

(19)usr。目录下是initramfs相关的,和linux内核的启动有关。

(20)virt。内核虚拟机相关的,暂时不用管。

简单讲一下linux内核的配置体系。

linux内核很庞大,里面模块很多,而且可配置性非常高。所以linux源代码的配置是一个很复杂的事情,必须要有一套很复杂的机制来保证linux内核可以被正确的配置。(对比一下uboot,uboot的配置项都是在xxx.h中,用宏定义来表示的。uboot的这种方式很依赖于人的水平,因为uboot的配置体系很简单。)
linux内核本身配置项有上千个,光靠人眼睛去看脑袋去记根本不可能,所以内核发明了一种体系用来帮助人进行简单化的配置。这种体系就是我们本课程中重点要研究的东西。
Kbuild、Kconfig等文件,都是和内核的配置体系有关的。

内核配置和编译体验

先确认Makefile

(1)主要是检查交叉编译工具链有没有设置对。CROSS_COMPILE ?= /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
(2)确认ARCH = arm。主要目的是为了编译时能找到arch/arm目录。

当我们make distclean后(也就是说默认情况下)是没有.config文件的,我们配置的两步过程就是为了得到内容合适的.config文件

1.make x210ii_qt_defconfig

.config
作用:生成.config文件。
.config文件是linux内核在编译过程中一个很重要的文件,其作用类似uboot中的include/configs/x210_sd.h,内核在编译过程中会读取.config中的配置项,并且用这些配置项去指导整个编译链接过程。

.config文件的格式类似于脚本文件,其中内容为类似于于:CONFIG_ARM=y的一个一个的配置项。这些配置项就类似于脚本文件中定义的一个一个变量,所以这一行可以被理解为定义了一个变量CONFIG_ARM,这个变量的值为y。

.config文件中每一行都是一个配置项,从.config文件的规模可以看出linux内核的可配置项有两三千个。所以linux内核是高度可配置的,而且linux内核的所有配置项很难全部搞明白。因为linux内核的配置项太多太繁杂超出了人的大脑能够记忆和处理的数量级,因此linux内核不像uboot那样直接手工配置,而是发明了一个图形化的配置工具menuconfig。

最后只要出现:configuration written to .config这句话,就证明我们的操作是正确的。如果没有出现这句话,就有错误。

可能出现的错误1:名字敲错了。名字是字符串匹配的,一定要正确。
注意:如果这一步配置没有得到.config文件,是不能进行下一步的。实际测试时没有.config也可以make menuconfig,但是这样做出来的内核编译和烧写运行应该是有问题的。

2.make menuconfig

make xxx_defconfig解决的问题是大部分的配置项(这一步结束后99%的配置项就已经正确了),下来就是对个别不同的针对我们的开发板进行细节调整,细节调整就通过make menuconfig来完成。

make xxx_defconfig这一步其实是参考别人已经做好的,这样做有很多好处:减少很多工作量,避开了很多自己不懂的配置项(譬如对内存管理的、调度系统的等模块的配置项),我们只用管自己需要管的。

make menuconfig其实就是读取第一步得到的.config,然后给我们一个图形化的界面,让我们可以更加容易的找到自己想要修改的配置项,然后更改配置。

make x210ii_qt_defconfig其实相当于:cp arch/arm/configs/x210ii_qt_defconfig .config

(1)可能出现的错误1:ncurses库没装

错误信息: * Unable to find the ncurses libraries or the * required
header files. * ‘make menuconfig’ requires the ncurses libraries.
* * Install ncurses (ncurses-devel) and try again.

解决方案: apt-get install libncurses5-dev
(参考了:http://blog.csdn.net/yao_qinwei/article/details/8805101)

(2)可能出现的错误2:屏幕太小

错误信息: Your display is too small to run Menuconfig! It must be at least 19 lines by 80 columns.

解决方案:全屏,或者是把字体调小。

总结:make menuconfig是第二步配置,具体的用法和配置意义在后面课程讲。我们这里因为是九鼎已经移植过的,所以第二步配置是可以不做的,直接退出即可。
用键盘的向右方向键移动到EXIT,按回车退出。

3.make

编译完成后得到的内核镜像不在源码树的根目录下,在arch/arm/boot这个目录下。得到的镜像名是zImage

可能出现的错误1:莫名其妙的错误,可以试试先make distclean
可能出现的错误2:代码本身的错误,具体问题具体分析。

menuconfig的工作原理与使用

linux为了实现图形化界面的配置,专门提供了一套配置工具menuconfig。

ncurses库是linux中用来实现文字式的图形界面,linux内核中使用了ncurses库来提供menuconfig

scripts\kconfig\lxdialog目录下的一些c文件就是用来提供menuconfig的那些程序源代码。

菜单项的项目内容从Kconfig文件来,菜单项的选择值从.config文件来。

menuconfig

linux内核中一个功能模块有三种编译方法:一种是编入、一种去去除、一种是模块化。所谓编入就是将这个模块的代码直接编译连接到zImage中去,去除就是将这个模块不编译链接到zImage中,模块化是将这个模块仍然编译,但是不会将其链接到zImage中,会将这个模块单独链接成一个内核模块.ko文件,将来linux系统内核启动起来后可以动态的加载或卸载这个模块。
在menuconfig中选项前面的括号里,*表示编入,空白表示去除,M表示模块化

Kconfig文件详解

Kconfig按照一定的格式来书写,menuconfig程序可以识别这种格式,然后从中提取出有效信息组成menuconfig中的菜单项。

将来在做驱动移植等工作时,有时需要自己添加Kconfig中的一个配置项来将某个设备驱动添加到内核的配置项目中,这时候就需要对Kconfig的配置项格式有所了解,否则就不会添加。

menuconfig表示菜单(本身属于一个菜单中的项目,但是他又有子菜单项目)
config表示菜单中的一个配置项(本身并没有子菜单下的项目)。

  • menuconfig或者config后面空格隔开的大写字母表示的类似于 NETDEVICES的就是这个配置项的配置项名字,这个字符串前面添加CONFIG_后就构成了.config中的配置项名字。
  • 一个menuconfig后面跟着的所有config项就是这个menuconfig的子菜单。这就是Kconfig中表示的目录关系。
  • 内核源码目录树中每一个Kconfig都会source引入其所有子目录下的Kconfig,从而保证了所有的Kconfig项目都被包含进menuconfig中。这个也告诉我们:如果你自己在linux内核中添加了一个文件夹,一定要在这个文件夹下创建一个Kconfig文件,然后在这个文件夹的上一层目录的Kconfig中source引入这个文件夹下的Kconfig文件。
  • depends中文意思是“取决于”或者“依赖于”。所以depends在这里的意思是:本配置项依赖于另一个配置项。如果那个依赖的配置项为Y或者M,则本配置项才有意义;如果依赖的哪个配置项本身被设置为N,则本配置项根本没有意义。
    depends项目会导致make menuconfig的时候找不到一些配置项。所以你在menuconfig中如果找不到一个选项,但是这个选项在Kconfig中却是有的,则可能的原因就是这个配置项依赖的一个配置项是不成立的。
  • depends并不要求依赖的配置项一定是一个,可以是多个,而且还可以有逻辑运算。这种时候只要依赖项目运算式子的逻辑结果为真则依赖就成立。

tristate和bool的含义

tristate意思是三态(3种状态,对应Y、N、M三种选择方式)
bool是要么真要么假(对应Y和N)。
所以tristate的意思就是这个配置项可以被三种选择,bool的意思是这个配置项只能被2种选择。

Kconfig和.config文件和Makefile三者的关联

menuconfig程序的配置项被配置成Y、N、M会影响.config文件中的CONFIG_XXX变量的配置值。
这个.config中的配置值(=y、=m、没有)会影响最终的编译链接过程。如果=y则会被编入(built-in),如果=m会被单独连接成一个ko模块,如果没有则对应的代码不会被编译。

那么这么是怎么实现的?

都是通过makefile实现的。

obj-$(CONFIG_DM9000) += dm9000.o
如果CONFIG_DM9000变量值为y,则obj += dm9000.o,因此dm9000.c会被编译;
如果CONFIG_DM9000变量未定义,则dm9000.c不会被编译。如果CONFIG_DM9000变量的值为m则会被连接成ko模块(这个是在linux内核的Makefile中定义的规则)。

总结:

把menuconfig中的菜单项、Kconfig中的配置项、.config中的一行、 Makefile中的一行,这4个东西结合起来理解,则整个linux内核的配置体系就明了了。

make menuconfig时,会读取.config中的配置值来初始化menuconfig中的配置项。

menuconfig读取Kconfig的内容作为菜单项目内容。

找一个模块,把他配制成y,然后去make编译连接,最后得到的zImage中这个模块就被编译连接到zImage中了。