Linux内核入门到放弃-无持久存储的文件系统-《深入Linux内核架构》笔记
proc文件系统
proc文件系统是一种虚拟的文件系统,其信息不能从块设备读取。只有在读取文件内容时,才动态生成相应的信息。
/proc的内容
- 内存管理
- 系统进程的特征数据
- 文件系统
- 设备驱动程序
- 系统总线
- 电源管理
- 终端
- 系统控制参数
特定于进程的数据
- cmdline:用于启动进程的命令行(用0作为分隔符,而不是空格)
- environ表示为该程序设置的所有环境变量
- maps以文本形式,列出了进程使用的所有库的内存映射。
- status包含了有关进程状态的一般信息(不仅提供了有关UID/GID及进程其他信息,还包括内存分配,进程能力,各个信号掩码的状态)
- stat和statm以一连串数字的形式,提供了进程及其内存消耗的更多状态信息。
- fd:各个文件描述符,可以通过(ls -l),查看其文件的位置
- cwd指向进程当前工作目录
- exe指向包含了应用程序代码的二进制文件。
- root指向进程的根目录。(不见得是全局的根目录)
一般性系统信息
与特定的内核子系统无关的一般性信息,一般存放在/proc下的文件中。
kcore是一个动态的内核文件,包含了运行中的内核的所有数据,即主内存的全部内容。与用户应用程序发生致命错误时内存转存所产生的普通内核文件相比,该文件没什么不同之处。可以将调试器用于该二进制文件,来查看运行中系统的当前状态。
网络信息
/proc/net子目录提供了内核的各种网络选项的有关数据
系统控制参数
/proc/sys/
数据结构
proc数据项的表示
proc文件系统中的每个数据项都由proc_dir_entry的一个实例描述,该结构定义了如下:
struct proc_dir_entry {
unsigned int low_ino;//inode编号
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;//指定了目录中子目录和符号链接的数目
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
/*
* NULL ->proc_fops means "PDE is going away RSN" or
* "PDE is just created". In either case, e.g. ->read_proc won't be
* called because it's too late or too early, respectively.
*
* If you're allocating ->proc_fops dynamically, save a pointer
* somewhere.
*/
const struct file_operations *proc_fops;
get_info_t *get_info;//指向相关子系统返回所需数据的例程
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;//parent:指向父目录的指针,subdir:指向一个目录中的第一个子数据项,next:将目录下的所有常见数据项都集成到一个单链表中
void *data;//作为read_proc,write_proc的参数传递
read_proc_t *read_proc;//指向的函数支持从内核读取数据
write_proc_t *write_proc;//指向的函数支持向内核写入数据
atomic_t count; /* use count */
int pde_users; /* number of callers into module in progress */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
struct completion *pde_unload_completion;
shadow_proc_t *shadow_proc;
};
typedef int (read_proc_t)(char *page, char **start, off_t off,
int count, int *eof, void *data);
typedef int (write_proc_t)(struct file *file, const char __user *buffer,
unsigned long count, void *data);
proc inode
内核提供了一个数据结构,称之为proc_inode,支持以面向inode的方式来查看proc文件系统的数据项。
<linux/proc_fs.h>
union proc_op {
int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **);//获得特定于进程的信息
int (*proc_read)(struct task_struct *task, char *page);//在虚拟文件系统中建立链接
};
struct proc_inode {
struct pid *pid;
int fd;
union proc_op op;
struct proc_dir_entry *pde;
struct inode vfs_inode;
};
初始化
<fs/proc/root.c>
void __init proc_root_init(void)
{
int err = proc_init_inodecache();//为proc_inode对象创建一个slab缓存
if (err)
return;
err = register_filesystem(&proc_fs_type);//注册文件系统
if (err)
return;
proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);//挂载文件系统
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc_fs_type);
return;
}
proc_misc_init();//创建proc主目录中的各种文件项
proc_net_init();
#ifdef CONFIG_SYSVIPC
proc_mkdir("sysvipc", NULL);
#endif
proc_root_fs = proc_mkdir("fs", NULL);
proc_root_driver = proc_mkdir("driver", NULL);
proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
/* just give it a mountpoint */
proc_mkdir("openprom", NULL);
#endif
proc_tty_init();
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
proc_bus = proc_mkdir("bus", NULL);
proc_sys_init();
}
装载proc文件系统
将特定于proc文件系统的超级块数据填充到一个vfsmount结构的实例中,使得新的文件系统能够集成到VFS树中。
<fs/proc/inode.c>
int proc_fill_super(struct super_block *s)
{
struct inode * root_inode;
s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
s->s_time_gran = 1;
de_get(&proc_root);
root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);//为根目录创建一个inode
if (!root_inode)
goto out_no_root;
root_inode->i_uid = 0;
root_inode->i_gid = 0;
s->s_root = d_alloc_root(root_inode);//
if (!s->s_root)
goto out_no_root;
return 0;
out_no_root:
printk("proc_read_super: get root inode failed\n");
iput(root_inode);
de_put(&proc_root);
return -ENOMEM;
}
proc文件系统中,根inode与其他inode的不同之处在于,它不仅包含“普通”的文件和目录,还管理着特定于进程的PID目录。
管理/proc数据项
数据项的创建和注册
数据项分两个步骤添加到proc文件系统。首先,创建proc_dir_entry的一个新实例,填充描述该数据项的所有需要的信息。然后,将该实例注册到proc的数据结构。
create_proc_entry->proc_register
proc_register会根据项的类型,选择适当的inode与file的operations.
查找proc数据项
按以前的讨论,该函数将调用inode_operations的lookup函数指针。根据文件名的各个路径分量,来确定文件名所对应的inode
inode->look_up
读取和写入信息
内核使用保存在proc_file_operations中的操作来读写常规proc数据项的内容:
static const struct file_operations proc_file_operations = {
.llseek = proc_file_lseek,
.read = proc_file_read,
.write = proc_file_write,
};
proc_file_read的实现
从proc文件读取数据的操作分为3个步骤
- 分配一个内核内存页面,产生的数据将填充到页面中
- 调用一个特定于文件的函数,向内核内存页面填入数据
- 数据从内核空间复制到用户空间。
<fs/proc/generic.c>
proc_file_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
...
if (dp->get_info) {
/* 处理旧的网络例程 */
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
...
}
内核在 proc_dir_entry 结构中提供了两个函数指针get_info 和 read_proc。这两个函数用于读取数据,而内核必须选择一个匹配的来使用。
proc_file_write的实现
static ssize_t
proc_file_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct proc_dir_entry * dp;
dp = PDE(inode);
if (!dp->write_proc)
return -EIO;
/* FIXME: does this routine need ppos? probably... */
return dp->write_proc(file, buffer, count, dp->data);
}
进程相关的信息
输出与系统进程相关的详细信息,是proc文件系统最初设计的主要任务之一。
proc_pid_lookup负责打开/proc/中特定于PID的文件。(创建一个inode作为第一个对象,用于后续的特定于PID的操作)。
<fs/proc/base.c>
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task;
unsigned tgid;
struct pid_namespace *ns;
result = proc_base_lookup(dir, dentry);//处理self目录的情况(创建一个inode表示self,并初始化inode operations结构中的链接相关操作)
if (!IS_ERR(result) || PTR_ERR(result) != -ENOENT)
goto out;
tgid = name_to_int(dentry);//将PID字符串转换成整数
if (tgid == ~0U)
goto out;
ns = dentry->d_sb->s_fs_info;
rcu_read_lock();
task = find_task_by_pid_ns(tgid, ns);//找到task_struct结构
if (task)
get_task_struct(task);
rcu_read_unlock();
if (!task)
goto out;
result = proc_pid_instantiate(dir, dentry, task, NULL);//首先创建一个proc inode,然后初始化inode_operations为proc_tgid_base_inode_operations
put_task_struct(task);
out:
return result;
}
处理文件
在特定于PID的目录/proc/pid中处理一个文件时,这是使用该目录的inode操作完成的。
<fs/proc/base.c>
static const struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
};
proc_tgid_base_lookup的任务是根据给定的名称,返回一个inode实例
系统控制机制
操纵内核行为的传统方法是sysctl系统调用。
Linux借助于 proc 文件系统。内核重排了所有的sysctl,建立起一个层次结构,并导出到 /proc/sys 目录下。可以使用简单的用户空间工具来读取或操纵这些参数。要修改内核的运行时行为, cat 和 echo 就足够。
使用sysctl
<sysctl.h>
struct ctl_table
{
int ctl_name; /* 二进制ID */
const char *procname;/* /proc/sys下各目录项的文本ID,或NULL */
void *data;
int maxlen;
mode_t mode;
struct ctl_table *child;
struct ctl_table *parent;
proc_handler *proc_handler;
ctl_handler *strategy;
struct proc_dir_entry *de;
void *extra1;
void *extra2;
};
内核提供了ctl_table_header数据结构,使得能够将几个sysctl表维护在一个链表中
<sysctl.h>
struct ctl_table_header
{
ctl_table *ctl_table;
struct list_head ctl_entry;
...
};
对所有的sysctl都定义了静态的sysctl表,无论系统配置如何。根结点对应的表是 root_table ,用作所有静态定义的数据的根:
kernel/sysctl.c
static ctl_table root_table[];
static struct ctl_table_header root_table_header =
{ root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) };
除了静态定义的sysctl之外,内核还提供了一个接口,用于动态注册和注销新的系统控制功能。register_sysctl_table 用于注册sysctl表,而其对应的 unregister_sysctl_table用于删除sysctl表,后者通常发生在模块卸载时。
register_sysctl_table 函数需要一个参数,一个指向 ctl_table 数组的指针,其中定义了新的sysctl层次结构。该函数由几个步骤组成。首先,创建一个新的 ctl_table_header 实例,并与目标sysctl表关联起来。然后,将 ctl_table_header 添加到现存sysctl层次结构的链表中。
简单的文件系统
顺序文件
基本上,必须提供一个 struct file_operations 的实例,其中一些函数指针指向一些 seq_ 例程,这样就可以利用顺序文件的标准实现了。kprobes子系统的做法如下:
kernel/kprobes.c
static struct file_operations debugfs_kprobes_operations = {
.open = kprobes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
唯一需要实现的方法是 open 。实现该函数不需要多少工作量,简单的一行代码就可以将文件关联
到顺序文件接口:
kernel/kprobes.c
static struct seq_operations kprobes_seq_ops = {
.start = kprobe_seq_start,
.next = kprobe_seq_next,
.stop = kprobe_seq_stop,
.show = show_kprobe_addr
};
static int __kprobes kprobes_open(struct inode *inode, struct file *filp)
{
return seq_open(filp, &kprobes_seq_ops);
}
seq_open将file中的private字段设置为seq_file。
<seq_file.h>
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
...
const struct seq_operations *op;
...
};
与虚拟文件系统的关联
首先,该函数需要从VFS层的 struct file 获得 seq_file 实例。回想前文, seq_open 通过private_data 建立了该关联。
如果有些数据等待写出(如果 struct seq_file 的 count 成员为正值),则使用 copy_to_user 将其复制到用户层。此外,还需要更新 seq_file 的各个状态成员。
下一步,会产生新的数据。在调用 start 之后,内核接连调用 show 和 next ,直至填满可用的缓冲区。最后,调用 stop ,使用 copy_to_user 将生成的数据复制到用户空间。
用libfs编写文件系统
使用libfs建立的虚拟文件系统,其文件和目录层次结构可使用 dentry 树产生和遍历。这意味着在该文件系统的生命周期内,所有的 dentry 都必须驻留在内存中。除非通过 unlink 或 rmdir 显式删除,否则不能消失。但这个要求很容易做到:代码只需要确保所有 dentry 的使用计数都是正值即可。
sysfs
sysfs是一个向用户空间导出内核对象的文件系统,它不仅提供了察看内核内部数据结构的能力,还可以修改这些数据结构。特别重要的是,该文件系统高度层次化的组织:sysfs的数据项来源于内核对象(kobject),而内核对象的层次化组织直接反映了sysfs的目录布局中。由于系统的所有设备和总线都是通过kobject组织的,所以sysfs提供了系统的硬件拓扑的一种表示。
注意,kobject与sysfs之间的关联不是自动建立的。独立的kobject实例默认情况下并不是集成到sysfs。要使一个对象在sysfs文件系统中可见,需要调用kobject_add。但如果kobject是某个内核子系统的成员,那么向sysfs的注册是自动进行的。
概述
<linux/kobject.h>
struct kobject {
const char * k_name;
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct sysfs_dirent * sd;
};
struct kset {
struct kobj_type *ktype;
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
- kobject包含在一个层次化的组织中。最重要的一点是,他们可以有一个父对象,可以包含到一个kset中。这决定了kobject出现在sysfs层次结构中的位置:如果存在父对象,那么需要在父对象对应的目录中新建立一项。否则,将其放置到kobject所在的kset所属的kobject对应的目录中。
- 每个kobject在sysfs中都表示为一个目录。出现在该目录中的文件是对象的属性。用于导出和设置属性的操作由对象所属的子系统提供。
- 总线、设备、设备驱动程序和类是使用kobject机制的主要内核对象,因而也占据了sysfs中几乎所有的数据项。
数据结构
目录项
<fs/sysfs/sysfs.h>
struct sysfs_dirent {
atomic_t s_count;
atomic_t s_active;
struct sysfs_dirent *s_parent;
struct sysfs_dirent *s_sibling;//用于连接同一父结点所有子结点
const char *s_name;
union {
struct sysfs_elem_dir s_dir;
struct sysfs_elem_symlink s_symlink;
struct sysfs_elem_attr s_attr;
struct sysfs_elem_bin_attr s_bin_attr;
};
unsigned int s_flags;//设置sysfs数据项的类型
ino_t s_ino;
umode_t s_mode;
struct iattr *s_iattr;//属性
};
根据sysfs数据项的类型不同,与之关联的数据类型也不同。由于一个数据项一次只能表示一种类型,封装了与sysfs项相关数据的结构,都群集到一个匿名联合中。
<fs/sysfs/sysfs.h>
struct sysfs_elem_dir {//目录
struct kobject *kobj;
/* children list starts here and goes through sd->s_sibling */
struct sysfs_dirent *children;//将所有子结点通过s_sibling连接起来。
};
struct sysfs_elem_symlink {//符号链接
struct sysfs_dirent *target_sd;
};
struct sysfs_elem_attr {//指向表示属性的数据结构的指针
struct attribute *attr;
struct sysfs_open_dirent *open;
};
struct sysfs_elem_bin_attr {//指向表示属性的数据结构的指针
struct bin_attribute *bin_attr;
};
与任何其他的文件系统类似,sysfs数据项也由struct dentry实例表示。两种层次的表示之间,通过dentry->d_fsdata建立关联。
属性
<linux/sysfs.h>
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
struct attribute_group {//属性组
const char *name;
struct attribute **attrs;
};
对于可读写的属性,需要提供两个方法。
<sysfs.h>
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
对于二进制属性,用于读取和修改数据的方法,通常对每个属性都是不同的。这一点反映到了数据结构中:
<sysfs.h>
struct bin_attribute {
struct attribute attr;
size_t size;
void *private;
ssize_t (*read)(struct kobject *, struct bin_attribute *,
char *, loff_t, size_t);
ssize_t (*write)(struct kobject *, struct bin_attribute *,
char *, loff_t, size_t);
int (*mmap)(struct kobject *, struct bin_attribute *attr,
struct vm_area_struct *vma);
};
声名新属性
考虑通用硬盘代码如何定义一个结构,即可将一个属性以及读写该属性的方法关联起来:
<genhd.h>
struct disk_attribute {
struct attribute attr;
ssize_t (*show)(struct gendisk *, char *);
ssize_t (*store)(struct gendisk *, const char *, size_t);
};
block/genhd.c
static struct sysfs_ops disk_sysfs_ops = {
.show = &disk_attr_show,
.store = &disk_attr_store,
};
block/genhd.c
static ssize_t disk_attr_show(struct kobject *kobj, struct attribute *attr,
char *page)
{
struct gendisk *disk = to_disk(kobj);
struct disk_attribute *disk_attr =
container_of(attr,struct disk_attribute,attr);
ssize_t ret = -EIO;
if (disk_attr->show)
ret = disk_attr->show(disk,page);
return ret;
}
装载文件系统
mount系统调用最终将填充超级块的工作委托给sys_fill_super。
static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = SYSFS_MAGIC;
sb->s_op = &sysfs_ops;
sb->s_time_gran = 1;
sysfs_sb = sb;
/* get root inode, initialize and unlock it */
inode = sysfs_get_inode(&sysfs_root);//创建一个新的struct inode实例,作为整个sysfs树的起点
if (!inode) {
pr_debug("sysfs: could not get root inode\n");
return -ENOMEM;
}
/* instantiate and link root dentry */
root = d_alloc_root(inode);//创建root dentry
if (!root) {
pr_debug("%s: could not get root dentry!\n",__FUNCTION__);
iput(inode);
return -ENOMEM;
}
root->d_fsdata = &sysfs_root;//在sysfs_dirent和dentry之间建立一个关联
sb->s_root = root;
return 0;
}
初始化sysfs inode的操作
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
struct bin_attribute *bin_attr;
inode->i_blocks = 0;
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;
inode->i_ino = sd->s_ino;
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
if (sd->s_iattr) {
/* sysfs_dirent has non-default attributes
* get them for the new inode from persistent copy
* in sysfs_dirent
*/
set_inode_attr(inode, sd->s_iattr);
} else
set_default_inode_attr(inode, sd->s_mode);
/* initialize inode according to type */
switch (sysfs_type(sd)) {
case SYSFS_DIR:
inode->i_op = &sysfs_dir_inode_operations;
inode->i_fop = &sysfs_dir_operations;
inode->i_nlink = sysfs_count_nlink(sd);
break;
case SYSFS_KOBJ_ATTR:
inode->i_size = PAGE_SIZE;
inode->i_fop = &sysfs_file_operations;
break;
case SYSFS_KOBJ_BIN_ATTR:
bin_attr = sd->s_bin_attr.bin_attr;
inode->i_size = bin_attr->size;
inode->i_fop = &bin_fops;
break;
case SYSFS_KOBJ_LINK:
inode->i_op = &sysfs_symlink_inode_operations;
break;
default:
BUG();
}
unlock_new_inode(inode);
}
文件和目录操作
实现文件系统操作的函数在sysfs和内部数据结构之间充当了胶水层的角色。
sysfs内部与虚拟文件系统之间的关联是在打开文件时建立的。
打开文件
为便于在用户层和sysfs实现之间交换数据,内核需要提供一些缓冲区:
struct sysfs_buffer {
size_t count;//指定缓冲区中数据的长度
loff_t pos;//内部数据当前位置
char * page;//指向一页,用于存储数据
struct sysfs_ops * ops;//属于与打开文件想关联的sysfs数据项
struct mutex mutex;
int needs_read_fill;
int event;
struct list_head list;//每个打开的文件都由一个struct file的实例表示,
//它通过file->private_data关联到一个sysfs_buffer的实例。
//多个打开的文件可以引用同一sysfs数据项,
//因此多个sysfs_buffer可以关联到同一个struct sysfs_dirent实例。
//所有这些缓冲区都集中到一个链表中,链表元素是sysfs_buffer->list。
//表头是sysfs_open_dirent(buffers字段)的一个实例,
//并且sysfs_open_dirent关联到sysfs_dirent(sysfs_elem_attr.open)
};
sysfs_file_operations提供了sys_open_file方法,在打开文件时调用
<fs/sysfs/file.c>
static int sysfs_open_file(struct inode *inode, struct file *file)
{
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
struct sysfs_buffer * buffer;
struct sysfs_ops * ops = NULL;
int error;
/* need attr_sd for attr and ops, its parent for kobj */
if (!sysfs_get_active_two(attr_sd))//获得与sysfs数据项关联的kobject实例的活动引用。
return -ENODEV;
/* if the kobject has no ktype, then we assume that it is a subsystem
* itself, and use ops for it.
*/
//查找打开文件的sysfs_ops操作
if (kobj->kset && kobj->kset->ktype)
ops = kobj->kset->ktype->sysfs_ops;
else if (kobj->ktype)
ops = kobj->ktype->sysfs_ops;
else
ops = &subsys_sysfs_ops;
error = -EACCES;
/* No sysfs operations, either from having no subsystem,
* or the subsystem have no operations.
*/
if (!ops)
goto err_out;
/* File needs write support.
* The inode's perms must say it's ok,
* and we must have a store method.
*/
if (file->f_mode & FMODE_WRITE) {
if (!(inode->i_mode & S_IWUGO) || !ops->store)
goto err_out;
}
/* File needs read support.
* The inode's perms must say it's ok, and we there
* must be a show method for it.
*/
if (file->f_mode & FMODE_READ) {
if (!(inode->i_mode & S_IRUGO) || !ops->show)
goto err_out;
}
/* No error? Great, allocate a buffer for the file, and store it
* it in file->private_data for easy access.
*/
error = -ENOMEM;
buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);//分配一个sysfs_buffer的实例,
if (!buffer)
goto err_out;
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
buffer->ops = ops;
file->private_data = buffer;//关联
/* make sure we have open dirent struct */
error = sysfs_get_open_dirent(attr_sd, buffer);
if (error)
goto err_free;
/* open succeeded, put active references */
sysfs_put_active_two(attr_sd);
return 0;
err_free:
kfree(buffer);
err_out:
sysfs_put_active_two(attr_sd);
return error;
}
读写文件的内容
读取数据
sysfs_read_file->fill_read_buffer
->simple_read_from_buffer
写入数据
sysfs_write_file->fill_write_buffer
->flush_write_buffer
目录遍历
sysfs_dir_inode_operations的lookup方法是目录遍历的关键。
<fs/sysfs/dir.c>
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct dentry *ret = NULL;
struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
struct sysfs_dirent *sd;
struct inode *inode;
mutex_lock(&sysfs_mutex);
sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
/* no such entry */
if (!sd) {
ret = ERR_PTR(-ENOENT);
goto out_unlock;
}
/* attach dentry and inode */
inode = sysfs_get_inode(sd);
if (!inode) {
ret = ERR_PTR(-ENOMEM);
goto out_unlock;
}
/* instantiate and hash dentry */
dentry->d_op = &sysfs_dentry_ops;
dentry->d_fsdata = sysfs_get(sd);
d_instantiate(dentry, inode);
d_rehash(dentry);
out_unlock:
mutex_unlock(&sysfs_mutex);
return ret;
}
struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
const unsigned char *name)
{
struct sysfs_dirent *sd;
for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
if (!strcmp(sd->s_name, name))
return sd;
return NULL;
}
属性组成了目录的各个目录项,该函数试图找到一个具有特定名称、属于某个struct sysfs_dirent实例对应的目录下的属性。
向sysfs添加内容
p562
Linux内核入门到放弃-无持久存储的文件系统-《深入Linux内核架构》笔记的更多相关文章
- Linux内核入门到放弃-进程管理和调度-《深入Linux内核架构》笔记
进程优先级 硬实时进程 软实时进程 普通进程 O(1)调度.完全公平调度器 抢占式多任务处理(preemptive multitasking):各个进程都分配到一定的时间段可以执行.时间段到期后,内核 ...
- Linux从入门到放弃、零基础入门Linux(第三篇):在虚拟机vmware中安装linux(二)超详细手把手教你安装centos6分步图解
一.继续在vmware中安装centos6.9 本次安装是进行最小化安装,即没有图形化界面的安装,如果是新手,建议安装带图形化界面的centos, 具体参考Linux从入门到放弃.零基础入门Linux ...
- Linux从入门到放弃、零基础入门Linux(第四篇):在虚拟机vmware中安装centos7.7
如果是新手,建议安装带图形化界面的centos,这里以安装centos7.7的64位为例 一.下载系统镜像 镜像文件下载链接https://wiki.centos.org/Download 阿里云官网 ...
- Linux内核入门到放弃-设备驱动程序-《深入Linux内核架构》笔记
I/O体系结构 总线系统 PCI(Peripheral Component Interconnect) ISA(Industrial Standard Architecture) SBus IEEE1 ...
- Linux内核入门到放弃-页缓存和块缓存-《深入Linux内核架构》笔记
内核为块设备提供了两种通用的缓存方案. 页缓存(page cache) 块缓存(buffer cache) 页缓存的结构 在页缓存中搜索一页所花费的时间必须最小化,以确保缓存失效的代价尽可能低廉,因为 ...
- Linux从入门到放弃
Ch.0 几点Linux常识 Linux严格区分大小写,不像windows中命令是不区分大小写的 Linux中所有内容以文件形式保存,包括硬件 Linux不靠扩展名区分文件类型,所有扩展名只是为了方便 ...
- Linux从入门到放弃、零基础入门Linux(第一篇):计算机操作系统简介、linux介绍
一.计算机操作系统简介 操作系统的定义: 操作系统是一个用来协调.管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间. 操作系统的内核的定义: 操作系统的内核是一个管理和控制程序,负责 ...
- Linux内核入门到放弃-网络-《深入Linux内核架构》笔记
网络命名空间 struct net { atomic_t count; /* To decided when the network * namespace should be freed. */ a ...
- Linux内核入门到放弃-页面回收和页交换-《深入Linux内核架构》笔记
概述 可换出页 只有少量几种页可以换出到交换区,对其他页来说,换出到块设备上与之对应的后备存储器即可,如下所述. 类别为 MAP_ANONYMOUS 的页,没有关联到文件,例如,这可能是进程的栈或是使 ...
随机推荐
- 【Java并发编程】Callable、Future和FutureTask的实现
启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future 下面就分析一下Callable.Future以及FutureTask的 ...
- 解决端口冲突问题(查询端口占用进程并kill)
1. 查看端口占用 在windows命令行窗口下执行: netstat -aon|findstr "8080" TCP 127.0.0.1:80 0.0.0.0:0 LISTENI ...
- 痞子衡嵌入式:串口调试工具Jays-PyCOM诞生记(1)- 环境搭建(Python2.7.14 + pySerial3.4 + wxPython4.0.3)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是串口调试工具Jays-PyCOM诞生之环境搭建. 在写Jays-PyCOM时需要先搭好开发和调试环境,下表列出了开发过程中会用到的所有软 ...
- #5 Python面向对象(四)
前言 本节将是Python面向对象的最后一篇博文了,这节将记录类的特殊方法.特殊成员方法.旧类和新类的不同,以及一些其他知识.Go! 一.类的特殊方法 Python有三种特殊方法:实例方法.静态方法. ...
- HTTP与HTTPS的理解
最近一直也在面试的过程中,可能由于各个方面的问题,导致没有时间抽出更新博客,今天开始陆续更新!!!以后自己的博客,会向React Native,swift ,以及H5延展,成为一个全栈的技术人员.本篇 ...
- Asp.NetCore程序发布到CentOs(含安装部署netcore)--最佳实践(一)
环境 本地 win7 服务器:Virtual Box 上的Centos ssh工具: Xshell 文件传输: xftp 1.在本地创建asp.net core应用发布 1.1 使用Vs2017 新建 ...
- C#生成随机数的三种方法
随机数的定义为:产生的所有数字毫无关系. 在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号. 在C#中获取随机数有三种方法: 一.Random 类 Random类默认的无参构造函数可以根据当 ...
- C# 添加、修改以及删除Excel迷你图表的方法
Excel表格中的迷你图表能够直观地向我们展示出数据的变化趋势.本文将介绍C#如何实现为表格数据生成迷你图表,以及修改和删除迷你图表的方法.下面将详细讲述. 所用组件工具:Spire.XLS for ...
- 2013年第四届蓝桥杯javaB组 试题 答案 解析
1.世纪末的星期 曾有邪教称1999年12月31日是世界末日.当然该谣言已经不攻自破. 还有人称今后的某个世纪末的12月31日,如果是星期一则会.... 有趣的是,任何一个世纪末的年份的12月31日都 ...
- Dynamics 365-RetrieveDependenciesForDeleteRequest
不少人在使用Dynamics 365的时候,或多或少都会遇到Delete a Component的情况,比如Unregister a Plugin/Workflow. 想象这么一个常见的情形:你定制了 ...