-
linux模块开发初探
2020-07-28 17:00:27新手入门,绕不开HelloWord,本章写一个最简单的helloword模块,从编译到加载,再到验证,没有问题。 第一步 创建hello.c文件,代码如下: #include <linux/module.h> /* Needed by all modules */ #...新手入门,绕不开HelloWord,本章写一个最简单的helloword模块,从编译到加载,再到验证,没有问题。
第一步
创建hello.c文件,代码如下:
#include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> int __init hello_module_init(void) { printk(KERN_INFO "Hello world.\n"); return 0;//A non 0 return means init_module failed; module can't be loaded. } void __exit hello_module_exit(void) { printk(KERN_INFO "Bye world.\n"); } module_init(hello_module_init); module_exit(hello_module_exit);
内核模块提供module_init和module_exit两个接口,其实是宏定义, 这些宏在linux / init.h中定义。 需要注意的是,必须在调用宏之前定义init和exit函数,否则会出现编译错误。
printk函数其实是内核态的printf,是内核日志接口,有8个优先级,对应有宏,可以在linux / kernel.h中查看。 如果未指定优先级,默认使用DEFAULT_MESSAGE_LOGLEVEL优先级别。
第二步
编写makefile,内容如下:
obj-m += hello.o hello-y := all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
obj-m表示编译生成可加载模块。相对应的,obj-y表示直接将模块编译进内核。
这里并没有输入hello.c源文件,这得益于makefile的自动推导功能。
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
-C选项:此选项指定内核源码的位置,make在编译时会进入内核源码目录执行编译,编译完成时返回。
M=$(PWD):需要编译的模块源文件地址。
第三步
在终端执行make,无报错,在源码目录下会生成hello.ko文件,如下:
[root@localhost helloword]# ls hello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile modules.order Module.symvers
此时执行命令以验证:
[root@localhost helloword]# insmod hello.ko [root@localhost helloword]# dmesg |tail -n 1 [25991.570137] Hello word. [root@localhost helloword]# rmmod hello.ko [root@localhost helloword]# dmesg |tail -n 2 [25991.570137] Hello word. [26029.873588] Bye world. [root@localhost helloword]#
可以看到,我的模块是正常加载,并能正常退出。
总结
这只是最简单的模块开发步骤,当然要构建一个复杂的模块代码,makefile也是很关键。自linux2.4开始,linux使用两个宏module_init和module_exit来完成模块的初始化和销毁。
内核开发,需要对应linux源码看,这样才能很清楚的找到接口的实现。内核说起来很难,其实,并没有想像的那么可怕,有问题在开源社区都能找到答案。
接下来,我会自己先玩一些复杂的模块,玩通了再来写博客,有意思又能成长的事情,欢迎一起交流。
-
linux模块开发必要的基础知识
2012-06-09 14:36:30当新加载的模块未声明许可证或者许可证不被内核认可的时候,内核将输出kernel tainted警告。这种情况,一般被称为内核污染。 声明许可证的方法: MODULE_LICENSE(“licensename”) 声明许可证的举例 MODULE_...1内核污染问题
当新加载的模块未声明许可证或者许可证不被内核认可的时候,内核将输出kernel tainted警告。这种情况,一般被称为内核污染。
声明许可证的方法:
MODULE_LICENSE(“licensename”)
声明许可证的举例
MODULE_LICENSE("DualBSD/GPL");
MODULE_LICENSE("GPL");
MODULE_LICENSE("BSD ");
内核认可(即不会认为受到污染)的证书类型如下:
GPL
BSD
DualBSD/GPL
内核污染标志本身上不会对内核的使用产生任何影响。
2符号导出问题
导出方法有以下两种
EXPORT_SYMBOL
EXPORT_SYMBOL_GPL
导出的“符号”可以是变量,也可以是函数。
导出和”static”概念有区别:
1:static在内核中的含义也只是限定一个变量只有本文件可以使用。
2:Static限制的变量也可以导出。
3:所有导出了的变量名都是唯一的。
4:只要导出了的变量就可以在其他模块使用,无论次变量是否static修饰。
5:一个变量或函数能不能由本模块中的其他文件使用,是由static修饰决定,规则与普通C语言规则一样,无论此函数是否导出。
总结地说,static的限制范围作用于模块本身以及模块自己的编译过程。导出的作用范围是模块加载入内核后的内核符号表。
3模块版本问题
CONFIG_MODVERSIONS内核选项打开后,加载模块时,内核将会强制验证模块的版本,如果和内核版本不一致,内核会拒绝加载。
模块的内核版本记录在于模块一起编译的.mod.c文件中,一般形式如下:
MODULE_INFO(vermagic,VERMAGIC_STRING);
v2.6.34.8的menuconfig中只有version检查的选项,
但是(不知道是内核升级还是发布版定制导致),后续还出现了校验内核接口CRC的版本校验,导致即使同一个小版本号的内核,只要源码树不同,编译出的模块就可能无法互通。
解决办法:
1:模块与内核在同一个目录树下编译
2:解除内核的版本检查机制。
4符号寻址问题
内核所有函数的名字与地址的对应关系都是被内核保存了的。从/proc/kallsyms可以看到这个对应关系。新加入的模块,想调用内核中已经导出的函数,就需要查这张表。同样,内核中已有的模块,想调用新加入的模块的函数,也需要查找这张表。幸运的是,这个复杂的查找过程对于我们来说是完全透明的。
从/proc/kallsyms文件中也可以看见你刚加载的模块的函数,以及他们在内核空间中的地址。
5编译时自动补齐的部分
编译模块时产生的.mod.c文件记录了这个模块的很多信息,比如版本。这个.c文件将与你自己写的.c模块一起编译成模块。
6自动drop的代码段
当一个函数被标注为__init的时候,它将被加载并使用一次,然后保存其代码的内存将被释放。这是一个半自动的过程:编程者指定哪些代码只用一次,而内核按照编程者的指定丢弃使用过的代码。
一般模块加载时的入口函数会被标记为__init。这个函数与模块中其他函数的不同就在于:其他函数将被加载进内核的代码空间中,直到卸载模块为止;而这个“初始化函数”,开始时也会被加载进内核的代码空间中,但是内核在调用过一次后,就会将这段代码自动自动丢弃掉,内核的代码空间中再也看不见这个函数了。
-
从零开始 linux 模块开发总结
2010-12-09 23:16:001.准备编译 ko 的 linux 环境: 本人使用 ubuntu 9.10. 2.查看系统的版本: 使用 uname -r 命令, 本人系统是 2.6.31-22-generic. 3.安装内核头文件: sudo apt-get install linux-headers-`uname -r` ...1.准备编译 ko 的 linux 环境:
本人使用 ubuntu 9.10.
2.查看系统的版本:
使用 uname -r 命令, 本人系统是 2.6.31-22-generic.
3.安装内核头文件:
sudo apt-get install linux-headers-`uname -r`
本人使用: sudo apt-get install linux-headers-2.6.31-22-generic. 可能会得到下面的信息:
linux-headers-2.6.31-22-generic is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.4.当然, gcc /make 等工具天生就是需要的,这些一般不用安装,系统自带了。
5.编写 c code:
本人的 hello.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "hello world enter/n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "hello world exit/n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Alex Xia");
MODULE_DESCRIPTION("A single hello world module");
MODULE_ALIAS("a simplest module");6.编写Makefile,注意,命令前面是 Tab 键,不是空格。
本人的 Makefile :
obj-m:=hello.o
KERNELBUILD:=/lib/modules/2.6.31-22-generic/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
rm -rf *.o *.ko *.mod.c *.cmd .tmp_versions7.在Makefile 一级的目录中执行 make, 生成 hello.ko 等文件。
8.使用 sudo insmod hello.ko 将模块插入内核系统。
9.如果没看到 module_init()注册的函数执行中的打印信息,使用 dmesg 命令查看系统日志。本人有看到 “hello world enter”。
10.使用 sudo rmmod hello 从内核中移出 hello.ko 模块。使用 dmesg 命令查看系统日志。本人有看到 “hello world exit”。
-
Linux内核模块开发
2020-10-17 20:46:05Linux内核模块开发 1. 源代码文件结构 下面是一个最基本的内核模块源代码结构,任何模块项目都需要包含这些内容 #include <linux/module.h> /*模块初始化函数*/ static int module_init_func(void) { ...Linux内核模块开发
1. 源代码文件结构
下面是一个最基本的内核模块源代码结构,任何模块项目都需要包含这些内容
#include <linux/module.h> /*模块初始化函数*/ static int module_init_func(void) { return 0; } /*模块卸载函数*/ static void module_exit_func(void) { } module_init(module_init_func); module_exit(module_exit_func); MODULE_LICENSE("GPL");
2. 常用的头文件
开发内核模块通常会用到以下头文件
#include <linux/module.h> // 内核模块相关操作,如模块注册,卸载等等 #include <linux/kernel.h> // 内核输入输出等功能函数 #include <linux/fs.h> // 虚拟文件系统相关操作,如打开文件,关闭文件等等 #include <linux/mm.h> // 内存映射相关操作 #include <linux/slab.h> // 内核动态分配,kmalloc/kfree等 #include <linux/vmalloc.h> // 内核动态分配, vmalloc/vfree等 #include <linux/mman.h> // 内存映射相关操作 #include <linux/kallsyms.h> // 查找内核符号表的相关函数 kallsyms_lookup_name #include <linux/uaccess.h> // 操作用户空间内存的相关函数 #include <linux/err.h> // 处理Linux错误码的相关函数 #include <linux/types.h> // Linux常用的基本数据类型定义,如uint8_t,size_t等等
3. 编译单个源代码文件的内核模块
最基本的用于编译一个内核模块的Makefile如下
# 这里设置模块名称,模块名称为源代码文件名去掉后缀名 MODULE_NAME := module_name # 内核构建系统会根据传递给其的M变量来找到这个Makefile文件并将其include,通过检查KERNELRELEASE变量,可以将传递给内核构建系统的内容和本Makefile单独的操作分开以避免相互影响 ifneq ($(KERNELRELEASE),) # 此内容传递给内核构建系统,因此只向内核构建系统传递其必须的变量(即obj-m),避免污染内核构建系统中的其他变量 obj-m := $(MODULE_NAME).o else # 下面的内容不需要被内核构建系统使用 # 通过ccflags-y变量可以增加编译时的编译参数 ccflags-y := # 注意这里传递给内核构建系统的M变量,内核构建系统将在M变量保存的目录中搜索Makefile文件,并将其include all: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) ccflags-y=$(ccflags-y) modules clean: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean endif
4. 编译多个源代码文件的内核模块
如果要编译的内核模块包括了多个源代码,需要对Makefile进行一些调整
# 这里设置模块名称(不需要为源代码名称) MODULE_NAME := module_name ifneq ($(KERNELRELEASE),) obj-m := $(MODULE_NAME).o # 这里写源代码列表,需要将后缀名.c改为.o,以空格分隔 $(MODULE_NAME)-objs := sourcecode1.o sourcecode2.o else # 添加额外的编译选项 ccflags-y := all: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) ccflags-y=$(ccflags-y) modules clean: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean endif
-
linux内核模块开发
2017-04-02 21:55:58linux内核模块开发 什么是内核模块? linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用需要的组件呢? 方法一:把所有的组件编译进内核文件,但这样会导致生成的内核文件过大和调整组件不方便。... -
LInux 内核模块开发实例
2019-02-28 18:50:23LInux 内核模块开发实例编写一个helloworld内核模块编写编译内核模块Makefile 编写一个helloworld内核模块 #include &lt;linux/init.h&gt; #include &lt;linux/module.h&gt; ... -
linux驱动开发笔记7驱动模块开发流程[汇编].pdf
2020-08-23 21:12:44实用标准文案 linux 驱动开发笔记 7 驱动模块开发流程 一驱动模块的搭建 1 在 Drivers 目录下建立自己的模块目录 2 建立 Makefile 文件见上一篇博客 3 建立源文件源文件矿建如下其中这两个函数式必须实现的 #include... -
【Linux开发】Linux模块机制浅析
2016-05-16 10:50:00Linux允许用户通过插入模块,实现干预内核的目的。一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析。 模块的Hello World! 我们通过创建一个简单的模块进行测试。首先是源文件... -
【ARM-Linux开发】Linux模块机制浅析
2017-08-28 10:52:00Linux模块机制浅析 Linux允许用户通过插入模块,实现干预内核的目的。一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析。 模块的Hello World! 我们通过创建一个简单的模块进行... -
Linux内核动态模块开发入门
2019-08-17 20:49:06这篇文章旨在介绍Linux内核动态模块开发时的基本结构,以及如何编译开发的模块。 Linux内核模块开发 我们知道Linux的内核是可以定制的,在编译之前我们可以通过make menuconfig对我们的内核进行配置。... -
linux 内核模块开发相关的文章搜集和模块开发过程中的小技巧
2020-12-22 08:20:13内核模块开发相关链接: https://www.thegeekstuff.com/2013/07/write-linux-kernel-module/入门教程;insmod, rmmod, modinfo等相关命令; https://www.thegeekstuff.com/2010/08/make-utility/make 工具使用教程;... -
linux驱动模块开发(一)
2016-12-21 11:07:47Linux的模块开发注意一、模块Linux模块是linux的特色,他可以在需要的时候动态加载进内核,也可以在合适的时候移除内核,这样就保证内核的简洁高效二、代码分析#include <linux/init.h> #include <linux/module.h> ... -
linux内核模块开发基础
2016-05-06 20:35:501. 什么是内核模块 ... 原因:Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译键内核,即:zImage或bzImage,但这样会导致一个问题:占用内存过多。然后 -
移远4G模块linux开发资料
2018-07-01 10:50:52移远4G通信模块在linux环境下开发文档,以及linux驱动程序,根据开发文档流程进行,可实现在linux环境下4G通信。 -
Linux下Apache模块开发
2018-12-07 16:48:50环境:Linux RedHat 首先来介绍下apache的一个工具apxs。apxs是一个为Apache HTTP服务器编译和安装扩展模块的工具,用于... 安装好Apache服务器后,安装目录的bin目录下包含apxs工具。 模块开发代码 mod_jump_... -
Linux驱动基础开发 Linux 驱动开发前奏(模块编程)
2017-08-10 16:24:15Linux 驱动开发前奏(模块编程) 转自Linux公社 一、linux内核模块简介 linux内核整体结构非常庞大,其包含的组件也非常多。我们怎么把需要的部分都包含在内核中呢? 一种办法是把所有的需要的... -
Linux内核模块开发实例学习
2017-09-14 09:09:12注:以下程序只是在我机器上测试通过,但代码不一定合理或高效,只是想了解一下内核模块的开发流程,以及工作流程 例子来源于网络,在此表示感谢 [cpp] view plain copy [root@localhost module]# cat... -
linux 内核模块开发注意事项
2016-04-12 16:43:08在写一个netfilter相关的内核模块,发现一些开发内核模块时要注意的问题,简记于此,如有谬误,还请不吝指正! 1、内核模块内不能使用stdlib,stdio等C标准库,内核在lib/string.c下实现了一些常用函数strcpy.... -
Linux 内核模块开发基本知识
2014-05-30 23:28:49Linux 内核模块开发 1、 什么是内核模块? 避免内核镜像太大,占用太多的内存资源,所以可以编译进内核模块中去。 特点:动态的加载与卸载;不会被编译进内核镜像文件。 2、 学习方法。 范例程序->思维导图...