国嵌视屏学习第八天——linux内核配置与编译

Linux kernel map

 

一.Linux体系结构分为用户空间和kernel space。中用户空间分为用户应用程序和GNU C library(glibc)kernel space分为 System Call Interface,kernel, 基于体系结构的内核代码

内核空间与用户空间是程序执行的两种不同状态, 通过系统调用(用户主动发起)和硬件中断(由外部原因引起,是被动的)能够完成从用户空间到内核空间的转移。

Linux内核架构由7部分组成:SCI, PM, VFS(隐藏各种文件系统(如EXT2FATNFS、设备文件等)的具体细节,为文件操作提供统一的接口例如open()函数), MM(完成虚地址到物理地址的转换), Network Stack, Arch(CPU密切相关), DD

二.目录结构

Linux内核源代码采用树形结构进行组织,非常合理地把功能相关的文件都放在同一个子目录下,使得程序更具可读性

内核源代码下载地址:www.kernel.org

---Arch目录:

archarchitecture的缩写。内核所支持的每种CPU体系,在该目录下都有对应的子目录。每个CPU的子目录,又进一步分解为bootmmkernel等子目录,分别包含控制系统引导,内存管理,系统调用等。(和开发板有关的放在“mach”开头的目录中)

----Block目录:

部分块设备驱动程序

----Crypto目录:

加密、压缩、CRC校验算法

----Documentation

内核的文档

-----Drivers目录:

设备驱动程序

----Fs目录:

存放各种文件系统的实现代码。每个子目录对应一种文件系统的实现,公用的源程序用于实现虚拟文件系统nfs

||---devpts/* /dev/pts虚拟文件系统*/

||---ext2/*第二扩展文件系统*/

||---fat/*MSfat32文件系统*/

||---isofs/*ISO9660光盘cd-rom上的文件系统*/

----include目录:
内核所需要的头文件。与平台无关的头文件在include/linux子目录下, 与平台相关的头文件则放在相应的子目录中。

----lib目录:

库文件代码

----mm(memory management)目录:

Mm目录中的文件用于实现内存管理中与体系结构无关的部分(与体系结构相关的部分在arch目录中)

----net目录:

网络协议的实现代码

||---802 /*802无限通信协议核心支持代码*/

||---appletalk /*与苹果系统联网的协议*/

||---ax25 /*AX25无限INTERNET协议*/

||---bridge /*桥接设备*/

||---ipv4 /*IP协议族V432位寻址模式*/

||---ipv6 /*IP协议族V6*/

---samples

一些内核编程的范例

---scripts

配置内核的脚本

---security

SElinux的模块

---sound

音频设备的驱动程序

---usr
cpio命令实现

---virt

内核虚拟机

经常会修改到的目录是arch目录和drivers目录

三.内核配置与编译

Linux内核具有可定制的优点,具体步骤如下:

1.清除临时文件、中间文件和配置文件.

----make clean:

remove most generated files but keep the config

----make mrproper:

remove all generated files and config files

----make distclean

mrproper and remove editor backup and patch files

在根目录位置下执行上述命令。

2.确定目标系统的软硬件配置情况,比如CPU的类型、网卡的型号、所需支持的网络协议等

3.使用如下命令之一配置内核:
----make config :基于文本模式的交互式配置

----make menuconfig :基于文本模式的菜单型配置(推荐使用)(需安装sudo apt-get install libncurses5-dev

----make oldconfig :使用已有的配置文件(.config),但是会访问新增的配置选项

----make xconfig : 图形化的配置(需安装图形化系统)

使用make menuconfig时配置菜单中的高亮行:[ _ ]表示不编译;[M]表示仅仅是编译;[*]表示诸*文件编译链接产生镜像文件。

配置好后有个.config隐藏文件(ls -a可以看到)

内核配置通常在一个已有的配置文件基础上,通过修改得到新的配置文件,linux内核提供了一系列可供参考的内核配置文件,位于arch/$cpu/configs

4.编译内核:make zlmage

make bzlmage ubuntumake命令即可)

区别:在X86 平台,zlmage只能用小于512K的内核

如需获取详细编译信息,可使用:

