Linux设备模型(八) - sysfs
一,sysfs目录介绍
sysfs是一个基于内存的虚拟的文件系统,有kernel提供,挂载到/sys目录下,负责以设备树的形式向user space提供直观的设备和驱动信息。
sysfs以不同的视角展示当前系统接入的设备:
/sys/block 历史遗留问题,存放块设备,提供一设备名(如sda)到/sys/devices的符号链接;
/sys/bus 按总线类型分类,在某个总线目录之下可以找到链接该总线的设备的符号链接,指向/sys/devices. 某个总线目录之下的drivers目录包含了该总线所需的所有驱动的符号链接。对应kernel中的struct bus_type;
/sys/calss 按设备功能分类,如输入设备在/sys/class/input之下,图形设备在/sys/class/graphics之下,是指向/sys/devices的符号链接。 对应kernel中的struct class
/sys/dev 按设备驱动程序分层(字符设备 块设备),提供以major:minor为名到/sys/devices的符号链接。 对应Kernel中的struct device_driver;
/sys/devices 包含所有被发现的注册在各种总线上的各种物理设备。 所有物理设备都按其在总线上的拓扑结构来显示,除了platform devices和system devices。 platform devices 一般是挂载在芯片内部高速或者低速总线上的各种控制器和外设,能被CPU直接寻址。 system devices不是外设,而是芯片内部的核心结构,比如CPU,timer等。对应kernel中的strcut device;
/sys/firmware 提供对固件的查询和操作接口(关于固件有专用于固件加载的一套api);
/sys/fs 描述当前加载的文件系统,提供文件系统和文件系统已挂载的设备信息;
/sys/kernel 提供kernel所有可调整参数,但大多数可调整参数依然存放在sysctl(/proc/sys/kernel);
/sys/module 所有加载模块(包括内联、编译进kernel、外部的模块)信息,按模块类型分类;
/sys/power 电源选项,可用于控制整个机器的电源状态,如写入控制命令进行关机、重启等;
sysfs支持多视角查看,通过符号链接,同样的信息可出现在多个目录下。
以硬盘sda为例,既可以在块设备目录/sys/block下找到,又可以在/sys/devices/pci0000:00/0000:00:10.0/host32/target32:0:0/下找到。
二,sysfs与kobject的关系
sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。
每一个Kobject,都会对应sysfs中的一个目录。因此在将Kobject添加到Kernel时,create_dir接口会调用sysfs文件系统的创建目录接口,创建和Kobject对应的目录,相关的代码如下:
static int create_dir(struct kobject *kobj)
{const struct kobj_type *ktype = get_ktype(kobj);const struct kobj_ns_type_operations *ops;int error;error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); //创建dirif (error)return error;error = populate_dir(kobj); //使用default attributes填充dirif (error) {sysfs_remove_dir(kobj);return error;}if (ktype) {error = sysfs_create_groups(kobj, ktype->default_groups); //使用default_groups填充dirif (error) {sysfs_remove_dir(kobj);return error;}}... ...
}/**
* sysfs_create_dir_ns - create a directory for an object with a namespace tag
* @kobj: object we're creating directory for
* @ns: the namespace tag to use
*/
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{struct kernfs_node *parent, *kn;kuid_t uid;kgid_t gid;if (WARN_ON(!kobj))return -EINVAL;if (kobj->parent)parent = kobj->parent->sd;elseparent = sysfs_root_kn;if (!parent)return -ENOENT;kobject_get_ownership(kobj, &uid, &gid);kn = kernfs_create_dir_ns(parent, kobject_name(kobj),S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,kobj, ns); //调用文件系统操作接口创建dirif (IS_ERR(kn)) {if (PTR_ERR(kn) == -EEXIST)sysfs_warn_dup(parent, kobject_name(kobj));return PTR_ERR(kn);}kobj->sd = kn;return 0;
}
三,attribute
1,attribute功能概述
所谓的attibute,就是内核空间和用户空间进行信息交互的一种方法。例如某个driver定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver的运行行为,那么就可以将该变量以sysfs attribute的形式开放出来。
Linux内核中,attribute分为普通的attribute和二进制attribute,如下:
msm_kernel\include\linux\sysfs.h
struct attribute {const char *name;umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOCbool ignore_lockdep:1;struct lock_class_key *key;struct lock_class_key skey;
#endif
};struct bin_attribute {struct attribute attr;size_t size;void *private;ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,char *, loff_t, size_t);ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,char *, loff_t, size_t);int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,struct vm_area_struct *vma);
};
struct attribute为普通的attribute,使用该attribute生成的sysfs文件,只能用字符串的形式读写。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写。
2,attribute文件创建
/sys/bus/i2c # cat /sys/bus/i2c/drivers_autoprobe
1
有这样一个sysfs node,当bus register的时候,drivers_autoprobe node会被创建出来,用来表示 device和device driver是否自动执行probe的操作,看一下这个attribute的创建过程。
2.1 /sys/bus/i2c/drivers_autoprobe sysfs node创建流程
bus_register(struct bus_type *bus) //msm_kernel\drivers\base\bus.c
struct subsys_private *priv;
priv->subsys.kobj.ktype = &bus_ktype; //bus_ktype
----add_probe_files(bus);
--------bus_create_file(bus, &bus_attr_drivers_autoprobe);
------------sysfs_create_file(&dev->kobj, &attr->attr);
----------------sysfs_create_file_ns(kobj, attr, NULL);
--------------------sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, uid, gid, ns);
------------------------kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid, size, ops, (void *)attr, ns, key);
----------------------------kn->priv = priv; //(void *)attr
----------------------------kernfs_add_one(kn);
--------------------------------kernfs_link_sibling(kn);
关键代码解析:
int sysfs_add_file_mode_ns(struct kernfs_node *parent,const struct attribute *attr, bool is_bin,umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{struct lock_class_key *key = NULL;const struct kernfs_ops *ops;struct kernfs_node *kn;loff_t size;if (!is_bin) {struct kobject *kobj = parent->priv;const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;/* every kobject with an attribute needs a ktype assigned *///如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的if (WARN(!sysfs_ops, KERN_ERR"missing sysfs attribute operations for kobject: %s\n",kobject_name(kobj)))return -EINVAL;if (sysfs_ops->show && sysfs_ops->store) {if (mode & SYSFS_PREALLOC)ops = &sysfs_prealloc_kfops_rw; //判断ktype->sysfs_ops中的操作函数,确定kernfs_ops ops支持哪些操作,只读/只写/读写elseops = &sysfs_file_kfops_rw;} else if (sysfs_ops->show) {if (mode & SYSFS_PREALLOC)ops = &sysfs_prealloc_kfops_ro;elseops = &sysfs_file_kfops_ro;} else if (sysfs_ops->store) {if (mode & SYSFS_PREALLOC)ops = &sysfs_prealloc_kfops_wo;elseops = &sysfs_file_kfops_wo;} elseops = &sysfs_file_kfops_empty;size = PAGE_SIZE;} else {struct bin_attribute *battr = (void *)attr;if (battr->mmap)ops = &sysfs_bin_kfops_mmap;else if (battr->read && battr->write)ops = &sysfs_bin_kfops_rw;else if (battr->read)ops = &sysfs_bin_kfops_ro;else if (battr->write)ops = &sysfs_bin_kfops_wo;elseops = &sysfs_file_kfops_empty;size = battr->size;}#ifdef CONFIG_DEBUG_LOCK_ALLOCif (!attr->ignore_lockdep)key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endifkn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,size, ops, (void *)attr, ns, key);if (IS_ERR(kn)) {if (PTR_ERR(kn) == -EEXIST)sysfs_warn_dup(parent, attr->name);return PTR_ERR(kn);}return 0;
}/**
* __kernfs_create_file - kernfs internal function to create a file
* @parent: directory to create the file in
* @name: name of the file
* @mode: mode of the file
* @uid: uid of the file
* @gid: gid of the file
* @size: size of the file
* @ops: kernfs operations for the file
* @priv: private data for the file
* @ns: optional namespace tag of the file
* @key: lockdep key for the file's active_ref, %NULL to disable lockdep
*
* Returns the created node on success, ERR_PTR() value on error.
*/
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,const char *name,umode_t mode, kuid_t uid, kgid_t gid,loff_t size,const struct kernfs_ops *ops,void *priv, const void *ns,struct lock_class_key *key)
{struct kernfs_node *kn;unsigned flags;int rc;flags = KERNFS_FILE;kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG,uid, gid, flags);if (!kn)return ERR_PTR(-ENOMEM);kn->attr.ops = ops; // kernfs_node ops操作函数初始化 #1kn->attr.size = size;kn->ns = ns;kn->priv = priv; //kernfs_node私有数据赋值,这里是struct attribute *attr,后续读写attribute的时候会用到#ifdef CONFIG_DEBUG_LOCK_ALLOCif (key) {lockdep_init_map(&kn->dep_map, "kn->active", key, 0);kn->flags |= KERNFS_LOCKDEP;}
#endif/** kn->attr.ops is accesible only while holding active ref. We* need to know whether some ops are implemented outside active* ref. Cache their existence in flags.*/if (ops->seq_show)kn->flags |= KERNFS_HAS_SEQ_SHOW;if (ops->mmap)kn->flags |= KERNFS_HAS_MMAP;if (ops->release)kn->flags |= KERNFS_HAS_RELEASE;rc = kernfs_add_one(kn);if (rc) {kernfs_put(kn);return ERR_PTR(rc);}return kn;
}
2.2 bus_ktype
如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的。
drivers/base/bus.c:
static struct kobj_type bus_ktype = {.sysfs_ops = &bus_sysfs_ops,.release = bus_release,
};static const struct sysfs_ops bus_sysfs_ops = {.show = bus_attr_show, // #2.store = bus_attr_store,
};#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{struct bus_attribute *bus_attr = to_bus_attr(attr);struct subsys_private *subsys_priv = to_subsys_private(kobj);ssize_t ret = 0;if (bus_attr->show)ret = bus_attr->show(subsys_priv->bus, buf); // #3return ret;
}static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,const char *buf, size_t count)
{struct bus_attribute *bus_attr = to_bus_attr(attr);struct subsys_private *subsys_priv = to_subsys_private(kobj);ssize_t ret = 0;if (bus_attr->store)ret = bus_attr->store(subsys_priv->bus, buf, count);return ret;
}
2.3 bus_attribute
struct bus_attribute {struct attribute attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};static ssize_t drivers_autoprobe_show(struct bus_type *bus, char *buf)
{return sysfs_emit(buf, "%d\n", bus->p->drivers_autoprobe);
}static ssize_t drivers_autoprobe_store(struct bus_type *bus,const char *buf, size_t count)
{if (buf[0] == '0')bus->p->drivers_autoprobe = 0;elsebus->p->drivers_autoprobe = 1;return count;
}static BUS_ATTR_RW(drivers_autoprobe);
ATTR 宏展开:
#define BUS_ATTR_RW(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)
#define BUS_ATTR_RO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)
#define BUS_ATTR_WO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_WO(_name)#define __ATTR(_name, _mode, _show, _store) { \.attr = {.name = __stringify(_name), \.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \.show = _show, \.store = _store, \
}#define __ATTR_PREALLOC(_name, _mode, _show, _store) { \.attr = {.name = __stringify(_name), \.mode = SYSFS_PREALLOC | VERIFY_OCTAL_PERMISSIONS(_mode) },\.show = _show, \.store = _store, \
}#define __ATTR_RO(_name) { \.attr = { .name = __stringify(_name), .mode = 0444 }, \.show = _name##_show, \
}#define __ATTR_RO_MODE(_name, _mode) { \.attr = { .name = __stringify(_name), \.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \.show = _name##_show, \
}#define __ATTR_RW_MODE(_name, _mode) { \.attr = { .name = __stringify(_name), \.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \.show = _name##_show, \.store = _name##_store, \
}#define __ATTR_WO(_name) { \.attr = { .name = __stringify(_name), .mode = 0200 }, \.store = _name##_store, \
}#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
3,应用程序read sysfs属性文件是如何调用到属性文件的show函数的
3.1 cat sysfs节点流程
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) //fs/read_write.c
----ksys_read(fd, buf, count);
--------vfs_read(f.file, buf, count, ppos);
------------.read_iter = kernfs_fop_read_iter //fs/kernfs/file.c
----------------seq_read_iter(iocb, iter);
--------------------struct seq_file *m = iocb->ki_filp->private_data;
--------------------p = m->op->start(m, &m->index);
--------------------m->op->show(m, p);
------------------------.show = kernfs_seq_show
----------------------------of->kn->attr.ops->seq_show(sf, v);
--------------------------------.seq_show = sysfs_kf_seq_show,
------------------------------------const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
----------------------------------------ops->show(kobj, of->kn->priv, buf);
--------------------------------------------.show = bus_attr_show, //msm_kernel\drivers\base\bus.c
------------------------------------------------struct bus_attribute *bus_attr = to_bus_attr(attr);
------------------------------------------------bus_attr->show(subsys_priv->bus, buf);
----------------------------------------------------drivers_autoprobe_show(struct bus_type *bus, char *buf)msm_kernel\fs\kernfs\file.c
const struct file_operations kernfs_file_fops = {.read_iter = kernfs_fop_read_iter,.write_iter = kernfs_fop_write_iter,.llseek = generic_file_llseek,.mmap = kernfs_fop_mmap,.open = kernfs_fop_open,.release = kernfs_fop_release,.poll = kernfs_fop_poll,.fsync = noop_fsync,.splice_read = generic_file_splice_read,.splice_write = iter_file_splice_write,
};msm_kernel\fs\kernfs\file.c
static const struct seq_operations kernfs_seq_ops = {.start = kernfs_seq_start,.next = kernfs_seq_next,.stop = kernfs_seq_stop,.show = kernfs_seq_show,
};msm_kernel\fs\sysfs\file.c
static const struct kernfs_ops sysfs_file_kfops_ro = {.seq_show = sysfs_kf_seq_show,
};
3.2 dump log
eg:
cat /sys/bus/i2c/drivers_autoprobe10-18 05:40:09.284 8993 8993 I Call trace:
10-18 05:40:09.284 8993 8993 I : dump_backtrace.cfi_jt+0x0/0x8
10-18 05:40:09.284 8993 8993 I : dump_stack_lvl+0xe4/0x180
10-18 05:40:09.284 8993 8993 I : drivers_autoprobe_show+0x28/0x5c
10-18 05:40:09.285 8993 8993 I : bus_attr_show+0x40/0x8c
10-18 05:40:09.285 8993 8993 I : sysfs_kf_seq_show+0xbc/0x158
10-18 05:40:09.285 8993 8993 I : kernfs_seq_show+0x54/0x9c
10-18 05:40:09.285 8993 8993 I : seq_read_iter+0x174/0x698
10-18 05:40:09.285 8993 8993 I : kernfs_fop_read_iter+0x70/0x2dc
10-18 05:40:09.285 8993 8993 I : vfs_read+0x2ec/0x368
10-18 05:40:09.285 8993 8993 I : ksys_read+0x7c/0xf0
10-18 05:40:09.285 8993 8993 I : __arm64_sys_read+0x20/0x30
10-18 05:40:09.285 8993 8993 I : el0_svc_common+0xd8/0x27c
10-18 05:40:09.285 8993 8993 I : el0_svc+0x38/0x9c
10-18 05:40:09.285 8993 8993 I : el0_sync_handler+0x8c/0xf0
10-18 05:40:09.285 8993 8993 I : el0_sync+0x1b4/0x1c0
3.3 关键代码解析
1)当user space read drivers_autoprobe节点的时候,会触发系统调用
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{return ksys_read(fd, buf, count);
}
然后会由VFS转到 file_operations 的read操作。
2)在kernfs_fop_open函数中,initialize sequential file
static int kernfs_fop_open(struct inode *inode, struct file *file)
{struct kernfs_node *kn = inode->i_private;struct kernfs_root *root = kernfs_root(kn);const struct kernfs_ops *ops;struct kernfs_open_file *of;bool has_read, has_write, has_mmap;int error = -EACCES;if (!kernfs_get_active(kn))return -ENODEV;ops = kernfs_ops(kn);has_read = ops->seq_show || ops->read || ops->mmap;has_write = ops->write || ops->mmap;has_mmap = ops->mmap;/* see the flag definition for details */if (root->flags & KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK) {if ((file->f_mode & FMODE_WRITE) &&(!(inode->i_mode & S_IWUGO) || !has_write))goto err_out;if ((file->f_mode & FMODE_READ) &&(!(inode->i_mode & S_IRUGO) || !has_read))goto err_out;}... .../** Always instantiate seq_file even if read access doesn't use* seq_file or is not requested. This unifies private data access* and readable regular files are the vast majority anyway.*/if (ops->seq_show)error = seq_open(file, &kernfs_seq_ops); //initialize sequential file, sets @file, associating it with a sequence described by @opelseerror = seq_open(file, NULL);if (error)goto err_free;of->seq_file = file->private_data;of->seq_file->private = of;/* seq_file clears PWRITE unconditionally, restore it if WRITE */if (file->f_mode & FMODE_WRITE)file->f_mode |= FMODE_PWRITE;... ...
}
3)
static int kernfs_seq_show(struct seq_file *sf, void *v)
{struct kernfs_open_file *of = sf->private;of->event = atomic_read(&of->kn->attr.open->event);return of->kn->attr.ops->seq_show(sf, v); //在attribute节点创建的过程中赋值,上文#1的位置
}
4)可供选择的kernfs_ops
static const struct kernfs_ops sysfs_file_kfops_empty = {
};static const struct kernfs_ops sysfs_file_kfops_ro = {.seq_show = sysfs_kf_seq_show,
};static const struct kernfs_ops sysfs_file_kfops_wo = {.write = sysfs_kf_write,
};static const struct kernfs_ops sysfs_file_kfops_rw = {.seq_show = sysfs_kf_seq_show,.write = sysfs_kf_write,
};static const struct kernfs_ops sysfs_prealloc_kfops_ro = {.read = sysfs_kf_read,.prealloc = true,
};static const struct kernfs_ops sysfs_prealloc_kfops_wo = {.write = sysfs_kf_write,.prealloc = true,
};static const struct kernfs_ops sysfs_prealloc_kfops_rw = {.read = sysfs_kf_read,.write = sysfs_kf_write,.prealloc = true,
};
5)找到 sysfs_ops
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{struct kernfs_open_file *of = sf->private;struct kobject *kobj = of->kn->parent->priv;const struct sysfs_ops *ops = sysfs_file_ops(of->kn);ssize_t count;char *buf;/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */count = seq_get_buf(sf, &buf);if (count < PAGE_SIZE) {seq_commit(sf, -1);return 0;}memset(buf, 0, PAGE_SIZE);/** Invoke show(). Control may reach here via seq file lseek even* if @ops->show() isn't implemented.*/if (ops->show) {count = ops->show(kobj, of->kn->priv, buf); //上文#2处定义了show函数if (count < 0)return count;}
kobj->ktype->sysfs_ops :
/*
* Determine ktype->sysfs_ops for the given kernfs_node. This function
* must be called while holding an active reference.
*/
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{struct kobject *kobj = kn->parent->priv;if (kn->flags & KERNFS_LOCKDEP)lockdep_assert_held(kn);return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}
6)
终于在上文#3处调用到了drivers_autoprobe的show函数。
4,bus_attribute/device_attribute/driver_attribute
所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的show和store函数中。这样,每个atrribute文件,实际上对应到一个自定义数据结构变量中了。
4.1 bus_attribute
struct bus_attribute {struct attribute attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
4.2 device_attribute
/* interface for exporting device attributes */
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);
};//device_attribute 节点创建举例
static ssize_t map_seg7_show(struct device *dev,struct device_attribute *attr,char *buf)
{memcpy(buf, &txx9_seg7map, sizeof(txx9_seg7map));return sizeof(txx9_seg7map);
}static ssize_t map_seg7_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t size)
{if (size != sizeof(txx9_seg7map))return -EINVAL;memcpy(&txx9_seg7map, buf, size);return size;
}
static DEVICE_ATTR(map_seg7, 0600, map_seg7_show, map_seg7_store);error = device_create_file(tx_7segled_subsys.dev_root, &dev_attr_map_seg7);
4.3 driver_attribute
/* sysfs interface for exporting driver attributes */
struct driver_attribute {struct attribute attr;ssize_t (*show)(struct device_driver *driver, char *buf);ssize_t (*store)(struct device_driver *driver, const char *buf,size_t count);
};//driver_attribute 节点创建举例
static ssize_t new_id_show(struct device_driver *driver, char *buf)
{struct usb_driver *usb_drv = to_usb_driver(driver);return usb_show_dynids(&usb_drv->dynids, buf);
}static ssize_t new_id_store(struct device_driver *driver,const char *buf, size_t count)
{struct usb_driver *usb_drv = to_usb_driver(driver);return usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, driver, buf, count);
}
static DRIVER_ATTR_RW(new_id);error = driver_create_file(&usb_drv->drvwrap.driver,&driver_attr_new_id);
参考链接:
Linux kernel sysfs device_attribute节点的创建和读写分析_如何在kernel中读取节点-CSDN博客
Linux设备模型(4)_sysfs
相关文章:

