创建两个ko, A.ko B.ko
A.ko中的函数要被B.ko所引用;
在A.ko中使用 EXPORT_SYMBOL_GPL(func);
在B.ko中使用 func 编译和加载会报错;
方法如下:
1、将 A.ko编译进内核, 任意目录的makefile中添加 obj -y += A.ko
obj -m += B.ko 如果A,B不在一个目录会报错
方法一:
拷贝A目录中的Module.symvers 到B目录中,之后编译B.ko =========》 测试通过
方法二:
在B的makefile中添加如下语句:
KBUILD_EXTRA_SYMBOLS += /home/chengjian/code/bsp_rru/testOM/BSP_RRU/modules/test_proc/Module.symvers
export KBUILD_EXTRA_SYMBOLS
示例A:
环境: Zynq UltraScale+ zcu102 2018.3
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/uaccess.h>
#define MYLOG_BUF_LEN 1024
/* 创建环形缓冲区
*/
static char tmp_buf[MYLOG_BUF_LEN];
static char mylogBuf[MYLOG_BUF_LEN];
static int mylog_r = 0;
static int mylog_w = 0;
static int mylog_r_tmp = 0;
DECLARE_WAIT_QUEUE_HEAD(mylog_wait);
/* 读取和写入的位置一致表示 当前缓冲区是空的 */
int isEmpty_mylogbuf(void)
{
return (mylog_r == mylog_w);
}
/* 假设当前r=0 如果w是1023 */
int isFull(void)
{
if ((mylog_w == (MYLOG_BUF_LEN - 1)) && (mylog_r == 0))
{
return 0; //没有满
}else if (mylog_w == MYLOG_BUF_LEN && (mylog_r == 0))
{
return 1;
}else
return (mylog_r == (mylog_w+1)%MYLOG_BUF_LEN);
}
static void mylog_putc(char c)
{
if (isFull())
{
mylog_r = (mylog_r + 1)%MYLOG_BUF_LEN;
if((mylog_r_tmp + 1)% MYLOG_BUF_LEN == mylog_r)
mylog_r_tmp= mylog_r;
}
mylogBuf[mylog_w] = c;
mylog_w = (mylog_w+1)%MYLOG_BUF_LEN;
wake_up_interruptible(&mylog_wait);
}
/*
*从循环队列中读字符
*输入:*p 单位:1byte
*输出:1表示成功
*/
static int mylog_getc(char *p)
{
if (isEmpty_mylogbuf())
{
return 0;
}
*p = mylogBuf[mylog_r_tmp];
mylog_r_tmp = (mylog_r_tmp + 1) % MYLOG_BUF_LEN;
return 1;
}
/*
*调用myprintk,和printf用法相同
*/
int myprintk(const char *fmt, ...)
{
va_list args;
int i;
int j;
va_start(args, fmt);
i= vsnprintf(tmp_buf, INT_MAX, fmt, args);
va_end(args);
for (j = 0; j < i; j++)
mylog_putc(tmp_buf[j]);
return i;
}
static ssize_t mymsg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int error ;
char c;
int i=0;
printk("mymsg_read \n");
if ((file->f_flags & O_NONBLOCK) && !isEmpty_mylogbuf())
return -EAGAIN;
error = wait_event_interruptible(mylog_wait, !isEmpty_mylogbuf()); //当不是非空的 时候等待;
for (i=0; i<count; i++)
{ mylog_getc(&c);
error = __put_user(c, buf);
buf++;
}
if (!error)
error = i;
return error;
}
static const struct file_operations proc_mymsg_operations = {
.read = mymsg_read,
};
static int mymsg_init(void)
{
printk("mymsg_init\n");
proc_create("mymsg", S_IRUSR, NULL, &proc_mymsg_operations);
return 0;
}
static void mymsg_exit(void)
{
remove_proc_entry("mymsg", NULL);
}
/*声名到内核空间*/
EXPORT_SYMBOL_GPL(myprintk);
module_init(mymsg_init);
module_exit(mymsg_exit);
MODULE_LICENSE("GPL");
obj-m += mymsg.o
B.ko
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
extern int myprintk(const char *fmt, ...);
int first_drv_open (struct inode * node, struct file * fp)
{
myprintk("first_drv_open cj \n");
return 0;
}
ssize_t first_drv_write (struct file *f, const char __user *buf, size_t cnt , loff_t * seek)
{
myprintk("first_drv_write cj \n");
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量*/
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
myprintk("first_drv_init\n");
major= register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv"); // 卸载
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
obj-m += testfirst.o
KERN_DIR=/home/.../....../kernel/linux-xlnx-xilinx-v2018.3/
KBUILD_EXTRA_SYMBOLS += /home/.../modules/test_proc/Module.symvers
export KBUILD_EXTRA_SYMBOLS
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
rm -f *.o
rm -f *.symvers
rm -f *.order
rm -f *.ko
rm -f *.mod.*
rm -f *.cmd
rm -rf .testfirst
rm -f .testfirst.ko.cmd
rm -f .testfirst.mod.o.cmd
rm -f .testfirst.o.cmd
加载驱动后结果:
dmesg 查看: