目标:
实现模块读取uboot参数;
实现给模块传递参数;
实现proc文件系统属性的读写。
背景:
因一项目使用的模块,在加载时需要传入参数。于是对这一知识点进行整理,附带实现模块读取uboot参数样例和实现proc文件系统属性的读写的样例。部分代码网络摘抄,部分自己实现。
模块源码(param_proc.c):
#include <linux/kernel.h>
#include <linux/module.h> //for init_module()
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h> //for init_task
#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0)
#include <asm/switch_to.h>
#include <linux/slab.h> //for kzalloc, kfree
#include <linux/proc_fs.h> //for create_proc_info_entry()
#else
#include <asm/system.h>
#endif
#include <asm/uaccess.h>
#include <linux/uaccess.h>
//root@wmh-VirtualBox:/opt/param_proc# uname -a
//Linux wmh-VirtualBox 5.15.0-69-generic #76~20.04.1-Ubuntu SMP Mon Mar 20 15:54:19 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
//root@wmh-VirtualBox:/opt# uname -a
//Linux wmh-VirtualBox 4.15.0-142-generic #146~16.04.1-Ubuntu SMP Tue Apr 13 09:27:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
//insmod paramproc.ko val1=2 val2=4 pstr="abc" 后执行dmesg,可以看到打印:val1:2, val2:4, pstr:abc
//insmod paramproc.ko 后执行dmesg,可以看到打印:val1:0, val2:0, pstr:(null)
//uboot启动参数里面添加参考(未验证):noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 getval=1,2 getstr=abc
//cat /proc/result_f/result
//cat /proc/result
int val1 = 0, val2 = 0;
char *pstr = NULL;
module_param(val1, int, S_IRUGO);
module_param(val2, int, S_IRUGO /*0664 ???*/);
module_param(pstr, charp, S_IRUGO);
static int ccnt = 0;
static ssize_t fts_debug_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos)
{
size_t size = 0;
if(ccnt == 0) { ccnt++; } else if(ccnt > 0) { ccnt =0; return 0; } //因为执行cat /proc/result时,控制台一直打印,所以通过这种方法,当第二次进入
//这个函数时,返回0,就可以防止控制台一直打印,及防止执行cat /proc/result不退出。
if(NULL == pstr) { pstr = kmalloc(56, GFP_KERNEL); memset(pstr, 0, 56); memcpy(pstr, "111111\r\n", 8);}
printk("%s():%d, count:%ld, pstr:%s, len:%ld\r\n", __FUNCTION__, __LINE__, count, pstr, strlen(pstr)); /*count打印处理的值为:131072*/
size = copy_to_user(buff, pstr, strlen(pstr)<count?strlen(pstr):count);
printk("%s():%d, count:%ld, pstr:%s, len:%ld, size:%ld\r\n", __FUNCTION__, __LINE__, count, pstr, strlen(pstr), size);
return strlen(pstr);
}
//insmod paramproc.ko val1=12 val2=13 pstr=123456qwer 后执行dmesg,可以看到打印:val1:12, val2:13, pstr:123456qwer
//echo "qwer123456ghjwe" > /proc/result 后执行dmesg,可以看到打印:
// fts_debug_write():50, count:16, pstr:10
//copy_from_user最后一个参数填count的打印: fts_debug_write():52, pstr:qwer123456ghjwe cpuacct.usage_pe\xbfFo\xf64\xf4\xf1, count:16, size:0
//copy_from_user最后一个参数填strlen(pstr)的打印:fts_debug_write():55, pstr:qwer123456, count:16, size:0
static ssize_t fts_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
int size = 0; /*count打印处理的值为:写入值的长度+回车*/
if(NULL == pstr) { pstr = kmalloc(56, GFP_KERNEL); }
printk("%s():%d, count:%ld, pstr:%ld\r\n", __FUNCTION__, __LINE__, count, strlen(pstr));
size = copy_from_user(pstr, buff, strlen(pstr)<count?strlen(pstr):count); //返回的是没有被拷贝成功的数量。
printk("%s():%d, pstr:%s, count:%ld, size:%d\r\n", __FUNCTION__, __LINE__, pstr, count, size);
return count; //必须返回count,否则会循环写,直到写满count个字节为止。
}
static struct proc_dir_entry *g_proc_dir = NULL;
static struct proc_dir_entry *proc1 = NULL;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
static struct proc_ops fts_proc_fops={
.proc_read = fts_debug_read,
.proc_write = fts_debug_write,
};
#else
static struct file_operations fts_proc_fops={
.owner = THIS_MODULE,
.read = fts_debug_read,
.write = fts_debug_write,
};
#endif
static __init int test_init_module(void) {
printk("val1:%d, val2:%d, pstr:%s\r\n", val1, val2, pstr);
g_proc_dir = proc_mkdir("result_f", NULL); //会在 /proc目录下创建result_f目录
if(NULL == g_proc_dir) { return -EINVAL; }
//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
// proc = proc_create("result", 0664, /*g_proc_dir*/NULL, &fts_proc_fops);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
proc1 = proc_create("result", 0664, /*g_proc_dir*/NULL, &fts_proc_fops); //如果有g_proc_dir,则会在result_f目录下创建result文件
//如果没有g_proc_dir,则会在 /proc目录下创建 result文件
#else
proc1 = create_proc_entry("result", 0664, /*g_proc_dir*/NULL);
if(NULL != proc1) { //proc->read_proc = read_result;
proc1->write_proc = fts_debug_write;
proc1->read_proc = fts_debug_read;
}
#endif
if(NULL == proc1) { proc_remove(g_proc_dir); }
return 0;
}
static void test_exit_module(void) {
//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
// proc_remove(proc1);
// proc_remove(g_proc_dir);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
proc_remove(proc1);
proc_remove(g_proc_dir);
#else
remove_proc_entry("result", g_proc_dir);
#endif
}
#ifndef MODULE
static __init int get_val(char *str) {
int ints[10];
str = get_options(str, ARRAY_SIZE(ints), ints);
if(ints[0] == 2) {
val1 = ints[1];
val2 = ints[2];
}
return 1;
}
static __init int get_str(char *str) {
if(NULL == pstr) { pstr = kmalloc(56, GFP_KERNEL); }
memcpy(pstr, str, min(strlen(str), 56));
return 1;
}
__setup("getval=", get_val);
__setup("getstr=", get_str);
__initcall(test_init_module);
#else
module_init(test_init_module);
#endif
module_exit(test_exit_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wmh");
MODULE_DESCRIPTION("wmh's program");
Makefile文件:
#make modules
#make clean
#make install
CONFIG_MODULE_SIG = n
TARGET_MODULE:=paramproc
$(TARGET_MODULE)-objs := param_proc.o
ifneq ($(KERNELRELEASE),)
obj-m := $(TARGET_MODULE).o
else
PWD := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build
modules:
$(MAKE) -C $(KDIR) M=$(PWD) modules
install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *.mod.c *.ko *.symvers *.order *.cmd *.maker .tmp_versions *.mod *.cmd
endif
编译、运行结果:
执行make编译出 paramproc.ko
执行 insmod paramproc.ko val1=2 val2=4 pstr="abc" ,后执行dmesg,可以看到打印:val1:2, val2:4, pstr:abc。
执行:echo "12345678" > /proc/result 往result里面写值,实际上值写到了pstr指向的地址。
执行:cat /proc/result读取数据。
疑问:
fts_debug_read函数中如果没有添加 if(ccnt == 0) { ccnt++; } else if(ccnt > 0) { ccnt =0; return 0; },执行cat /proc/result会一直打印?????。