proc文件系统详解

本文详细介绍了Linux内核中的procfs文件系统,包括procfs的用途、历史和与sysfs、debugfs的区别。重点讲解了procfs常用的API,如struct proc_dir_entry数据结构、在/proc下创建目录、创建具体文件以及移除目录或文件的方法,并提供了procfs使用实例。

1 前言

内核中有三个常用的伪文件系统:procfs,debugfs和sysfs。

  • procfs — The proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
  • sysfs — The filesystem for exporting kernel objects.
  • debugfs — Debugfs exists as a simple way for kernel developers to make information available to user space.

它们都用于Linux内核和用户空间的数据交换,但是适用的场景有所差异:

  • procfs 历史最早,最初就是用来跟内核交互的唯一方式,用来获取处理器、内存、设备驱动、进程等各种信息。由于procfs 比较混乱,不够体系化,所以在procfs基础上发展出来了sysfs。
  • sysfs 跟 kobject 框架紧密联系,而 kobject 是为设备驱动模型而存在的,所以 sysfs 是为设备驱动服务的。sysfs要比proc更加体系化。
  • debugfs 从名字来看就是为debug而生,所以更加灵活。

它们仨的挂载方式类似,做个实验:

  1. $ sudo mkdir /tmp/{proc,sys,debug}
  2. $ sudo mount -tprocnondev /tmp/proc/  //加载文件系统到指定的加载点/tmp/proc/下
  3. $ sudo mount -tsysnondev /tmp/sys/
  4. $ sudo mount -tdebugfsnondev /tmp/debug/

不过,默认情况下,它们分别挂载在/proc,/sys/,/sys/kernel/debug/。

下面简单介绍这三个文件系统的用法。在介绍之前,请记下他们的官方文档:

  • procfs — Documentation/filesystems/proc.txt
  • sysfs — Documentation/filesystems/sysfs.txt
  • debugfs — Documentation/filesystems/debugfs.txt

以上转载于:http://tinylab.org/show-the-usage-of-procfs-sysfs-debugfs/,稍有修改。

sysfs比 proc多一点就是挂载设备的管理, 对于当前机器上所有的设备有一个统一的管理位置。 而proc本身设计只是虚拟文件系统用来展示当前运行内核的状态参数配置情况,给user space apps一个接口看kernel inside。

2 procfs常用的API

Linux内核提供了很多proc文件系统的API,先来认识几个常用的,然后通过实例来学习怎么使用这些API。

相关API定义在kernel-3.18/fs/proc/generic.c中,并在kernel-3.18\include\linux\proc_fs.h中引用。

(1)了解struct proc_dir_entry数据结构

定义路径:kernel-3.18/fs/proc/internal.h,这个结构体与低版本的描述有不少变化。

 /*这还没有完全实现。这个想法是为了在这些proc_dir_entries中创建一个内存树(就像实际的/proc文件系统一样 ),
 这样我们就可以动态地 向/proc添加新文件。
 * The "next" pointer creates a linked list of one /proc directory,
 * while parent/subdir create the directory structure (every
 * /proc file has a parent, but "subdir" is NULL for all
 * non-directory entries).
 */
struct proc_dir_entry {
	unsigned int low_ino;
	umode_t mode;
	nlink_t nlink;
	kuid_t uid;
	kgid_t gid;
	loff_t size;
	const struct inode_operations *proc_iops;
	const struct file_operations *proc_fops;//默认的file_operations
	struct proc_dir_entry *next, *parent, *subdir;//链表
	void *data;
	atomic_t count;		/* use count */
	atomic_t in_use;	/* number of callers into module in progress; */
			/* negative -> it's going away RSN */
	struct completion *pde_unload_completion;
	struct list_head pde_openers;	/* who did ->open, but not ->release */
	spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
	u8 namelen;
	char name[];
};

(2)在/proc/下创建目录

struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)
{
	/*proc_mkdir_data()第二个参数mode=0表示mode = S_IRUGO | S_IXUGO; */
	return proc_mkdir_data(name, 0, parent, NULL);
}

第二个参数parent表示name的父目录,一般为NULL,表示挂载在/proc/下。name就是在proc/下创建的目录名。该函数实际上调用了proc_mkdir_data()来创建一个目录的。proc_mkdir_data()定义如下:

struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
		struct proc_dir_entry *parent, void *data)
{
	struct proc_dir_entry *ent;

	if (mode == 0)
		mode = S_IRUGO | S_IXUGO;

	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
	if (ent) {
		ent->data = data;
		if (proc_register(parent, ent) < 0) {
			kfree(ent);
			ent = NULL;
		}
	}
	return ent;
}

当需要对创建的目录指定mode的时候,可用下面这个API:
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
				       struct proc_dir_entry *parent)
{
	return proc_mkdir_data(name, mode, parent, NULL);
}
(3)在/proc/${name}/下创建具体的文件