Linux设备模型(八) - sysfs
一,sysfs目录介绍 sysfs是一个基于内存的虚拟的文件系统,有kernel提供,挂载到/sys目录下,负责以设备树的形式向user space提供直观的设备和驱动信息。 sysfs以不同的视角展示当前系统接入的设备: /sys/block 历史遗…...

C语言实现Linux下的UDP服务端和客户端
程序实现了UDP服务端和客户端,客户端发送消息后等待服务端响应。 udp_server.c: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> …...

Excel小技巧 (2) - 如何去除和增加前导0
1. 如何去除前导0 公式:SUBSTITUTE(A2,0,""),然后拖动十字架,同步所有列数据,轻松搞定。 2. 如何补充前导0 公式:TEXT(D2,"0000000") ,0的个数是数字的完整位数。然后拖动十字架&a…...

【GIS人必看】ArcPy脚本如何导入到ArcToolBox中(上)【建议收藏】
经常使用ArcGIS的朋友应该知道,ArcGIS平台可以支持非常丰富的全栈链二次开发,比如ArcPy脚本开发、ArcGIS Engine桌面端开发、ArcGIS AddIn插件开发、WebGIS开发、移动端GIS开发等。当然,这些技术本人全部精通,后面会给大家陆续介绍…...

AI入门笔记(三)
神经网络是如何工作的 神经网络又是如何工作的呢?我们用一个例子来解释。我们看下面这张图片,我们要识别出这些图片都是0并不难,要怎么交给计算机,让计算机和我们得出同样的结果?难点就在于模式识别的答案不标准&…...

