Linux 驱动开发之内核模块开发 (一)—— 内核模块机制基础
2016-03-09 19:23
579 查看
一、内核模块的概念
1、什么是模块?
内核模块是一些可以让操作系统内核在需要时载入和执行的代码,同时在不需要的时候可以卸载。这是一个好的功能,扩展了操作系统的内核功能,却不需要重新启动系统,是一种动态加载的技术。
特点:动态加载,随时载入,随时卸载,扩展功能
2、内核模块的加载作用
内核模块只是向linux内核预先注册自己,以便于将来的请求使用;由目标代码组成,没有形成完整的可执行程序。只是告诉内核,它有了新增的功能,而并不马上使用(执行),只是等待应用程序的调用;而应用程序在加载后就开始执行。
3、内核模块所用函数
内核模块代码编写没有外部的函数库可以用,只能使用内核导出的函数。而应用程序习惯于使用外部的库函数,在编译时将程序与库函数链接在一起。例如对比printf( ) and printk( )。
所以驱动所用头文件均来自内核源代码,应用程序所用头文件来自库函数。
4、内核模块代码运行空间
内核代码运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。
二、linux内核模块的框架
最少两个入口点
*模块加载函数 module_init()
*模块卸载函数 module_exit()
module_init() and module_exit()两个宏定义声明模块的加载函数和卸载函数,这个定义在linux3.14/include/linux/init.h中。内容为:
#define module_init(x) __initcall(x)
//在内核启动或模块加载时执行
#define module_exit(x) __exitcall(x)
//在模块卸载时执行
每一个模块只能有一个module_init 和一个module_exit。
下面我们对比一下应用程序,看看应用程序与内核模块的区别:
应用程序和内核模块对比总结如下
三、Linux 内核模块的编译和加载
其实内核的编译在前面就已经讲过了,现在回顾一下:
linux3.14内核的Makefile分为5个组成部分:
Makefile
最顶层的Makefile
.config 内核的当前配置文件,编译时成为定层Makefile的一部分
arch/$(ARCH)/Makefile 与体系结构相关的Makefile
s/ Makefile.* 一些Makefile的通用规则
kbuild Makefile 各级目录下的大概约500个文件,编译时根据上层Makefile传下来的宏定义和其他编译规则,将源代码编译成模块或者编入内核
顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 s目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。(其中.config的内容是在make menuconfig的时候,通过Kconfig文件配置的结果,上面已经说过)
对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。主要部分有:
1、目标定义
目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。
最简单的只有一行,如
obj-y += foo.o
表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。除了y,m以外的obj-x形式的目标都不会被编译。
由于既可以编译成模块,也可以编译进内核,更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式,如:
obj-$(CONFIG_HELLO_MODULE) += hello.o
除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下
2、多目标
一个内核模块由多个源文件编译而成,这是Makefile有所不同。
采用模块名加 –objs后缀或者 –y后缀的形式来定义模块的组成文件。
如以下例子:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模 块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o 直至ext2.ko文件,是否包括xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的 ext2.o将被链接进built-in.o最终链接进内核。这里需要注意的一点是,该kbuild Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s
或者写成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
3、目录的迭代
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS 的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的
4、不同的模块编译方式
编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:
make -C path/to/kernel/src M=$PWD modules
-C指定内核源码的根目录,$PWD 或 `PWD` 是当前目录的环境变量,告诉kbuild回到当前目录来执行build操作。
5、模块安装
当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH 指定一个前缀,如:
make INSTALL_MOD_PATH=/foo modules_install
模块将被安装到 /foo/lib/modules目录下
注:内核模块是.ko后缀,使内核模块和普通的目标文件区别开。
1、什么是模块?
内核模块是一些可以让操作系统内核在需要时载入和执行的代码,同时在不需要的时候可以卸载。这是一个好的功能,扩展了操作系统的内核功能,却不需要重新启动系统,是一种动态加载的技术。
特点:动态加载,随时载入,随时卸载,扩展功能
2、内核模块的加载作用
内核模块只是向linux内核预先注册自己,以便于将来的请求使用;由目标代码组成,没有形成完整的可执行程序。只是告诉内核,它有了新增的功能,而并不马上使用(执行),只是等待应用程序的调用;而应用程序在加载后就开始执行。
3、内核模块所用函数
内核模块代码编写没有外部的函数库可以用,只能使用内核导出的函数。而应用程序习惯于使用外部的库函数,在编译时将程序与库函数链接在一起。例如对比printf( ) and printk( )。
所以驱动所用头文件均来自内核源代码,应用程序所用头文件来自库函数。
4、内核模块代码运行空间
内核代码运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。
二、linux内核模块的框架
最少两个入口点
*模块加载函数 module_init()
*模块卸载函数 module_exit()
module_init() and module_exit()两个宏定义声明模块的加载函数和卸载函数,这个定义在linux3.14/include/linux/init.h中。内容为:
#define module_init(x) __initcall(x)
//在内核启动或模块加载时执行
#define module_exit(x) __exitcall(x)
//在模块卸载时执行
每一个模块只能有一个module_init 和一个module_exit。
下面我们对比一下应用程序,看看应用程序与内核模块的区别:
#include <stdio.h> int main() { printf("Hello World!\n"); return 0; } | #include <linux/module.h> //所有内核模块都必须包含这个头文件 #include<linux/kernel.h> //使用内核信息优先级时要包含这个 #include<linux/init.h> //一些初始化的函数如module_init() static int hello_init(void) { printk("hello_init"); } static void hello_exit(void) { printk("hello_exit \n"); } MODULE_LICENSE("GPL"); //模块许可声明 module_init(hello_init); 加载时候调用该函数insmod module_exit(hello_exit);卸载时候 rmmod |
应用程序 | 模块 | |
入口函数 | main | 加载时候调用hello_init |
函数的调用 | /lib | 所有函数可以直接调用 |
运行空间 | 用户空间 | 内核空间 |
资源的释放 | 系统自动释放 kill -9 pid 手动释放 | 手动释放 |
其实内核的编译在前面就已经讲过了,现在回顾一下:
linux3.14内核的Makefile分为5个组成部分:
Makefile
最顶层的Makefile
.config 内核的当前配置文件,编译时成为定层Makefile的一部分
arch/$(ARCH)/Makefile 与体系结构相关的Makefile
s/ Makefile.* 一些Makefile的通用规则
kbuild Makefile 各级目录下的大概约500个文件,编译时根据上层Makefile传下来的宏定义和其他编译规则,将源代码编译成模块或者编入内核
顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 s目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。(其中.config的内容是在make menuconfig的时候,通过Kconfig文件配置的结果,上面已经说过)
对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。主要部分有:
1、目标定义
目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。
最简单的只有一行,如
obj-y += foo.o
表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。除了y,m以外的obj-x形式的目标都不会被编译。
由于既可以编译成模块,也可以编译进内核,更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式,如:
obj-$(CONFIG_HELLO_MODULE) += hello.o
除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下
2、多目标
一个内核模块由多个源文件编译而成,这是Makefile有所不同。
采用模块名加 –objs后缀或者 –y后缀的形式来定义模块的组成文件。
如以下例子:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模 块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o 直至ext2.ko文件,是否包括xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的 ext2.o将被链接进built-in.o最终链接进内核。这里需要注意的一点是,该kbuild Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s
或者写成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
3、目录的迭代
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS 的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的
4、不同的模块编译方式
编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:
make -C path/to/kernel/src M=$PWD modules
-C指定内核源码的根目录,$PWD 或 `PWD` 是当前目录的环境变量,告诉kbuild回到当前目录来执行build操作。
5、模块安装
当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH 指定一个前缀,如:
make INSTALL_MOD_PATH=/foo modules_install
模块将被安装到 /foo/lib/modules目录下
注:内核模块是.ko后缀,使内核模块和普通的目标文件区别开。
相关文章推荐
- Linux线程-互斥锁pthread_mutex_t
- centos 7 中防火墙的关闭问题
- Linux设备与驱动学习笔记(概述)
- 整理github上的一些好的项目(C\C++\Java\PHP\Android\Linux\JS\CSS\iOS)
- linux学习之c语言/var/lib/apt/lists/lock
- 检查linux的磁盘空间占用
- Linux下的压缩解压缩命令详解
- Linux 系统中僵尸进程
- Unix Study之--Linux共享访问AIX 6之NFS
- Unix Study之--Linux共享访问AIX 6之NFS
- Linux 文件系统IO性能优化
- SELinux简介
- centos系统下实现ssh无密码登陆
- 【ntp】centos7下ntp服务器设置
- Linux vmstat命令实战详解
- virtualbox下给centos共享文件
- CentOs6.5中安装和配置vsftp
- Linux时间戳和标准时间的互转
- Linux时间戳和标准时间的互转
- linux 起步走 platform_driver_register