多文件编译
对于比较复杂的驱动程序,常常会把它的功能进行拆分,由不同的文件实现,这样也能进行并行开发,缩短开发周期。多文件编译的简单例子如下:
mod.c:
//mod.c
#include <linux/init.h>
#include <linux/module.h>
#include "ext.h"//其他文件的头文件ext.h
static int hello_init(void)
{
printk("Hello World.\n");
xxx_init();//调用其他文件的函数,实现在ext.c
return 0;
}
static void hello_exit(void)
{
printk("Goodbye World.\n");
xxx_exit();//调用其他文件的函数,实现在ext.c
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ono Zhang");
MODULE_DESCRIPTION("A simple Hello World Module");
ext.h:
//ext.h
#ifndef __EXT_H__
#define __EXT_H__
void xxx_init(void);
void xxx_exit(void);
#endif
ext.c
//ext.c
#include <linux/kernel.h>
void xxx_init(void)
{
printk("Init extern.\n");
}
void xxx_exit(void)
{
printk("Exit extern.\n");
}
Makefile
obj-m = mymod.o
mymod-objs = mod.o ext.o
KDIR = /home/linux-kernel
PWD =$(shell pwd)
modules:
$(MAKE) -C $(KDIR) M=$(PWD) modules
其中KERNELDIR是内核源码目录所在的路径。
新增了ext.c及ext.h,在里面定义了xxx_init机xxx_exit函数。在Makefile里面的第2行“mymod-objs=mod.o ext.o”表示mymod模块是由mod.o和ext.o两个目标文件共同生成的,最后生成的文件为mymod.ko。
加载
1、若编辑进内核,见《内核模块编写及编译》的编译章节,则linux启动时,会自动加载模块,调用的函数栈如下:
init/main.c
start_kernel -> reset_init -> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
|
|->static int __ref kernel_init(void *unused)
|
|-> kernel_init_freeable( )
|
|-> do_basic_setup();
|
|——> do_initcalls();
|
|->do_initcall_level()
2、若单独编译 *.ko,见《内核模块编写及编译》的编译章节,需要使用命令进行加载,加载命令如下:
insmod mymod.ko //mymod.ko为上节编译出的ko文件
也可以使用:
modprobe mymod //mymod为上节编译出的模块名
modprobe和insmod的区别,我们在模块依赖章节介绍。
其调用栈为:
kernel/module.c
finit_module-->static int load_module(struct load_info *info, const char __user *uargs,int flags)
|
|->static int do_init_module(struct module *mod)
参数传递
在应用开发中,可以给main()函数传递参数,这样应用程序就能知道最初的控制参数是什么,当然也可以选择不向应用程序传递参数。在驱动开发中,也可以向内核模块传递参数,需要使用宏module_param或者module_param_array。
module_param(参数名,参数类型,参数权限)
module_param_array (参数名, 参数类型, 数组元素个数指针, 参数权限);
另外使用MODULE_PARM_DESC来描述参数作用。
MODULE_PARM_DESC(参数名,参数描述)
内核支持参数类型有bool、invbool、charp(字符串指针)、short、int、long、ushort、uint、ulong。这些类型又可以复合成对应的数组类型。
参考例子如下:
#include <linux/init.h>
#include <linux/module.h>
int a = 0;
int b[10] = {1, 2, 3};
int c = 0;
char *d = "456ab";
module_param(a, int, 0664);
module_param_array(b, int, &c, 0664);
module_param(d, charp, 0664);
static int hello_init(void)
{
int i = 0;
printk("Hello World.\n");
printk("a = %d\n", a);
for (i = 0; i < 10; i++)
{
printk("b[%d] = %d\n", i, b[i]);
}
printk("c = %d\n", c);
printk("d is %s\n", d);
return 0;
}
static void hello_exit(void)
{
printk("Goodbye World.\n");
return;
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ono Zhang");
MODULE_DESCRIPTION("A simple Hello World Module");
加载:
1、若编译进内核,在bootloader中可向内置的模块传递参数,例如可以在bootargs中设置模块名.参数名=值的形式给该内置的模块传递参数。
2、若单独编译 *.ko,使用insmod xxx.ko 参数名=值,如上例子为:
sudo insmod mod.ko a=10 b=12,3,5 d=ws
打印回显为:
[ 4729.691707] a = 10
[ 4729.691708] b[0] = 12
[ 4729.691709] b[1] = 3
[ 4729.691710] b[2] = 5
[ 4729.691710] b[3] = 0
[ 4729.691711] b[4] = 0
[ 4729.691712] b[5] = 0
[ 4729.691712] b[6] = 0
[ 4729.691713] b[7] = 0
[ 4729.691713] b[8] = 0
[ 4729.691714] b[9] = 0
[ 4729.691714] c = 3
[ 4729.691715] d is ws
加载完成后,希望修改它的值,需要在sys文件系统中找到对应的文件节点/sys/module/模块名/parameters/参数名,可以使用echo和cat来修改及查询参数的值。
如:
cat /sys/module/mod/parameters/a
10
echo 20 > /sys/module/mod/parameters/a
cat /sys/module/mod/parameters/a
20
模块依赖
模块依赖是指A模块引用了B模块的函数或者全局变量,A模块就依赖于B模块,B模块就是被依赖的模块,加载模块时,需要先加载B模块,再加载A模块。另外A模块使用B模块的函数或者全局变量,需要在B模块中导出,否则A模块会编译出错,导出使用宏EXPORT_SYMBOL。
EXPORT_SYMBOL(函数名或者变量名)
标签:int,void,ext,module,参数传递,init,模块,内核模块,加载
From: https://blog.csdn.net/qq_40896451/article/details/140592655