简介
- 本文主要来讲讲Linux内核驱动中,EXPORT_SYMBOL()宏定义的用法。
- 在阅读的Linux内核驱动源码的时候,我们会发现很多的函数带有EXPORT_SYMBOL()宏定义。
- 从这个宏定义的理解为输出符号。那么他究竟有什么作用。
EXPORT_SYMBOL()宏定义作用
- EXPORT_SYMBOL宏定义定义的函数或者符号将对内核代码公开,不用修改内核代码就在其他的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。
使用方法
在模块函数定义之后使用"EXPORT_SYMBOL(函数名)"来导出。
static int rice_func(void) { return 0; } EXPORT_SYMBOL(rice_func);
在调用该函数的另外一个模块中使用extern对之声明。
extern int rice_func(void);
- 先加载定义该函数的模块,然后再加载调用该函数的模块,先后顺序必须注意。
实验
编写代码
- 编写两个模块:rice_export.ko 和 rice_import.ko,其中:
- rice_export.ko:导出定义的函数
- rice_import.ko:调用导出的函数
导出函数模块的代码(rice_export.c)
- 导出函数为:rice_drv_export,函数含义:外部输入一个字符串,然后打印出来
#include "rice_export.h"
#define CLASS_NAME "rice_export"
#define DEVICE_NAME "rice_export"
typedef struct {
int major_number;
struct device *device;
struct class *class;
} Rice_Driver;
Rice_Driver rice_drv;
static int rice_drv_export(char *name) {
printk(KERN_ALERT "Rice Export: %s\n", name);
return 0;
}
EXPORT_SYMBOL(rice_drv_export);
static int __init rice_export_init(void) {
rice_drv.major_number = register_chrdev(0, DEVICE_NAME, NULL);
if (rice_drv.major_number < 0) {
printk(KERN_ALERT "Register fail!!\n");
return rice_drv.major_number;
}
printk(KERN_ALERT "Registe success, major number is %d\n", rice_drv.major_number);
rice_drv.class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(rice_drv.class)) {
unregister_chrdev(rice_drv.major_number, DEVICE_NAME);
return PTR_ERR(rice_drv.class);
}
rice_drv.device = device_create(rice_drv.class, NULL, MKDEV(rice_drv.major_number, 0), NULL, DEVICE_NAME);
if (IS_ERR(rice_drv.device)) {
class_destroy(rice_drv.class);
unregister_chrdev(rice_drv.major_number, DEVICE_NAME);
return PTR_ERR(rice_drv.device);
}
printk(KERN_ALERT "rice export ko init!!\n");
return 0;
}
static void __exit rice_export_exit(void) {
device_destroy(rice_drv.class, MKDEV(rice_drv.major_number, 0));
class_unregister(rice_drv.class);
class_destroy(rice_drv.class);
unregister_chrdev(rice_drv.major_number, DEVICE_NAME);
printk(KERN_ALERT "rice export ko exit!!\n");
}
module_init(rice_export_init);
module_exit(rice_export_exit);
MODULE_AUTHOR("RieChen");
MODULE_LICENSE("GPL");
电泳函数模块的代码(rice_import.c)
调用函数声明:extern int rice_drv_export(char * name);,含义:声明外部函数
#include "rice_import.h" #define CLASS_NAME "rice_import" #define DEVICE_NAME "rice_import" typedef struct { int major_number; struct device *device; struct class *class; } Rice_Driver; Rice_Driver rice_drv; extern int rice_drv_export(char *name); static int __init rice_import_init(void) { rice_drv.major_number = register_chrdev(0, DEVICE_NAME, NULL); if (rice_drv.major_number < 0) { printk(KERN_ALERT "Register fail!!\n"); return rice_drv.major_number; } printk(KERN_ALERT "Registe success, major number is %d\n", rice_drv.major_number); rice_drv.class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(rice_drv.class)) { unregister_chrdev(rice_drv.major_number, DEVICE_NAME); return PTR_ERR(rice_drv.class); } rice_drv.device = device_create(rice_drv.class, NULL, MKDEV(rice_drv.major_number, 0), NULL, DEVICE_NAME); if (IS_ERR(rice_drv.device)) { class_destroy(rice_drv.class); unregister_chrdev(rice_drv.major_number, DEVICE_NAME); return PTR_ERR(rice_drv.device); } printk(KERN_ALERT "rice import ko init!!\n"); rice_drv_export("RiceChen"); return 0; } static void __exit rice_import_exit(void) { device_destroy(rice_drv.class, MKDEV(rice_drv.major_number, 0)); class_unregister(rice_drv.class); class_destroy(rice_drv.class); unregister_chrdev(rice_drv.major_number, DEVICE_NAME); printk(KERN_ALERT "rice import ko exit!!\n"); } module_init(rice_import_init); module_exit(rice_import_exit); MODULE_AUTHOR("RieChen"); MODULE_LICENSE("GPL");
编译运行
- 将两个模块编译完,push到板子,先加载导出模块--rice_export.ko,然后再加载调用模块--rice_import.ko
- 运行结果:
首发:Rice 嵌入式开发技术分享
作者:RiceDIY
推荐阅读
- RT-Thread Nano如何适配ADC设备API
- RT-Thread Nano如何适配I2C设备API,并在RT-Thread Nano使用软件包
- RT-Thread Nano如何适配pin设备API,并在RT-Thread Nano使用软件包
更多嵌入式技术干货请关注Rice 嵌入式开发技术分享