Linux内核程序的命令传递参数
- 明确:要想给Linux内核传递参数,必须遵循以下三个原则:
- 接收参数的内核变量必须是全局变量。
- 内核变量:此变量定义在内核空间,也就是此变量定义在内核程序内,也就是此变量的地址一定位于0xC0000000~0xFFFFFFFF.
- 接收参数的内核全局变量的数据类型一定是以下类型:
- 内核全局变量:明确其生命周期,当insmod安装内核程序时,内核会给他分配内存空间,永驻内存,直到rmmod从内核卸载。
- bool 、invbool:布尔类型。
- char、uchar
- short、ushort
- int、uint
- long、ulong
- charp(char*)
- 切记:内核要求变量不允许是浮点数,CPU核处理浮点数的速度特别慢,所以不能使用浮点数。
- 接收参数的内核全局变量接收参数之前必须用以下宏进行传参声明:
- module_param(name, type, perm);
- name:内核全局变量名。
- type:内核全局变量的数据类型。
- perm:内核全局变量的访问权限,一般用8进制来表示权限,不允许用可执行权限。
案例:编写内核程序,实现Linux内核程序的命令行传参。
- 上位机执行:
mkdir /opt/drivers/day01/3.0 cd /opt/drivers/day01/3.0 vim helloworld.c vim Makefile make cp helloworld.ko /opt/rootfs/home/drivers/
- 下位机测试:
cd /home/drivers echo 8 > /proc/sys/kernel/printk insmod helloworld.ko rmmod helloworld
安装内核程序时,传递参数
#include <linux/init.h> #include <linux/module.h> static int irq; static char *pstring; //传参声明 module_param(irq, int, 0664); module_param(pstring, charp, 0); //insmod时执行 static int helloworld_init(void) { printk("%s: irq = %d, pstring = %s \n", __func__, irq, pstring): return 0; } //rmmod时执行 static void helloworld_exit(void) { printk("%s: irq = %d, pstring = %s\n", __func__, irq, pstring); } module_init(helloworld_init); module_exit(helloworld_exit); MODULE_LICENSE("GPL"); #编写Makefile obj-m += helloworld.o all: make -C /opt/kernel SUBDIRS=$(PWD) modules clean: make -C /opt/kernel SUBDIRS=$(PWD) clean #使用执行: insmod helloworld.ko irq-250 pstring=china
//安装内核程序后,传递参数
#安装内核后,传递参数执行: #查看当前参数值: cat /sys/module/helloworld/parameters/irq #修改参数值: echo 2555 > /sys/module/helloworld/parameters/irq
总结:
- 如果权限非零,将来在/sys/…/目录下会生成一个跟变量名同名的文件,将来通过修改文件的值来间接的修改变量的值,将来安装以后也能再次传递参数。
- 如果权限为0,不会有同名的文件,此变量的传参只能在安装时传递参数,安装后不能再次传递参数。
Linux内核符合导出
- 明确:何为符号(symbol)
- 符合就是变量名或者函数名。
- 符号导出目的:为了让别人的程序能够访问调用。
- 回顾应用程序符号导出(多文件之间的访问,多文件,使用头文件声明和包含)
Linux内核符号导出(内核多文件之间的访问调用)
- 明确:Linux内核程序多文件之间的访问和应用程序一模一样,都需要声明、定义和调用,但是内核要求导出的变量或者函数需要显示的导出一下。
- 用以下宏导出即可:
- EXPORT_SYMBOL(变量名或者函数名);
- 或者——EXPORT_SYMBOL_GPL(变量名或者函数名);
- 前者导出的函数或者变量,不管调用的内核程序是否遵循GPL协议,这个内核程序都可以调用。
- 后者导出的函数或者变量只能给那些遵循GPL协议的内核程序访问。
- 内核程序遵循GPL协议的代码:MODULE_LICENSE(“GPL”);
案例:helloworld调用test的变量
- test.h
#ifndef __TEST_H #define __TEST_H //函数声明 extern void my_test(void); #endif
- test.c
#include <linux/init.h> #include <linux/module.h> //函数定义 void my_test(void) { printk("%s\n", __FUNCTION__); } //显式将函数进行导出 EXPORT_SYMBOL(my_test); MODULE_LICENSE("GPL");
- helloworld.c
#include <linux/init.h> #include <linux/module.h> #include "test.h" static int helloworld_init(void) { //调用test my_test(); printk("%s\n", __func__); return 0; } static void helloworld_exit(void) { //调用test my_test(); printk("%s\n", __func__); } module_init(helloworld_init); module_exit(helloworld_exit); MODULE_LICENSE("GPL");
- 编译调用:
vim Makefile obj-m += helloworld.o obj-m += test.o all: make -C /opt/kernel SUBDIRS=$(PWD) modules clean: make -C /opt/kernel SUBDIRS=$(PWD) clean #复制到下位机上: cp test.ko helloworld.ko /opt/rootfs/home/drivers
printk vs printf
- 相同点:
- 都是用于打印调试信息的。
- 用法一模一样。
- 不同点:
- printf 只能用于用户空间
printk 只能用于内核空间
printk 特点
- 能够指定打印输出级别,共8级。分别是:
- KERN_EMERG <0> 系统奔溃
- KERN_ALERT <1> 情况紧急必须立刻处理
- KERN_CRIT <2> 严重情况
- KERN_ERR <3> 错误情况
- KERN_WARNING <4> 警告信息
- KERN_NOTICE <5> 正常情况但是需要注意
- KERN_INFO <6> 信息
- KERN_DEBUG <7> 调试信息
- 结论:数字越小,危险系数越高。此信息越应该打印输出。
- 数字越小,打印级别越高。
- 用法:
- printk(KERN_ERR “this is a error msg!\n”);
- printk("<3>" “this is a error msg!\n”);
实例:测试打印
test_printk.c
#include <linux/init.h> #include <linux/module.h> static int printk_all_init(void) { printk("<0>", "level 0\n"); printk("<1>", "level 1\n"); printk("<2>", "level 2\n"); printk("<3>", "level 3\n"); printk("<4>", "level 4\n"); printk("<5>", "level 5\n"); printk("<6>", "level 6\n"); printk("<7>", "level 7\n"); return 0; } static void printk_all_exit(void) { printk("<0>", "level 0\n"); printk("<1>", "level 1\n"); printk("<2>", "level 2\n"); printk("<3>", "level 3\n"); printk("<4>", "level 4\n"); printk("<5>", "level 5\n"); printk("<6>", "level 6\n"); printk("<7>", "level 7\n"); } module_init(printk_all_init); module_exit(printk_all_exit); MODULE_LICENSE("GPL");
printk 函数的默认打印输出级别的配置
- printk 函数默认的打印阈值的配置
- 如果printk函数指定的打印输出级别对应的数字小于默认的打印阈值的数据,此信息输出,否则屏蔽。
- 问:内核默认的打印输出级别(阈值)如果配置?
- 两种方法:
- 通过修改配置文件/proc/sys/kernel/printk来修改默认的打印输出级别。(此方法解决不了Linux启动的时候打印信息的控制)
- 在内核的启动参数中设置默认的打印输出级别(推荐使用此方法)