linux 驱动学习笔记04--简单驱动

首先贴代码helloworld.c和Makefile

/*************************************************************************
    > File Name: helloworld.c
    > Author: hailin.ma
    > Mail: mhl2018@126.com 
    > Created Time: Wed 15 Jul 2015 02:39:35 PM CST
 ************************************************************************/

#include <linux/init.h>
#include <linux/module.h>

static char* mod_name = "hello";
static int mod_num = 1;

static int __init hello_init(void)
{
    printk(KERN_INFO"hello world init! mod_name = %s,mod_num = %d\n",mod_name,mod_num);
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO"hello world exit!\n");
}

module_init(hello_init);
module_exit(hello_exit);

module_param(mod_name,charp,S_IRUGO);
module_param(mod_num,int,S_IRUGO);
MODULE_AUTHOR("hailin.ma <mhl2018@126.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple hello world module");
MODULE_ALIAS("simple driver test");

Makefile

#CFLAGS = -g
KVERS = /lib/modules/$(shell uname -r)/build

obj-m += hello.o
hello-objs:=helloworld.o


all:
    make -C $(KVERS) M=$(PWD) modules
    @rm *.o

clean:
    make -C $(KVERS) M=$(PWD) clean

make 执行后将生成hello.ko文件,命令:insmod hello.ko 即可加载模块到内核。加载 hello.ko 后,内核中将包含/sys/module/hello 目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在/sys/module/hello
目录下运行“ tree –a” 得到如下目录树:

modinfo <模块名 >命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及 vermagic:

insmod  加载模块

lsmod  查看已加载模块, lsmod 命令实际上是读取并分析“/proc/modules” 文件。

rmmod  卸载模块

tail /var/log/messages 查看printk打印消息

一个 Linux 内核模块主要由如下几个部分组成。( 1 )模块加载函数(一般需要)。当通过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。

static int _ _init initialization_function(void)      //初始化函数
{
 /* 初始化代码 */
}
module_init(initialization_function);    //指定模块初始化函数

模块加载函数必须以“ module_init(函数名 )” 的形式被指定。它返回整型值,若初始化成功,应返回 0。而在初始化失败时,应该返回错误编码。在 Linux 内核里,错误编码是一个负值,在<linux/errno.h>中定义,包含-ENODEV、 -ENOMEM 之类的符号值。总是返回相应的错误编码是种非常好的习惯,因为只有这样,用户程序才可以利用 perror 等方法把它们转换成有意义的错误信息字符串。在 Linux 2.6 内核中,可以使用 request_module(const char *fmt, …)函数加载内核模块,驱动开发人员可以通过调用request_module(module_name);或request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev));这种灵活的方式加载其他内核模块。在 Linux 中,所有标识为_ _init 的函数在连接的时候都放在.init.text 这个区段内,此外,所有的_ _init 函数在区段.initcall.init 中还保存了一份函数指针,在初始化时内核会通过这些函数指针调用这些_ _init 函数,并在初始化完成后,释放 init 区段(包括.init.text、 。initcall.init等)。

( 2)模块卸载函数(一般需要)。当通过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块卸载函数相反的功能。

static void _ _exit cleanup_function(void)    //模块卸载函数
{
  /* 释放代码 */
}
module_exit(cleanup_function);      //指定模块卸载函数

模块卸载函数在模块卸载的时候执行,不返回 任何值,必须以“ module_exit(函数名 )” 的形式来指定。通常来说,模块卸载函数要完成与模块加载函数相反的功能, 如下所示。! 若模块加载函数注册了 XXX,则模块卸载函数应该注销 XXX。! 若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。! 若模块加载函数申请了硬件资源(中断、 DMA 通道、 I/O 端口和 I/O 内存等)的占用,则模块卸载函数应释放这些硬件资源。! 若模块加载函数开启了硬件,则卸载函数中一般要关闭之。和_ _init 一样, _ _exit 也可以使对应函数在运行完成后自动回收内存。实际上, _ _init 和_ _exit都是宏,其定义分别为:#define _ _init _ _attribute_ _ (( _ _section_ _ (".init.text")))和#ifdef MODULE#define _ _exit _ _attribute_ _ (( _ _section_ _(".exit.text")))

#else#define _ _exit _ _attribute_used_ _attribute_ _ (( _ _section_ _(".exit.text")))#endif数据也可以被定义为_ _initdata 和_ _exitdata,这两个宏分别为:#define _ _initdata _ _attribute_ _ (( _ _section_ _ (".init.data")))和#define _ _exitdata _ _attribute_ _ (( _ _section_ _(".exit.data")))

( 3)模块许可证声明(必须)。许可证( LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染 ( kernel tainted)的警告。在 Linux 2.6 内核中,可接受的 LICENSE 包括“GPL”、“GPL v2”、“GPL and additional rights”、“ Dual BSD/GPL”、 “ Dual MPL/GPL” 和“ Proprietary”。大多数情况下,内核模块应遵循 GPL 兼容许可权。 Linux 2.6 内核模块最常见的是以MODULE_LICENSE( "Dual BSD/GPL" )语句声明模块采用 BSD/GPL 双 LICENSE。( 4)模块参数(可选)。模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。

我们可以用 “ module_param(参数名 ,参数类型,参数读/写权限)” 为模块定义一个参数,例如下列代码定义了 1 个整型参数和 1 个字符指针参数:

static char *book_name = " dissecting Linux Device Driver ";
static int num = 4 000;
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);

在装载内核模块时,用户可以向模块传递参数,形式为“ insmode(或 modprobe)模块名 参数名 =参数值”,如果不传递,参数将使用模块内定义的缺省值。参数类型可以是 byte、 short、 ushort、 int、 uint、 long、 ulong、 charp(字符指针)、 bool 或 invbool(布尔的反),在模块被编译时会将 module_param 中声明的类型与变量定义的类型进行比较,判断是否一致。模块被加载后,在/sys/module/目录下将出现以此模块名命名的目录。当“参数读/写权限” 为 0时,表示此参数不存在 sysfs 文件系统下对应的文件节点,如果此模块存在“参数读/写权限” 不为 0的命令行参数,在此模块的目录下还将出现 parameters 目录,包含一系列以参数名命名的文件节点,这些文件的权限值就是传入 module_param()的“参数读/写权限”,而文件的内容为参数的值。除此之外,模块也可以拥有参数数组,形式为“module_param_array(数组名,数组类型,数组长,参数读/写权限)”。从 2.6.0~2.6.10 版本, 需将数组长变量名赋给“数组长”,从 2.6.10 版本开始, 需将数组长变量的指针赋给“数组长”,当不需要保存实际输入的数组元素个数时,可以设置“数组长” 为 NULL。运行 insmod,不同参数用空格隔开

( 5)模块导出符号(可选)。内核模块可以导出符号( symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值