ubuntu/linux mint 创建proc文件的三种方法(四)

时间:2021-08-06 16:29:04

在做内核驱动开发的时候,能够使用/proc下的文件,获取对应的信息,以便调试。

大多数/proc下的文件是仅仅读的,但为了演示样例的完整性,都提供了写方法。


方法一使用create_proc_entry创建proc文件(简单,但写操作有缓冲区溢出的危急)

方法二使用proc_create和seq_file创建proc文件(较方法三简洁)

方法三使用proc_create_data和seq_file创建proc文件(较麻烦,但比較完整)

演示样例四在proc文件里使用内核链表的一个演示样例(用的方法三)

--------------------------------------------------------------------------------------------------------------------

四、

proc_test04.c 源代码

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

struct proc_head
{
	struct list_head lhead;
	int item_count;
	int str_count;
};

struct proc_item
{
	struct list_head litem;
	char *buf;
	int num;
};

struct proc_head *gp_head;

// linux/seq_file.h
// void * (*start) (struct seq_file *m, loff_t *pos);
// void (*stop) (struct seq_file *m, void *v);
// void * (*next) (struct seq_file *m, void *v, loff_t *pos);
// int (*show) (struct seq_file *m, void *v);

/**
* author:  aran
* fuction: seq_operations -> start
*/
static void *my_seq_start(struct seq_file *m, loff_t *pos)
{
	struct proc_item *entry;

	if (0 == *pos)
	{
		seq_printf(m, "List has %d items, total %d bytes\n", gp_head->item_count, gp_head->str_count);
	}

	// get first item
	++*pos;
	list_for_each_entry(entry, &gp_head->lhead, litem)
	{
		if (*pos == entry->num)
		{
			return entry;
		}
	}
	return NULL;
}

/**
* author:  aran
* fuction: seq_operations -> next
*/
static void *my_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
	struct proc_item *entry;

	// get next item
	++*pos;
	list_for_each_entry(entry, &gp_head->lhead, litem)
	{
		if (*pos == entry->num)
		{
			return entry;
		}
	}
	return NULL;
}

/**
* author:  aran
* fuction: seq_operations -> stop
*/
static void my_seq_stop(struct seq_file *m, void *v)
{
	// clean sth.
	// nothing to do
}

/**
* author:  aran
* fuction: seq_operations -> show
*/
static int my_seq_show(struct seq_file *m, void *v)
{
	struct proc_item *tmp = v;
	seq_printf(m, "%s", tmp->buf);

	return 0;
}

// global var
static struct seq_operations my_seq_fops = 
{
	.start	= my_seq_start,
	.next	= my_seq_next,
	.stop	= my_seq_stop,
	.show	= my_seq_show,
};

// file_operations
// int (*open) (struct inode *, struct file *)
// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *)

/**
* author:  aran
* fuction: file_operations -> open
*/
static int proc_seq_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &my_seq_fops);
}

/**
* author:  aran
* fuction: file_operations -> write
*/
static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
	struct proc_item *tmp;
	int ret;

	// allocate proc_item
	tmp = (struct proc_item *)kzalloc(sizeof(*tmp), GFP_KERNEL);
	if (NULL == tmp)
	{
		ret = -ENOMEM;
		goto err_kzalloc1;
	}
	INIT_LIST_HEAD(&tmp->litem);

	// allocate str buf
	tmp->buf = (char *)kzalloc(count, GFP_KERNEL);
	if (NULL == tmp->buf)
	{
		ret = -ENOMEM;
		goto err_kzalloc2;
	}

	if (0 != copy_from_user(tmp->buf, buffer, count))
	{
		ret = -1;
		goto err_copy;
	}

	list_add(&tmp->litem, &gp_head->lhead);
	gp_head->item_count++;
	gp_head->str_count += count;
	tmp->num = gp_head->item_count; 

	return count;

err_copy:
	kfree(tmp->buf);
err_kzalloc2:
	kfree(tmp);
err_kzalloc1:
	return ret;
}

// global var
static struct file_operations proc_seq_fops = 
{
	.owner		= THIS_MODULE,
	.open		= proc_seq_open,
	.read		= seq_read,
	.write		= proc_seq_write,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

static int __init my_init(void)
{
	struct proc_dir_entry *file;
	int ret;

	// allocate & init proc_head
	gp_head = (struct proc_head *)kzalloc(sizeof(*gp_head), GFP_KERNEL);
	if (NULL == gp_head)
	{
		ret = -ENOMEM;
		goto err_kzalloc;
	}
	gp_head->item_count = 0;
	gp_head->str_count = 0;
	INIT_LIST_HEAD(&gp_head->lhead);

	// create "/proc/proc_seq" file
	file = proc_create_data(
		"proc_seq",	// name
		0666,		// mode
		NULL,		// parent dir_entry
		&proc_seq_fops,	// file_operations
		NULL		// data
		);
	if (NULL == file)
	{
		printk("Count not create /proc/proc_seq file!\n");
		ret = -1;
		goto err_proc_create_data;
	}

	return 0;

err_proc_create_data:
	kfree(gp_head);
err_kzalloc:
	return ret;
}

static void __exit my_exit(void)
{
	struct proc_item *tmp1, *tmp2;

	remove_proc_entry("proc_seq", NULL);
	list_for_each_entry_safe(tmp1, tmp2, &gp_head->lhead,litem)
	{
		list_del(&tmp1->litem);
		kfree(tmp1->buf);
		kfree(tmp1);
	}
	kfree(gp_head);
}

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("aran");
MODULE_LICENSE("GPL");
Makefile文件:

obj-m	:= proc_test04.o
KERNEL	:= /lib/modules/`uname -r`/build #for mint/ubuntu
#KERNEL	:= /lib/modules/`uname -r`/source #for redhat

all:
	make -C $(KERNEL) M=`pwd` modules

install:
	make -C $(KERNEL) M=`pwd` modules_install
	depmod -A

clean:
	make -C $(KERNEL) M=`pwd` clean
測试结果:

ubuntu/linux mint 创建proc文件的三种方法(四)