linux内核中做开关变量的三种方法之一:proc文件系统 (其它可参考[https://blog.csdn.net/peterbig/article/details/18273665])。
test_proc.c
#include
#include
#include
#include
#include
#include
#include
#define IGH_EC_PROC_DIR "ethercat"
#define IGH_GFAR_PROC_NAME "gfar_log"
static int igh_gfar_proc_show(struct seq_file *m, void *v);
static int igh_gfar_proc_open(struct inode *inode, struct file *file);
static ssize_t igh_gfar_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos);
static int igh_gfar_proc_init(void);
static void igh_gfar_proc_exit(void);
static int igh_gfar_proc_show(struct seq_file *m, void *v)
{
return 0;
}
static int igh_gfar_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, igh_gfar_proc_show, NULL);
}
static ssize_t igh_gfar_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
char *tmp = kzalloc((count+1), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
if (copy_from_user(tmp,buffer,count)) {
kfree(tmp);
return -EFAULT;
}
printk("%s Get user str :%s\n", __func__, tmp);
kfree(tmp);
return count;
}
ssize_t igh_ec_proc_read(struct file *file, char __user *buf, size_t len, loff_t *off)
{
int len_copy, ret;
char data[32] = "1234566789";
// fl->f_pos表示当前文件描述符对文件的偏移, len表示用户进程想要读的大小
if ((file->f_pos + len) > strlen(data)) //如果剩下没读的数据长度少于len,则只复制出剩下没读部分
len_copy = strlen(data) - file->f_pos;
else
len_copy = len; //如果剩下的数据长度超出len,则本次复制len字节
ret = copy_to_user(buf, data+file->f_pos, len_copy);
//内容复制后,需要改变文件描述符的位置偏移
*off += len_copy - ret; //在read/write函数里必须通过off来改变
return len_copy - ret;
}
static struct file_operations igh_gfar_proc_fops = {
.owner = THIS_MODULE,
.open = igh_gfar_proc_open,
.release = single_release,
.read = igh_ec_proc_read,
.llseek = seq_lseek,
.write = igh_gfar_proc_write,
};
struct proc_dir_entry *igh_gfar_proc_dir = NULL;
static int igh_ec_proc_init(void)
{
struct proc_dir_entry* file;
igh_gfar_proc_dir = proc_mkdir(IGH_EC_PROC_DIR, NULL);
if (igh_gfar_proc_dir == NULL) {
printk("%s proc create %s failed\n", __func__, IGH_EC_PROC_DIR);
return -EINVAL;
}
file = proc_create(IGH_GFAR_PROC_NAME, 0777, igh_gfar_proc_dir, &igh_gfar_proc_fops);
if (!file) {
printk("%s proc_create failed!\n", __func__);
return -ENOMEM;
}
return 0;
}
static void igh_gfar_proc_exit(void)
{
remove_proc_entry(IGH_GFAR_PROC_NAME, igh_gfar_proc_dir);
remove_proc_entry(IGH_EC_PROC_DIR, NULL);
}
static int __init my_init(void)
{
igh_ec_proc_init();
return 0;
}
static void __exit my_exit(void)
{
igh_gfar_proc_exit();
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("WXY");
MODULE_LICENSE("GPL");
Makefile
obj-m := test_proc.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
将test_proc.c和Makefile放到统一文件夹里
- $ make
- $ sudo insmod test_proc.ko
- $ echo “test” > /proc/ethercat/gfar_log
- $ dmesg (执行命令后可以看到内核打印信息)
struct proc_dir_entry *proc_create(
const char *name, umode_t mode, struct proc_dir_entry *parent,
const struct file_operations *proc_fops);
参数:
name:/proc/下节点的文件名。例:"gfar_log",在/proc/下创建demo节点
mode:访问权限。例:0666可读可写,0444只读,0222只写
parent:父目录,可以为 NULL(表示 /proc 目录)
proc_fops:此文件的操作函数file_operations
返回值: 创建proc节点文件
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
参数:
name:创建的文件夹名称
parent:创建的文件夹路径,就是在哪个文件夹中创建,如果是proc根目录,此参数为NULL
返回值:创建的文件夹节点
void remove_proc_entry(const char *name,
struct proc_dir_entry *parent);
参数:
name:待删除的proc文件。
parent:该文件的父目录。注意需要传struct proc_dir_entry
proc节点的使用主要包括创建节点和删除节点
创建节点的基本步骤:
创建节点一般的三种方式
默认在/proc路径下进行创建
只需要proc_create创建节点即可,参数parent传NULL,不需要proc_mkdir创建路径。
例:proc_create(“myProc”, 0777, NULL, &igh_gen_proc_fops);
在/proc下已经存在的目录下进行创建
给参数name传路径名加节点名的组合
例:proc_create(“myDir/myProc”, 0777, NULL, &igh_gen_proc_fops);
在/proc下创建目录再创建节点
先用proc_mkdir创建一个节点目录,再用proc_create创建节点
如上面示例代码显示,proc_create的parent参数要传入proc_mkdir返回的实例;
对应创建节点的三种方式进行删除节点
删除/proc路径下的节点
在exit里用函数remove_proc_entry删除节点,由于是在proc路径下的,所以不需要删除父节目录,参数parent传NULL即可。
删除某一路径下的节点,如删除myDir/my节点
例: remove_proc_entry(“myDir/my”, IGH_EC_PROC_DIR"/"IGH_GEN_PROC_NAME, NULL);
myDir是在/proc路径下,参数parent传NULL。
删除创建的文件夹和节点
需要先删除创建的节点,并且parent传入父路径,在删除文件夹
remove_proc_entry(IGH_GFAR_PROC_NAME, igh_gfar_proc_dir);
remove_proc_entry(IGH_EC_PROC_DIR, NULL);
同样的,由于文件夹是在/proc下的,所以parent传NULL。
注:
read回调是在读取proc文件时会调用到,但是在cat的时候会重复的调用read函数,
只有在回调的返回值为0时才会停止所以可以利用偏移量loff_t*ppo进行控制,
在写到最后时让函数返回0就能停止调用read回调。