一、内核模块头文件
#include <linux/module.h>:包含内核模块信息声明的相关函数。
#include <linux/init.h>:包含了 module_init( )
和 module_exit( )
函数的声明。(module_init解析)
#include <linux/kernel.h>:包含内核提供的各种函数,如 printk
函数。
二、内核模块打印函数 printk
printf:glibc实现的打印函数,工作于用户空间 / 目态。
printk:内核模块无法使用glibc库函数,内核自身实现的一个类printf函数,但是需要指定打印等级(若不指定就是用默认的打印等级)。
printk 打印等级
相关定义如下:
/* include/linux/kern_levels.h */
#define KERN_SOH "\001" /* ASCII Start Of Header */
...
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
注意表示等级的数字越小,则优先级越高。
只有 prink 指定的打印等级
比 console 当前等级
小才能打印到console,等于都不行。
查看当前系统printk打印等级:cat /proc/sys/kernel/printk
(不手动去修改的话就是依次打印这个数组的4个成员值)。
//kernel/printk/printk.c
int console_printk[4] = {
/* console_loglevel 当前控制台日志级别 */
CONSOLE_LOGLEVEL_DEFAULT,
/* default_message_loglevel 默认消息日志级别 */
MESSAGE_LOGLEVEL_DEFAULT,
/* minimum_console_loglevel 最小的控制台级别 */
CONSOLE_LOGLEVEL_MIN,
/* default_console_loglevel 默认控制台日志级别 */
CONSOLE_LOGLEVEL_DEFAULT,
};
/ #
/ # cat /proc/sys/kernel/printk
7 4 1 7
/ #
/proc/sys/kernel/printk
Printk 共有4个参数
Cat /proc/sys/kernel/printk
7 4 1 7
- (1)第一个参数 7表示小于7优先级消息才会被输出到控制台。
(2)第二个参数4 表示默认的printk消息优先级别,即printk(“hell world”);优先级为4, 由于4<7,故可以被打印到控制台。
(3)第三个参数1 表示可接收的最高优先级,当printk disable控制台输出时,设置第一个参数为1,但是,从内核等级来看,还有优先级0,这个是printk最高级优先级,一般用于内核严重消息打印。比如内存错误或者 watchdog reset.也可以设置第一个和第三个参数为0
(4)第四个参数7 默认控制台优先级,即第一个参数的默认优先级。
修改上述四个默认优先级的方法:
1、修改源码
2、修改 /proc/sys/kernel/printk
文件
将 要设置的值
写入到 /proc/sys/kernel/printk
中。我们要先 cat /proc/sys/kernel/printk
来看一下这个文件中都有什么值。然后我们再写入。
其中这里的格式为:控制台的日志级别、默认消息日志级别、最小控制台日志级别 和 默认控制台日志级别。
而我们要设置的就是第一个控制台的日志级别。我们通过 echo “W X Y Z” > /proc/sys/kernel/printk
将我们想要设置的四个值写入到 /proc/sys/kernel/printk
中。
通过 dmesg
可以查看 printk 的所有打印信息
内核log缓冲区大小有限制,缓冲区数据可能被冲掉
- 补充
printk函数 相关
三、例程
1、内核模块源码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
/*
* __init 是把该函数放到统一的段里,所有内核模块的入口函数都存到这个指定的段里,
* 等到内核调用完成就释放这个段,以此节约内存空间
* __exit 同理
*/
static int __init hello_init(void)
{
printk(KERN_EMERG "[KERN_EMERG] Hello World Module Init\n");
/* 也可以手动替换上面的宏如下,效果是一样的
* printk("\001" "1""[ KERN_EMERG ] Hello World Module Init\n");
*/
printk( "[default] Hello World Module Init\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("[default] goodbye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("hello world module");
MODULE_ALIAS("test_module");
2、Makefile
# 内核源码所在路径
KERNELDIR := /home/jl/linux/imx6ull/alientek_made_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
# 指定编译出的内核模块的名字
obj-m := test.o
build: kernel_modules
# -C 选项来指定子Makefile,借助内核Makefile执行modules伪目标来编译我们的模块
# $(CURDIR):Makefile默认变量,值为当前目录所在路径
# M=$(CURRENT_PATH) 告诉内核 模块源码所在位置
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
加一个 copy伪目标,省得每次都要手动复制
KERNELDIR := /home/jl/linux/imx6ull/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
#KERNELDIR := /home/jl/linux/imx6ull/alientek_made_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := test.o
# 注意这个模式替换操作
res := $(patsubst %.o,%.ko,$(obj-m))
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
copy:
sudo cp $(res) ../../../nfs/rootfs/lib/modules/4.1.15/ -rf