Linux搭建SFTP服务器
案例:搭建SFTP服务器 SFTP(SSH文件传输协议) SFTP(SSH文件传输协议)是一种安全的文件传输协议,用于在计算机之间传输文件。它基于SSH(安全外壳协议)的子系统,提供了加密的…...

MobaXterm无法上传整个文件夹,只能上传的单个文件
问题描述: 本来想使用MobaXterm上传.vscode文件夹上传到服务器,但是选择文件夹打开后只能选择文件夹下面的子文件无法上传整个文件。 解决方案: 1、简单暴力 2、压缩后解压...

Android 中get请求网络数据 详细举例
请求链接 https://api.bilibili.com/x/web-interface/ranking 1.添加网络权限 依赖等 implementation com.squareup.okhttp3:okhttp:4.9.3 implementation com.google.code.gson:gson:2.8.92.写请求类network package com.example.myapplication;import android.graphics.Bi…...

每日五道java面试题之mysql数据库篇(六)
目录: 第一题. MySQL中InnoDB引擎的行锁是怎么实现的?第二题. InnoDB存储引擎的锁的算法有三种第三题. 什么是死锁?怎么解决?第四题. 数据库的乐观锁和悲观锁是什么?怎么实现的?第五题. 为什么要使用视图&a…...

Latex——多张图排列
一、方式一(subfig 与 subfloat 配合使用) % Need:\usepackage{subfig} \begin{figure}[htbp] % \setlength{\abovecaptionskip}{0.2cm} % \setlength{\belowcaptionskip}{-0.5cm} \centering\subfloat[MOT15]{\label{fig:a}\includegrap…...

前端复选框问题-节点赋值未选中最后显示时确变成选中状态?
问题: 前两天一同事请教我:前端复选框问题-节点赋值未选中最后显示时确变成选中状态? 还有就是明明传过为的是false,在控制台上打印确变成选中状态,如下图: 以下是前端vue代码: <Scroll h…...

JavaScript命名标识符规范,前端开发面试
前端的现状 提到现状,必须先提到一个概念 大前端。由于近几年互联网的发展,尤其是移动互联网的发展,有的大前端概念将 Native 归入前端的范畴,有的大前端概念将 Node 甚至只渲染页面的 PHP 归入前端范畴,但不管怎么说…...

从零学习Linux操作系统 第三十部分 部署Anisble
一、ansible实验环境的部署 主控机 更改服务器主机名 hostnamectl set-hostname westos_ansible.westos.org 主服务器需要能够实现上网 修改网卡使之能够上网 能ping通 代表可以连接外网 搭载本地软件仓库 并且挂载镜像 装载 dnf install httpd -y 让其开机启动并且…...

C++对象模型剖析(六)一一Data语义学(三)
Data 语义学(三) “继承” 与 Data member 上期的这个继承的模块我们还剩下一个虚拟继承(virtual inheritance)没有讲,现在我们就来看看吧。 虚拟继承(Virtual Inheritance) 虚拟继承本质就是…...

Java 代理模式详解(附案例源代码)
前言 Java代理模式是一种设计模式,在 Java 开发中被广泛应用。它允许我们通过添加一个代理对象来控制对另一个对象的访问,从而提供了一种间接访问实际对象的方法。 代理模式可以分为静态代理和动态代理两种。静态代理是在代码实现阶段就确定了代理…...

七牛云 上传 文件 file is empty
问题 七牛云 上传 文件 file is empty 详细问题 笔者进行Android 开发,使用URI上传文件,上传核心代码 具体报错信息 {ver:8.7.0,ResponseInfo:1709276329412131,status:-6, reqId:, xlog:null, xvia:null, host:null, time:1709276329,error:file is…...

【AI视野·今日Sound 声学论文速览 第五十二期】Tue, 5 Mar 2024
AI视野今日CS.Sound 声学论文速览 Tue, 5 Mar 2024 Totally 18 papers 👉上期速览✈更多精彩请移步主页 Daily Sound Papers SA-SOT: Speaker-Aware Serialized Output Training for Multi-Talker ASR Authors Zhiyun Fan, Linhao Dong, Jun Zhang, Lu Lu, Zejun M…...

使用 BLAS 调用加快生成的独立代码中的矩阵运算
为了提高某些低级向量生成的代码的执行速度,并 矩阵运算(如矩阵乘法)在独立代码中,指定您 要MATLAB Coder™生成 BLAS 调用。BLAS 是一个用于低级向量和矩阵计算的软件库,它具有 几个高度优化的机器特定实现。代码生成…...

一台服务器,最大支持的TCP连接数是多少?
一个服务端进程最大能支持多少条 TCP 连接? 一台服务器最大能支持多少条 TCP 连接? 一、原理 TCP 四元组的信息:源IP、源端口、目标IP、目标端口。 一个服务端进程最大能支持的 TCP 连接个数的计算公式:最大tcp连接数客户端的IP…...

微信小程序云开发教程——墨刀原型工具入门(编辑页面)
引言 作为一个小白,小北要怎么在短时间内快速学会微信小程序原型设计? “时间紧,任务重”,这意味着学习时必须把握微信小程序原型设计中的重点、难点,而非面面俱到。 要在短时间内理解、掌握一个工具的使用…...

flutter打包app
Flutter 打包APP (Android & IOS)_encountered error while building for device.-CSDN博客 使用命令行 keytool -genkey -v -keystore ../key -keyalg RSA -keysize 2048 -validity 10000 -alias key 将在文件根目录上一层生成key文件࿰…...

力扣543. 二叉树的直径
Problem: 543. 二叉树的直径 文章目录 题目描述思路复杂度Code 题目描述 思路 1.最大直径 左子树的最大深度 右子树的最大深度; 2.定义一个变量maxDiameter记录最大直径,并编写一个递归函数maxDepth,利用树的后序遍历每次递归求取leftMax&a…...

python网络爬虫教程笔记(1)
系列文章目录 文章目录 系列文章目录前言一、爬虫入门1.爬虫是什么?2.爬虫工作原理3.爬虫基本原理4.工作流程5.HTTP请求6.HTTP响应7.HTTP原理:证书传递、验证和数据加密、解密过程解析8.Urllib.request库的使用9.TCP3次握手,4次挥手过程 总结…...

C# 异步返回类型详解
在现代软件开发中,异步编程已经成为一种重要的编程范式,尤其是在需要与I/O密集型操作交互的上下文中,比如网络请求、数据库操作等。C# 语言提供了强大的异步支持,使得异步编程变得更加简单和直观。本文将详细介绍C#中异步返回类型…...

BAT等大厂必问技术面试题,【2024Android最新学习路线
下面分享一下我在爱奇艺的面经 面试前的话:在面试时一定不要受前面没有过的面试的影响,一定要有一个好的心态,不要面试还没开始就自己把自己思绪搞乱了 一共进行了4轮面试 爱奇艺一面 50min 项目 主要介绍了以前做过的项目,分析…...

72. 编辑距离【leetcode】/动态规划难
72. 编辑距离 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作: 插入一个字符删除一个字符替换一个字符 示例 1: 输入:word1 “horse”, word2 “ros”…...

【MySQL】视图、索引
目录 视图视图的用途优点视图的缺点创建视图查看视图修改视图删除视图注意事项 索引索引的原理索引的数据结构二分查找法Hash结构Hash冲突!!! B树二叉查找树 存在问题改造二叉树——B树降低树的高度 B树特点案例继续优化的方向 改造B树——B树…...

反编译java生成的.class文件
java代码编译后生成xxx.class文件,有时候需要反编译这个class文件看代码是怎么写的,可以使用下面这个工具。 工具已经上传到资源,链接: https://download.csdn.net/download/weixin_42556307/88915887 具体使用如下: …...

Cookie 探秘:了解 Web 浏览器中的小甜饼
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

Python线性代数数字图像和小波分析之二
要点 数学方程:数字信号和傅里叶分析,离散时间滤波器,小波分析Python代码实现及应用变换过程: 读取音频和处理音频波,使用Karplus-强算法制作吉他音频离散傅里叶计算功能和绘制图示结果计算波形傅里叶系数正向和反向&…...