Linux模块基础、带参数模块、内核模块符号导出
驱动模块基础格式:
#include <linux/module.h>
#include <linux/kernel.h>
static int __init helloworld_init(void){
printk(KERN_EMERG "helloworld init!\r\n");
return 0;
}
static void __exit helloworld_exit(void){
printk(KERN__EMERG "helloworld exit\r\n");
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("LYT");
编写Makefile
export ARCH=arm64 #设置ARCH变量为arm64
export CROSS_COMPILE=aarch64-linux-gnu- #设置交叉编译器前缀
obj -m += helloworld.o #helloworld要与.c文件名称一致,将文件以模块形式编译
KDIR :=/home/lishu/rk3568/kernel #内核源码所在虚拟机ubuntu的实际路径
PWD ?= $(shelll pwd) #获取当前目录的变量
all:
make -C $(KDIR) M=$(PWD) modules #make操作 #进入内核源码路径,把当前路径下的代码编译成模块
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
最终生成helloworld.ko文件。
模块加载与卸载
insmod helloworld.ko
rmmod helloworld
lsmod
带参驱动模块:
#include <linux/init.h>
#include <linux/module.h>
static int number;
static char *name;
static int pra[8];
static char strl[10];
static int n_para;
module_para(number,int,S_IRUGO);
module_param(name,charp,S_IRUGO);
module_param_array(para,int ,&n_para,S_IRUGO);
module_param_string(str,str1,sizeof(str1),S_IRUGO);
static int __init parameter_init(void){
static int i;
printk(KERN_EMERG "%d\n",number);
printk(KERN_EMERG "%s\n",name);
for(i=0;i<n_para;i++)
{
printk(KERN_EMERG "para[%d] : %d \n",i,para[i]);
}
printk(KERN_EMERG "%s\n",str1);
retrun 0;
}
static void __exit parameter_exit)(void)
{
printk(KERN_EMERG "parameter_exit\n");
}
module_init(parameter_init);
module_exit(parameter_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("LYT");
insmod parameter.ko number=100 name=“lyt” para=0,1,2,3,4,5,6,7 str=“itop”
内核提供的传参宏:
#define module_param(name,type,perm) module_param_named(name,name,type,perm)
#define module_param_array(name,type,nump,perm)
#define module_param_string(name,string,len,perm)
- name:模块参数的名称;外部传入的参数名,即加载模块时的传入值
- type:模块参数的数据类型
- perm:模块参数的访问权限
- nump:传递数组元素的个数
- string:内部的变量名,即程序内定义的参数名
- len:以string命名的buffer大小(可以小于buffer的大小,但是没意义)
type可以取以下值:
boot:布尔型
inbool:布尔反值
charp:字符指针 (相当于char* ,不超过1024字节的字符串)
short:短整型
ushort:无符号短整型
int:整型
uint:无符号整型
long:长整型
ulong:无符号长整型
perm表示该参数所对应的文件节点的属性,可以用宏定义 or 数字
#define S_IRUSR 00400 /*文件所有者可读*/
#define S_IWUSR 00200 /*文件所有者可写*/
#define S_IXUSR 00100 /*文件所有者可执行*/
#define S_IRGRP 00040 /*与文件所有者同组的用户可读*/
#define S_IWGRP 00020 /*与文件所有者同组的用户可写*/
#define S_IXGRP 00010 /*与文件所有者同组的用户可执行*/
#define S_IROTH 00004 /*与文件所有者不同组的用户可读*/
#define S_IWOTH 00002 /*与文件所有者不同组的用户可写*/
#define S_IXOTH 00001 /*与文件所有者不同组的用户可执行*/
内核模块符号导出:
调用其他模块中的函数、变量。
calculate.c
#include <linux/init.h>
#include <linux/module.h>
static int c;
static int d;
module_param(c,int,S_IRUGO);//驱动模块传参
module_param(d,int S_IRUGO);
extern int num; //导入int类型变量num
extern int add(int a,int b); //导入函数add
static int __init calculate_init(void)//驱动入口函数
{
static int sum;
printk("num = %d\n",num);//打印num值
sum = add(c,d);//使用add函数进行3+4的运算
printk("sum = %d\n",sum);//打印add函数的运算值
return 0;
}
static void __exit calculate_exit(void)//驱动出口函数
{
printk("Goodbye hello module\n");
}
module_init(hello_init);//注册入口函数
module_exit(hello_exit);//注册出口函数
MODULE_LICENSE("GPL");//同意GPL开源协议
MODULE_AUTHOR("lyt");//作者信息
mathmodule.c
#include <linux/init.h>
#include <linux/module.h>
int num = 10;//定义参数num
EXPORT_SYMBOL(num);//导出参数num
int add(int a,int b)//定义数学函数add
{
return a+b;
}
EXPORT_SYMBOL(add);//导出数学函数add
static int __init math_init(void)//驱动入口函数
{
printk("math_module init\n");
return 0;
}
static void __exit math_exit(void)//驱动出口函数
{
printk("math_module exit\n");
}
module_init(math_init);//注册入口函数
module_exit(math_exit);//注册出口函数
MODULE_LICENSE("GPL");//同意GPL开源协议
MODULE_ATHOR("lyt");//作者信息
Makefile
export ARCH=arm64 #设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu- #交叉编译器前缀
obj-m := mathmodule.o
obj-m += calaulate.o
KDIR := /home/lishu/rk3568/kernel #内核目录
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
insmod mathmodule.ko
insmod calaulate.ko c=2 d=3