Linux常用文件系统接口原理与应用

PROC文件系统

proc文件系统原理

proc文件系统,俗称伪文件系统 用户态与内核态交互的一种文件系统接口之一,该文件系统在5.8版本的内核之前,实质上就是一个普通的file_operations结构体文件,该类型的文件一般定义了open、write、read、mmap等过程,具体需要根据用户的需求,proc文件系统分为如下几类:
1、进程管理:每个进程在创建之初,都会有一个proc接口,记录了进程的status、ns(namespace)、mem、cgroup、io、oom、stack、uid_map、mount、cpuset等信息,这些信息记录在/proc/[pid]/目录下,具体目录的意义可参考博客《简析Linux中的proc
2、系统相关:在Linux加载之后,会在/proc目录下创建一些系统相关的Proc文件,包括:cpuinfo(记录cpu信息)、cmdline(记录启动参数)、cgroups(记录cgroup信息)、meminfo(内存信息)、zoneinfo(内存zone信息)、slabinfo(页表信息)、swaps、iomem、interrupts(中断)等,还有一些重要的目录,包括:bus(系统总线,包括pci等)、irq、driver(默认只有nvram和rtc)、sys(很庞大)、tty等。
3、用户调用proc函数自己创建的。

proc文件系统常用函数

1、proc_mkdir

(1)参数详解
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent):定义在fs/proc/generic.c
@name:创建的目录名称,如/proc/driver,name = “driver”;
@parent:新建的Proc目录的父级目录,如果改参数为NULL,则表示在/proc/目录下新建一个目录;
@return:如果成功,则返回创建的proc_dir_entry,否则返回NULL。

(2)函数实现分析

static bool pde_subdir_insert(struct proc_dir_entry *dir,
			      struct proc_dir_entry *de)
{
	struct rb_root *root = &dir->subdir;
	struct rb_node **new = &root->rb_node, *parent = NULL;

	/* Figure out where to put new node */
	while (*new) {
		struct proc_dir_entry *this = rb_entry(*new,
						       struct proc_dir_entry,
						       subdir_node);
		int result = proc_match(de->name, this, de->namelen);

		parent = *new;
		if (result < 0)
			new = &(*new)->rb_left;
		else if (result > 0)
			new = &(*new)->rb_right;
		else
			return false;
	}

	/* Add new node and rebalance tree. */
	rb_link_node(&de->subdir_node, parent, new);
	rb_insert_color(&de->subdir_node, root);
	return true;
}
struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
		struct proc_dir_entry *dp)
{
	if (proc_alloc_inum(&dp->low_ino))
		goto out_free_entry;

	write_lock(&proc_subdir_lock);
	dp->parent = dir;
	//pde_subdir_insert通过红黑树的节点操作,这里的红黑树根据proc名称长度来分配,左子树为是短名称,右子树是长名称,将新增的Proc目录插入到红黑树节点中,这里面有一个文件名匹配的过程,如果匹配到了则直接返回false,则表示proc红黑树中存在该节点,直到遍历所有的红黑树节点,如果没有找到,则将将新增的目录节点放在红黑树的对应节点上,并给节点上色。
	if (pde_subdir_insert(dir, dp) == false) {
		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
		     dir->name, dp->name);
		write_unlock(&proc_subdir_lock);
		goto out_free_inum;
	}
	dir->nlink++;
	write_unlock(&proc_subdir_lock);

	return dp;
out_free_inum:
	proc_free_inum(dp->low_ino);
out_free_entry:
	pde_free(dp);
	return NULL;
}

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

	if (mode == 0)
		mode = S_IRUGO | S_IXUGO;//proc目录权限

	ent = __proc_create(&parent, name, S_IFDIR | mode, 2);//创建一个proc目录实体
	if (ent) {
		ent->data = data;
		ent->proc_dir_ops = &proc_dir_operations;
		ent->proc_iops = &proc_dir_inode_operations;
		//下面的处理与5.0版本之前的方式不同,之前的版本只记录了一个parent->nlink,如果创建成功,则加一次,并调用proc_register,如果proc_gister失败,则将nlink自减,nlink表示proc目录创建成功,而这里的pde_force_lookup的作用就只是给ent挂一个proc_dops,nlink放在了proc_register中完成
		if (force_lookup) {
			pde_force_lookup(ent);
		}
		ent = proc_register(parent, ent);
	}
	return ent;
}
EXPORT_SYMBOL_GPL(_proc_mkdir);

struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
		struct proc_dir_entry *parent, void *data)
{
	return _proc_mkdir(name, mode, parent, data, false);
}
EXPORT_SYMBOL_GPL(proc_mkdir_data);

struct proc_dir_entry *proc_mkdir(const char *name,
		struct proc_dir_entry *parent)
{
	return proc_mkdir_data(name, 0, parent, NULL);
}
EXPORT_SYMBOL(proc_mkdir);

2、proc_create

struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_ops);
头文件:include/linux/proc_fs.h,定义位置:fs/proc/generic.c