这一步是在上一步创建好的目录下继续创建具体的文件,有以下3种方法。

struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); /*只在低版本内核中使用,3.10中就已经没有了,file_operations在API中不指定。*/

下面是Linux内核对create_proc_entry的更新描述:

Note that the above article uses create_proc_entry which was removed in
kernel 3.10. Current versions require the following update
- entry = create_proc_entry("sequence", 0, NULL);
- if (entry)
- entry->proc_fops = &ct_file_ops;
+ entry = proc_create("sequence", 0, NULL, &ct_file_ops);

static inline struct proc_dir_entry *proc_create(const char *name,  umode_t mode, struct proc_dir_entry *parent, 
const struct file_operations *proc_fops)
{
	return proc_create_data(name, mode, parent, proc_fops, NULL);
}
/*kernel-3.18\include\linux\proc_fs.h 新的内核版本中用,proc_create中需要指定file_operations中的read和write等方法,以方便用户空间的操作。*/

在parent目录创建一个名为name,权限为mode的文件,并指定文件操作方法 proc_fops。在proc_fops中可以填充read、write、release等函数。如果parent=NULL,则直接创建在/proc/下。proc_create()实际上直接调用的是我要介绍的第三种方法proc_create_data()。

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
					struct proc_dir_entry *parent,
					const struct file_operations *proc_fops,
					void *data)
{
	struct proc_dir_entry *pde;
	if ((mode & S_IFMT) == 0)
		mode |= S_IFREG;

	if (!S_ISREG(mode)) {
		WARN_ON(1);	/* use proc_mkdir() */
		return NULL;
	}

	if ((mode & S_IALLUGO) == 0)
		mode |= S_IRUGO;
	pde = __proc_create(&parent, name, mode, 1);
	if (!pde)
		goto out;
	pde->proc_fops = proc_fops;
	pde->data = data;
	if (proc_register(parent, pde) < 0)
		goto out_free;
	return pde;
out_free:
	kfree(pde);
out:
	return NULL;
}
同上,在parent目录创建一个名为name,权限为mode的文件,并指定文件操作方法 proc_fops。同时,多了个data参数,可以任意指定其数据类型。

(4)移除创建的目录或文件
void remove_proc_entry(const char *name, struct proc_dir_entry *parent){}
name:要移除的文件名或目录名,parent:父目录,若没有则为NULL。

3 procfs使用实例

static unsigned int variable;
static struct proc_dir_entry *parent_dir, *sub_entry;

static ssize_t xxx_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
	struct seq_file *seq = file->private_data;
	unsigned int *ptr_var = seq->private;
	int err;
	char *kbuffer;
	if (!buffer || count > PAGE_SIZE - 1)
		return -EINVAL;

	kbuffer = (char *)__get_free_page(GFP_KERNEL);
	if (!kbuffer)
		return -ENOMEM;

	err = -EFAULT;
	if (copy_from_user(kbuffer, buffer, count))
	goto out;
	kbuffer[count] = '\0';

	*ptr_var = simple_strtoul(kbuffer, NULL, 10);
		return count;

	out:
	free_page((unsigned long)buffer);
	return err;
}

static int xxx_proc_show(struct seq_file *seq, void *v)
{
	unsigned int *ptr_var = seq->private;
	seq_printf(seq, "%u\n", *ptr_var);
	return 0;
}

static int xxx_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, xxx_proc_show, PDE_DATA(inode));
} 
   
static int single_release(struct inode *inode, struct file *file)
{
	return;
}

static const struct file_operations my_proc_fops =
{
	.owner = THIS_MODULE,
	.open = xxx_proc_open,//直接调用single_open()
//	.read = seq_read,
	.write = xxx_proc_write,
//	.llseek = seq_lseek,
	.release = single_release,/*single_release必须与single_open配合使用*/
};

static __init int xxx_proc_init(void)
{
	parent_dir = proc_mkdir("parent_dir", NULL);/*在proc下创建parent_dir目录*/
	if (parent_dir) {
 /*在proc/parent_dir/目录下创建文件sub_file,权限0666,可读可写,并指定文件操作方法      
        my_proc_fops*/
	sub_entry = proc_create_data("sub_file",0666, parent_dir, &my_proc_fops, &variable);
	if (sub_entry)
	 return 0;
	}
	return -ENOMEM;
}

static __exit void xxx_proc_exit(void)
{
	remove_proc_entry("sub_file", parent_dir);
	remove_proc_entry("parent_dir", NULL);
}

module_init(xxx_proc_init);
module_exit(xxx_proc_exit);
MODULE_LICENSE("GPL V2");
MODULE_AUTHOR("xxxx");
MODULE_DESCRIPTION("proc exmaple");
未完待续。。。












评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值