linux内核源码分析之proc文件系统(三)
一、数据结构分析
1、proc数据项的表示proc_dir_entry
2、proc inode
二、proc初始化
三、管理/proc数据项
1. 数据项的创建和注册
2. 查找proc数据项
三、读取和写入信息
四、进程相关的信息
1、创建目录inode
2、处理文件
一、数据结构分析
1、proc数据项的表示proc_dir_entry
proc文件系统中的每个数据项都由proc_dir_entry 的一个实例描述
struct proc_dir_entry {spinlock_t pde_unload_lock;struct completion *pde_unload_completion;const struct inode_operations *proc_iops;union {const struct proc_ops *proc_ops;const struct file_operations *proc_dir_ops;};const struct dentry_operations *proc_dops;proc_write_t write;...}
实例
struct proc_dir_entry proc_root = {.low_ino= PROC_ROOT_INO, .namelen= 5, .mode= S_IFDIR | S_IRUGO | S_IXUGO, .nlink= 2, .refcnt= REFCOUNT_INIT(1),.proc_iops= &proc_root_inode_operations, //对proc根目录几乎不能做什么.proc_dir_ops= &proc_root_operations,//proc目录是特别的,因为其中包含了目录,因而不能对该目录使用通用处理函数.parent= &proc_root,.subdir= RB_ROOT,.name= "/proc",};
2、proc inode
内核提供了一个数据结构,称之为 proc_inode ,支持以面向 inode 的方式来查看 proc 文件系统的 数据项。
//proc的数据与VFS层的inode数据关联起来,struct proc_inode {struct pid *pid;unsigned int fd;union proc_op op;struct proc_dir_entry *pde;//pde是一个指针,指向关联到proc数据项的proc_dir_entry实例struct ctl_table_header *sysctl;struct ctl_table *sysctl_entry;struct hlist_node sysctl_inodes;const struct proc_ns_operations *ns_ops;struct inode vfs_inode;} __randomize_layout;
两个结构体之间的关系
proc_inode中的成员pde是一个指针,指向关联到proc数据项的proc_dir_entry实例
二、proc初始化
初始化,装载文件系统
三、管理/proc数据项
在 proc 文件系统投入使用之前,必须向其中添加数据项。内核提供了几个辅助例程来添加文件、 创建目录
1. 数据项的创建和注册
内核中有相关的宏定义
#define proc_create_seq_private(name, mode, parent, ops, size, data) ({NULL;})#define proc_create_seq_data(name, mode, parent, ops, data) ({NULL;})#define proc_create_seq(name, mode, parent, ops) ({NULL;})#define proc_create_single(name, mode, parent, show) ({NULL;})#define proc_create_single_data(name, mode, parent, show, data) ({NULL;})#define proc_create(name, mode, parent, proc_ops) ({NULL;})#define proc_create_data(name, mode, parent, proc_ops, data) ({NULL;})
在创建了数据项之后,绑定相关的操作函数 文件相关的 :proc_ops, proc_file_inode_operations 目录相关的 :proc_dir_operations,proc_dir_inode_operations 连接相关的 :proc_link_inode_operations使用 proc_register 将其注册到 proc 文件系统,5.6.18 内核先绑定相关的操作函数如
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,struct proc_dir_entry *parent,const struct proc_ops *proc_ops, void *data){struct proc_dir_entry *p;p = proc_create_reg(name, mode, &parent, data);if (!p)return NULL;p->proc_ops = proc_ops;return proc_register(parent, p);}
2. 查找proc数据项
分为进程pid相关的查找,和普通查找
static const struct inode_operations proc_root_inode_operations = {.lookup= proc_root_lookup,.getattr= proc_root_getattr,};static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags){if (!proc_pid_lookup(dentry, flags))//PIDreturn NULL;return proc_lookup(dir, dentry, flags);}
三、读取和写入信息
使用proc_ops, 而不是file_operations
struct proc_ops {int(*proc_open)(struct inode *, struct file *);ssize_t(*proc_read)(struct file *, char __user *, size_t, loff_t *);ssize_t(*proc_write)(struct file *, const char __user *, size_t, loff_t *);loff_t(*proc_lseek)(struct file *, loff_t, int);int(*proc_release)(struct inode *, struct file *);__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);long(*proc_ioctl)(struct file *, unsigned int, unsigned long);#ifdef CONFIG_COMPATlong(*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);#endifint(*proc_mmap)(struct file *, struct vm_area_struct *);unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);};
这里以读取CPU信息为例
extern const struct seq_operations cpuinfo_op;static int cpuinfo_open(struct inode *inode, struct file *file){arch_freq_prepare_all();return seq_open(file, &cpuinfo_op);}//读写相关的操作函数 static const struct proc_ops cpuinfo_proc_ops = {.proc_open= cpuinfo_open,.proc_read= seq_read,.proc_lseek= seq_lseek,.proc_release= seq_release,};//创建cpuinfo文件static int __init proc_cpuinfo_init(void){proc_create("cpuinfo", 0, NULL, &cpuinfo_proc_ops);return 0;}fs_initcall(proc_cpuinfo_init);
四、进程相关的信息
1、创建目录inode
//查看进程信息struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags){struct task_struct *task;unsigned tgid;struct pid_namespace *ns;struct dentry *result = ERR_PTR(-ENOENT);tgid = name_to_int(&dentry->d_name);if (tgid == ~0U)goto out;ns = dentry->d_sb->s_fs_info;rcu_read_lock();task = find_task_by_pid_ns(tgid, ns);if (task)get_task_struct(task);rcu_read_unlock();if (!task)goto out;result = proc_pid_instantiate(dentry, task, NULL);put_task_struct(task);out:return result;}
2、处理文件
因为特定于 PID 的目录,其内容总是同样的,内核源代码中定义了所有文件的静态列表以及其他一些信息,内核定义了静态参数.
static const struct pid_entry tgid_base_stuff[] = {REG("cmdline", S_IRUGO, proc_pid_cmdline_ops),ONE("stat",S_IRUGO, proc_tgid_stat),ONE("statm", S_IRUGO, proc_pid_statm),REG("maps",S_IRUGO, proc_pid_maps_operations),...}
参考内核学习连接
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