目录
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, ®) < 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的时候挂上去。