欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux驱动之模块化编程

程序员文章站 2022-03-22 16:42:47
...

概念

区别于直接编译源码,可以快速编译功能代码,以模块的形式添加到linux系统中,便于测试,而不是反复修复源码进行编译。

步骤

linux驱动之模块化编程

第1步

包含头文件,并通过MODULE_LICENSE("GPL")告诉内核模块遵从GPL协议,这个事情必须要做。MODULE_AUTHOR("CYG")指定模块的作者,可不写。

第2步

编辑模块的入口函数,一般叫xxx_init,在模块加载到Linux内核时,Linux会调用该函数。

第3步

编辑模块的出口函数,一般叫xxx_exit,在模块卸载的时候释放资源。

第4步

内核通过module_init宏查找入口函数,用module_exit宏查找出口函数。

注意按该框架进行编程

printk

优先级

printk函数用法与printf类似,printf用于用户空间,printk用于内核空间。用printk函数时,内核会根据日志级别,可能把消息打印到当前控制台上。这些消息正常输出的前提是:日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。日志级别一共有8个,printk的日志级别定义如下(/linux/kernel.h):

linux驱动之模块化编程

没有指定日志级别的printk语句默认采用的级别是DEFAULT_MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING一个级别),可以通过 cat /proc/sys/kernel/printk查看系统默认的日志级别

linux驱动之模块化编程

ring buffer

在 Linux 刚启动内核初始化时,控制台设备还没有初始化的时候,printk 只会把信息输送到 ring buffer 中,等控制台设备初始化好后,再根据 ring buffer 中消息的优先级决定是否需要输送到控制台设备上。可以通过dmesgdmesg|tail读取 ring buffer(环形缓冲区)信息,通过dmesg -c清空 ring buffer。

模块编译

linux驱动之模块化编程

第1步

调用 Linux 源码树的 Makefile 进行收集编译一个模块所需要的信息

第2步

Linux 源码树的 Makefile 收集玩信息后,调用模块的 Makefile 获取需要编译成模块的.c文件,最后生成模块.ko

模块的 Makefile 实例如下:

obj-m := hello.o   
KERNELDIR := /home/fa/linux-3.4.y  
PWD := $(shell pwd)   

modules:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
modules_install:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  

make -C 表示调用指定路径下的 Makefile ,M 后边跟的是模块的绝对路径,modules 告诉 Makefile 要进行模块编译。当我们在调用 Linux 内核源码树顶层的 Makefile 时,找到的是顶层 Makefile 的modules目标,截取片段如下:

linux驱动之模块化编程

obj-m := hello.o,这里的obj=m告诉 Linux 源码树的顶层 Makefile 是动态编译(编译成模块),而不是编译进内核(obj-y),顶层Makefile会根据 hello.o 去找到 hello.c 文件。

模块的加载&卸载

$ insmod xxx.ko #加载,注意只有root超级用户权限才可以添加模块到内核
$ lsmod #查看模块
$ rmmod xxx.ko #卸载模块

实例

编写 hello 驱动

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

MODULE_LICENSE("Dual BSD/GPL");   

static int hello_init(void)   
{   
    printk(KERN_ALERT "Hello, world\n");   
    return 0;   
}   
static void hello_exit(void)   
{   
    printk(KERN_ALERT "Goodbye, cruel world\n");   
}   

module_init(hello_init);   
module_exit(hello_exit);  
//加载或卸载时会有打印
# Makefile
obj-m := hello.o   
KERNELDIR := /home/fa/linux-3.4.y  
PWD := $(shell pwd)   

modules:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
modules_install:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  
clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

注意 makefile 的格式,如果直接 copy 可能会有格式错误,最好使用 vim 检查一下

相关标签: 驱动