linux可以将功能编译成模块,在需要使用的时候加到内核。从而实现动态,这不单单决绝了内核文件过大的问题,而且但组件发送变化只需要重新编译模块就可以了。加载过程不需要重启电脑这也是模块最大的好处,十分方便调试!功能模块一般要么编译进内核要么编译成模块。如果编译进内核会造出内核文件变大而且修改不变,发现变化时都必须重新编译内核,但是优点是一直都存在内核中。我觉得也不算是优点吧。编译成模块则在使用时加载就可以实现其功能,并不一直存在内核中。就是使用时加载。特点不编译进内核,在运行时期动态安装、卸载。
模块框架:
//特定头文件
#include <linux/init.h>
#include <linux/moudule.h>
//许可证
MODULE_LICENSE(“GPL”);
//初始化函数
static int __init hello_init(void)
{
return 0;
}
//退出函数
static void __exit hello_exit(void)
{
return ;
}
//两个指定初始化函数、退出函数的宏
module_init(hello_init);
module_exit(hello_exit);
makefile框架:
#第一次在当前目录没定义,在linux代码运行此时有定义并且导出obj-m
ifneq ($(KERNELRELEASE),)
#obj-m代表模块
obj-m := hello.o
else
#指定linux代码目录
KDIR := /lib/module/xxx/build
all:
make –C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
insmod:加载模块
rmmod:卸载模块
lmmod:列出所加载的函数
c语言函数与模块区别
1,c语言指定main函数执行并且执行完后就消失了。但是模块一旦加载就一直存在, 知道将其卸载。通过两个宏指定进出入口。
2,c语言可以直接编译,但是模块就必须结合linux代码、makef来进行编译
hello例子(module_param)
#include <linux/init.h>
#include <linux/module.h>
//使用参数,必须点定义参数module_param再声明参数
//module_param(, , )第二个参数为类型可以为int,bool,charp.第三个参数为全限
//假如不传递参数默认为1
int count = 1;
module_param(count, int, S_IRUGO);
MODULE_LICENSE(“GPL”);
static int __init hello_init(void)
{
for(;count > 0;count--)
printk(KERN_ERR “hello,Dsliang!\n”);
reutn 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ERR “GoodBye!\n”);
return ;
}
//指定hello_init函数,当调用ismod加载此模块时自动调用hello_init函数
module_init(hello_init);
//指定hello_exit函数,当调用rmmod加载此模块时自动调用hello_exit函数
module_exit(hello_exit);
symbol例子(EXPORT_SYMBOL)
//symbol.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE(“GPL”);
extern void myprint(char *ptr);
static int __init symbol_init(void)
{
myprint(“hello!Dsliang”);
return 0;
}
static void __exit symbol_exit(void)
{
myprint(“GoodBye!”);
return ;
}
module_init(symbol_init);
module_exit(symbol_exit);
//symbol_export.c
#include <linux/init.h>
#include <inux/module.h>
MODULE_LICENSE(“GPL”);
void myprint(char *p)
{
printk(KERN_ERR “%s”, p);
return ;
}
static int __init symbol_export_init(void)
{
return 0;
}
static void __exit symbol_export_exit(void)
{
return ;
}
module_init(symbol_export_init);
module_exit(symbol_export_exit);
EXPORT_SYMBOL(myprint);
!!!主要一定要再同一个目录下编译才能
//obj-m = symbol.o symbol_export.o
通过查看/proc/kallsyms可以看到对于的符号有没在系统
EXPORT_SYMBOL(xxx)à添加xxx函数关联到/proc/kallsyms文件中
假如没有这符号,加载模块会出现”unknow symbol in module”并且无法加载
假如有不匹配问题(当前运行内核没其代码):
1, 可以使用modprobe –force-modversion 强制加载(不建议)
2, 使用同一版本内核代码进行编译
printk与printf
printk是内核函数,printf是c库封装了printk函数。在内核只能使用printk。
printk函数有优先级”<0>” “<1>” ”<2>” “<3>” ”<4>” “<5>” ”<6>” ”<7>”优先级越高值越低
不加默认为KERN_WARNING,假如优先级低于(Console_loglevel)则不输出终端。但是无论如何在 /var/log/message都会记录所有输出
通过查看/proc/sys/kernel/printk(可以对其修改,但是重启复位)可以看到对应的值
4 4 1 7
控制台 默认值