struct proc_dir_entry *proc_create(const char *name, umode_t mode,
				   struct proc_dir_entry *parent,
				   const struct proc_ops *proc_ops)
{
	return proc_create_data(name, mode, parent, proc_ops, NULL);
}
EXPORT_SYMBOL(proc_create);

用例

下面是基于5.10内核提供的例子,opts为struct proc_ops结构体变量。

	int ret;
	cgel_exclusive_dir = proc_mkdir("name_dir", NULL);
	if (!cgel_exclusive_dir) {
		ret = -EINVAL;
		goto out;
	}
	cgel_exclusive_proc = proc_create("name_proc_fs", 0644,
			cgel_exclusive_dir, &opts);
	if(!cgel_exclusive_proc) {
		ret = -EINVAL;
		goto out;
	}

sysFS文件系统

sysFS文件系统原理

sysfs文件系统主要是针对设备的内核与用户空间交互借口,正常情况下,一个设备驱动,都会对应一个sysfs作为一个设备属性读写文件,相对proc文件系统而言,sysfs更灵活,可以一次性添加多个变量的修改,不好之处在于,sysfs每增加一个属性变量,都需要添加对应的IO函数,这样整个代码的冗余度会随着变量的多少变化,尤其是涉及到设备寄存器的读写操作函数。sysfs文件系统主要涉及到一下几个宏和结构体的使用,见下一章节。

sysFS文件系统常用函数

1、结构体

struct device_attribute,声明在include/linux/device.h

struct device_attribute {
	struct attribute	attr;//设备文件属性
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);//用户态读取设备属性值的钩子函数
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);//用户态写操作的钩子函数
};

struct attribute,声明在include/linux/sysfs.h

struct attribute {
	const char		*name;//属性名称
	umode_t			mode;//属性权限,也表示文件的读写等权限
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;//动态分配key的track对应的hash实体
	struct lock_class_key	skey;
#endif
};

2、主要的宏

DEVICE_ATTR宏
#define DEVICE_ATTR(_name, _mode, _show, _store) \
	struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

//include/linux/sysfs.h
#define __ATTR(_name, _mode, _show, _store) {				\
	.attr = {.name = __stringify(_name),				\
		 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\
	.show	= _show,						\
	.store	= _store,						\
}

给设备增加一个自定义权限的属性文件

DEVICE_ATTR_PREALLOC宏

该宏主要实现SYSFS_PREALLOC权限模式的创建

#define DEVICE_ATTR_PREALLOC(_name, _mode, _show, _store) \
	struct device_attribute dev_attr_##_name = \
		__ATTR_PREALLOC(_name, _mode, _show, _store)

//include/linux/sysfs.h
#define __ATTR_PREALLOC(_name, _mode, _show, _store) {			\
	.attr = {.name = __stringify(_name),				\
		 .mode = SYSFS_PREALLOC | VERIFY_OCTAL_PERMISSIONS(_mode) },\
	.show	= _show,						\
	.store	= _store,						\
}

读写操作的宏

下面几个宏是使用频率最高的

//设备属性可读写
#define DEVICE_ATTR_RW(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
//设备属性只读
#define DEVICE_ATTR_RO(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
//设备属性只写
#define DEVICE_ATTR_WO(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_WO(_name)

特殊类型的设备属性
//无符号长整型
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \
	struct dev_ext_attribute dev_attr_##_name = \
		{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
//整型
#define DEVICE_INT_ATTR(_name, _mode, _var) \
	struct dev_ext_attribute dev_attr_##_name = \
		{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
//bool类型
#define DEVICE_BOOL_ATTR(_name, _mode, _var) \
	struct dev_ext_attribute dev_attr_##_name = \
		{ __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) }

用例

static ssize_t reg;

static ssize_t reg_show(struct device *dev, struct device_attrbute *attr)
{
	if (!dev || !attr){
		pr_err("invalid\n");
		return -EINVAL;
	}
	return reg;
}

static ssize_t reg_store(struct device *dev,
						 struct device_attrbute *attr,
						 const char *buf, size_t count)
{
	if (!dev || !attr){
		pr_err("invalid\n");
		return -EINVAL;
	}
	if (kstroll(buf, 16, &reg) < 0) {
		pr_err("kstroll failed\n");
		return -ERANGE;
	}
	pr_info("reg : 0x%x\n", reg);
	return count;
}
static DEVICE_ATTR_RW(reg);

static struct attribute *test_attrs[] = {
	&dev_attr_reg.attr.
	NULL,
};

static const struct attribute_group test_attr_group = {
	.attrs = test_attrs,
};

static const struct attribute_group test_attr_groups[] = {
	&test_attr_group,
	NULL,
};

有的设备如网络设备(struct net_device)包含一个group类型的成员,此时,就可以将test_attr_group挂在设备上,在系统起来后会在/sys/device/platform或者别的/sys路径下创建对应的设备文件。这个过程一般在设备probe的时候挂上去。

debugfs文件系统的使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值