ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Linux 驱动小白教程】四、模块符号导出 EXPORT_SYMBOL + 模块传参完整实操

2026/8/15 23:16:16 拓冰建站 浏览量
【Linux 驱动小白教程】四、模块符号导出 EXPORT_SYMBOL + 模块传参完整实操 接上篇内核 Kconfig Makefile 配置编译全流程前面我们学会了驱动模块编译、Kconfig 配置。本篇讲解两个内核模块高频知识点模块符号导出 EXPORT_SYMBOL、模块命令行传参。实际项目多模块联合开发、调试参数不用重新编译驱动全靠这两套 API面试经常考。一、模块符号导出 EXPORT_SYMBOL1. 什么是符号导出内核每个.ko模块有自己的函数、全局变量。默认情况下A 模块的函数B 模块无法直接调用。EXPORT_SYMBOL()作用把本模块的函数 / 全局变量符号导出到内核符号表其他内核模块就可以调用这个函数。区分两个宏EXPORT_SYMBOL(sym)导出符号GPL 与非 GPL 模块都可以使用该符号。EXPORT_SYMBOL_GPL(sym)只允许声明MODULE_LICENSE(GPL)的模块使用非 GPL 模块调用会内核告警。项目开发优先使用 EXPORT_SYMBOL_GPL。示例代码 drv_a.c提供函数导出符号#includelinux/module.h#includelinux/init.h// 待导出函数intdrv_add(inta,intb){returnab;}// 导出符号其他ko可以调用drv_addEXPORT_SYMBOL_GPL(drv_add);staticint__initdrv_a_init(void){printk(drv_a 模块加载成功\n);return0;}staticvoid__exitdrv_a_exit(void){printk(drv_a 模块卸载成功\n);}module_init(drv_a_init);module_exit(drv_a_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(ZKX);MODULE_DESCRIPTION(导出符号模块A);drv_b.c外部模块调用 drv_a 导出的函数#includelinux/module.h#includelinux/init.h// 声明外部符号告诉编译器这个函数在别的模块externintdrv_add(inta,intb);staticint__initdrv_b_init(void){intretdrv_add(10,20);printk(drv_b调用drv_add结果%d\n,ret);return0;}staticvoid__exitdrv_b_exit(void){printk(drv_b 模块卸载成功\n);}module_init(drv_b_init);module_exit(drv_b_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(ZKX);MODULE_DESCRIPTION(使用外部符号模块B);⚠️实操顺序非常重要编译两个 kodrv_a.ko、drv_b.ko先加载提供符号的模块 drv_a.kosudoinsmod drv_a.ko# 再加载使用符号的模块sudoinsmod drv_b.ko查看打印dmesg卸载顺序反过来先 rmmod drv_b再 rmmod drv_a踩坑先加载 drv_b 会报错Unknown symbol drv_add。符号还没导出就调用。符号存放内核符号表/proc/kallsyms可以 grep 查看导出符号cat/proc/kallsyms|grepdrv_add多模块编译注意编译时需要Module.symversdrv_a 编译生成的 Module.symvers 拷贝到 drv_b 编译目录否则编译阶段报未定义警告。二、模块传参module_param 系列 API应用场景加载模块insmod/modprobe的时候命令行直接给驱动传入参数不需要重新编译驱动调试非常方便。头文件必须引入#includelinux/moduleparam.h四个核心 APImodule_param(name, type, perm)普通单个参数module_param_array(name, type, nump, perm)数组参数module_param_string(name, string, len, perm)字符串参数MODULE_PARM_DESC(_parm, desc)参数说明modinfo 可以看到参数说明name变量名驱动内部定义的全局变量type支持类型byte, short, ushort, int, uint, long, ulong, bool, charpcharp字符指针字符串permsysfs 文件权限/sys/module/xxx/parameters/0不在 sysfs 创建文件只能命令行传参0644rw‑r‑‑r‑‑ 用户可读root 可写常用 0644注意perm 不能写 0777内核不允许。numpmodule_param_array 专用输出实际传入数组元素个数传 NULL 不接收。lenmodule_param_string字符串缓冲区最大长度。完整可运行示例代码 param_drv.c#includelinux/module.h#includelinux/init.h#includelinux/moduleparam.h// 1.普通单个参数staticintdev_id1;module_param(dev_id,int,0644);MODULE_PARM_DESC(dev_id,设备IDint类型默认1);staticbool enable_logfalse;module_param(enable_log,bool,0644);MODULE_PARM_DESC(enable_log,是否开启日志打印bool);// 2.数组参数staticintbuf[8];staticintbuf_cnt;module_param_array(buf,int,buf_cnt,0644);MODULE_PARM_DESC(buf,int数组最多8个元素);//3.字符串参数staticchardev_name[32];module_param_string(dev_name,dev_name,sizeof(dev_name),0644);MODULE_PARM_DESC(dev_name,设备名字符串);staticint__initparam_drv_init(void){inti;printk(模块传参打印\n);printk(dev_id %d\n,dev_id);printk(enable_log %d\n,enable_log);printk(dev_name %s\n,dev_name);printk(数组实际传入元素个数%d\n,buf_cnt);for(i0;ibuf_cnt;i){printk(buf[%d] %d\n,i,buf[i]);}return0;}staticvoid__exitparam_drv_exit(void){printk(param_drv 模块卸载\n);}module_init(param_drv_init);module_exit(param_drv_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(ZKX);MODULE_DESCRIPTION(模块传参demo);实操命令编译生成param_drv.ko1. insmod 传递参数# 传参语法 insmod xxx.ko 变量名值 数组1,2,3,4sudoinsmod param_drv.kodev_id100enable_log1dev_namemydevbuf10,20,30查看打印dmesg2. modprobe 传参modprobe 传参不加 ko 后缀sudomodprobe param_drvdev_id200dev_nametestmodbuf1,3,53. modinfo 查看参数描述MODULE_PARM_DESC 生效modinfo param_drv.ko可以看到 parm 一栏每个参数的说明文字。4.sysfs 下查看参数perm0644 才生成ls/sys/module/param_drv/parameters/cat/sys/module/param_drv/parameters/dev_id卸载模块sudormmod param_drv关键注意点参数必须定义为全局静态变量 static局部栈变量不能用于 module_param。module_param_string拷贝字符串到缓冲区charp类型 module_param 只是指针指向用户传入字符串不要修改。数组module_param_array超出数组定义长度的元素会被丢弃。perm0sysfs 不生成参数文件只能加载时赋值运行时不能改0644 运行时 root 可以修改 sysfs 文件动态改参数。MODULE_PARM_DESC 仅仅是注释信息给 modinfo 读取不影响代码逻辑。本篇知识点总结模块符号导出宏作用EXPORT_SYMBOL(sym)导出符号任意模块都可以调用EXPORT_SYMBOL_GPL(sym)仅 GPL 协议模块可调用项目推荐实操要点先加载导出符号的模块再加载使用符号的模块卸载顺序反过来查看符号cat /proc/kallsyms。模块传参API功能#include linux/moduleparam.h模块传参头文件必须包含module_param(name,type,perm)单个变量传参module_param_array(name,type,nump,perm)数组传参nump 返回实际元素数量module_param_string(name,string,len,perm)字符串拷贝到缓冲区MODULE_PARM_DESC(parm,desc)参数描述modinfo 查看传参类型int、bool、charp(字符串指针)perm 常用0644或者0insmod/modprobe 加载时变量值传参。以上为作者知识总结教学不易点赞收藏达标持续更新