怎样在Linux驱动中创建proc节点(示例)

linux内核中做开关变量的三种方法之一:proc文件系统 (其它可参考[https://blog.csdn.net/peterbig/article/details/18273665])。

代码示例

test_proc.c

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#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放到统一文件夹里

  1. $ make
  2. $ sudo insmod test_proc.ko
  3. $ echo “test” > /proc/ethercat/gfar_log
  4. $ dmesg (执行命令后可以看到内核打印信息)

函数介绍

  1. 创建函数
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节点文件
  1. 创建文件夹函数
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);

    参数:
    name:创建的文件夹名称
    parent:创建的文件夹路径,就是在哪个文件夹中创建,如果是proc根目录,此参数为NULL
    返回值:创建的文件夹节点
  1. 删除函数(删除节点或者目录)
void remove_proc_entry(const char *name,
	struct proc_dir_entry *parent);
    参数:
    name:待删除的proc文件。
    parent:该文件的父目录。注意需要传struct proc_dir_entry

应用方法

proc节点的使用主要包括创建节点删除节点

1. 创建节点

创建节点的基本步骤:

创建节点一般的三种方式

  1. 默认在/proc路径下进行创建

    只需要proc_create创建节点即可,参数parent传NULL,不需要proc_mkdir创建路径。
    例:proc_create(“myProc”, 0777, NULL, &igh_gen_proc_fops);

  2. 在/proc下已经存在的目录下进行创建

    给参数name传路径名加节点名的组合
    例:proc_create(“myDir/myProc”, 0777, NULL, &igh_gen_proc_fops);

  3. 在/proc下创建目录再创建节点

    先用proc_mkdir创建一个节点目录,再用proc_create创建节点
    如上面示例代码显示,proc_create的parent参数要传入proc_mkdir返回的实例;

2. 删除节点

对应创建节点的三种方式进行删除节点

  1. 删除/proc路径下的节点

    在exit里用函数remove_proc_entry删除节点,由于是在proc路径下的,所以不需要删除父节目录,参数parent传NULL即可。

  2. 删除某一路径下的节点,如删除myDir/my节点

    例: remove_proc_entry(“myDir/my”, IGH_EC_PROC_DIR"/"IGH_GEN_PROC_NAME, NULL);
    myDir是在/proc路径下,参数parent传NULL。

  3. 删除创建的文件夹和节点

    需要先删除创建的节点,并且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回调。

猜你喜欢

转载自blog.csdn.net/weixin_38056448/article/details/83218643