make zlmage V = 1

make bzlmage V = 1

编译好的内核位于arch/<cpu>/boot/目录下

5.编译内核模块:make modules

6.安装内核模块:make modules_install

  将编译好的内核模块从内核源代码目录copy/lib/modules

7.制作init ramdiskmkinitrd initrd -$version $version

例:mkinitrd initrd-2.6.29 2.6.29

$version可以通过查询/lib/modules下的目录得到(该目录是经过第6步之后就产生的)

利用该命令将/lib/modules下的目录做成一个文件

内核安装(X86平台)

1.cp arch/x86/boot/bzlmage  /boot/vmlinuz-$version(拷贝内核文件)

2.cp $initrd  /boot/(拷贝randisk

3.修改/etc/grub.conf或者/etc/lilo.conf

$version为所编译的内核版本号

四.Linux内核模块开发

Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用需要的组件呢:

----方法一:把所有的组件都编译进内核文件,即:zlmagebzlmage,但这样会导致两个问题:一是生成的内核文件过大(bzlmage过大);二是如果要添加或删除某个组件,需要重新编译整个内核

那么有没有一种机制能让内核文件(zlmagebzlmage)本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核中呢?

——有,linux提供了一种叫做“内核模块”的机制,可以实现以上效果。

内核模块具有以下特点:a.模块本身并不被编译进内核文件(zlmagebzlmage);b.可以根据需求,在内核运行期间动态地安装和卸载

(hello_world.c),从中可以看见模块函数没有main函数

#include <linux/init.h>

#include <linux/module.h>

static int __int hello_int()

{

printk("Hello World!\n");

return 0;

static void __exit hello_exit()

{

printk("<7>hello <0>exit\n");

}

module_init(hello_init);

module_exit(hello_exit);

内核模块程序结构

1.模块加载函数(必须)

安装模块时被系统自动调用的函数,通过module_init宏来指定(指定程序的入口函数,相当于应用程序中的main函数),在HelloWorld模块中,模块加载函数为hello_init

2.模块卸载函数(必须)

卸载模块时被系统自动调用的函数,通过module_exit宏来指定,在HelloWorld模块中,模块卸载函数为hello_exit

内核模块的编译

linux 2.6下编译内核模块,通常使用makefile(不再像linux 2.4下面可以使用gcc来编译)

例——内核模块由一个源文件构成,该如何编写makefile

ifneq($(KERNELRELEASE),)

obj-m := hello_world.o

else

KDIR :=/lib/modules/2.6.38-14-generic/build

all:

make -C $(KDIR)  M=$(PWD) modules

clean: rm -f *.ko *.o *.mod *.mod.c *.symvers

Endif

说明

Obj-m:这个变量是指定你要生成哪些模块。模块的格式为 obj-m  :=<模块名>.o

KDIR:这是我们正在运行的操作系统内核编译目录。也就是编译模块需要的环境

M = :指定我们源文件的位置

PWD :这是当前工作路径$(shell )make 的一个内置函数。用来执行shell命令。

 

除了obj-mKDIR的内容需改变之外,其他的可以不做修改

例——内核模块由多个源文件构成(main.c add.c),该如何编写makefile

ifneq($(KERNELRELEASE), )

obj-m := hello.o //要生成的模块名

hello-objs :=main.o add.o //生成这个模块所需要的目标文件,模块的名字不能和目标文件相同名字

else

KDIR := /lib/modules/2.6.38-14-generic/build

all:make -C $(KDIR)  M=$(PWD) modules

clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers

Endif

安装与卸载

----加载insmodinsmod hello.ko

----卸载rmmodrmmod hello

----lsmod命令用于查看有哪些内核模块被安装

----加载modprobemodprobe hello

Modprobe如同insmod,也是加载一个模块到内核。它的不同之处在于它会根据文件/lib/modules/<$version>/modules.dep

来查看要加载的模块,看它是否还依赖于其他模块,如果是,modprobe会首先找到这些模块,把他们先加载到内核。

模块可选信息

1.许可证申明

MODULE_LICENSE用来告知内核,该模块带有一个许可证,没有这样的说明,加载模块时内核会抱怨,有效地许可证有“GPL”、“GPLv2”、“GPL and additional rights”、“DualBSD/GPL”、“Dual MPL/GPL”和“Proprietary

2.作者申明(可选)

MODULE_AUTHOR("Simon Li")

3.模块描述(可选)

MODULE_DESCRIPTION("Hello World Module");

4.模块版本(可选)

MODULE_VERSION("V1.0");

5.模块别名(可选)

MODULE_ALIAS(" a simple module");

6.模块参数

通过宏module_param指定模块参数(在应用型程序中,命令行参数是char*类型的(即字符串),保存在char *argv[]中),模块参数用于在加载模块时传递参数给模块。

module_pram(name, type, perm)

----name:是模块参数的名称,type是这个参数的类型,perm是模块参数的访问权限

type常见值:bool,布尔型;int,整型;charp,字符串型

perm常见值:

S_IRUGO:任何用户都对/sys/module中出现的该参数具有读权限

S_IWUSR:允许root用户修改/sys/module中出现的该参数

例如:

 int a = 3;

 char *st;

 module_param(a, int, S_IRUGO);

 module_param(st, charp, S_IRUGO);

param.c

内核符号导出

/proc/kallsyms记录了内核中所有导出的符号的名字与地址

内核符号的导出使用:EXPORT_SYMBOL(符号名)

EXPORT_SYMBOL_FPL(符号名)

其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块

常见问题:版本不匹配

内核模块的版本由其所依赖的内核代码版本所决定,在加载内核模块时,insmod程序会将内核模块版本与当前正在运行的内核版本比较,如果不一致时,就会出现类似下面的错误:

insmod hello.ko

disagrees about version of symbol struct_module 

Insmod: error inserting 'hello.ko': -1 invalid module format

可以使用uname -r查看当前使用的内核版本

就是在makefile中指定编译的内核代码的版本即是内核模块的版本,当两个版本不一致的时候加载不进去

内核模块版本是由它的内核代码所决定的(可以通过查看内核的Makefile知道其版本)

解决方法:

1.使用modprobe ----force-modversion强行插入

2.确保编译内核模块时,所依赖的内核代码版本等同于当前正在运行的内核(推荐)

总结--对比应用

对比应用程序,内核模块具有以下不同:

应用程序是从头(main)在尾执行任务,执行结束后从内存中消失。内核模块则是先在内核中注册自己以便服务于将来的某个请求,然后它的初始化函数结束,此时模块依然存在于内核中,知道卸载函数被调用,模块才从内核中消失。

内核打印

printkprintf对比:printk只在内核中使用,printf只在应用程序中使用

   printk允许根据严重程度,通过附加不同的“优先级”来对消息分类:在<linux/kernel.h>中定义了8种记录级别。按照优先级递减的顺序分别是:

KERN_EMERG "<0>":用于紧急消息,常常是那些崩溃前的消息

KERN_ALERT "<1>":需要立刻行动的消息

KERN_CRIT "<2>":严重情况

KERN_ERR "<3>":错误情况

KERN_WARNING "<4>":有问题的警告

KERN_NOTICE "<5>":正常情况,但是任然值得注意

KERN_INFO "<6>":信息型消息

KERN_DEBUG "<7>":用作调试消息

.通过优先级控制消息是否打印到屏幕

没有指定优先级的printk默认使用DEFAULT_MESSAGE_LOCAL优先级,它是一个在kernel/printk.c中定义的整数。

2.6.29内核中

#define DEFAULT_MESSAGE_LOGLEVEL 4

/*KERN_WARNING*/

不管信息是否打印到屏幕上,在/var/message中肯定有相关信息(也可以在命令行使用dmesg | tail -12来查看/var/log目录中,名为dmesg的文件内容。

dmesg用来显示开机信息,kernel会将开机信息存储在ring buffer中。您若是开机时来不及查看信息,可利用dmesg来查看

纯字符控制台优先级配置

/proc/sys/kernel/printk

6 4 1 7

----console_loglevel

----default_message_loglevel

----minimum_console_level

----default_console_loglevel

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值