目录
一、数据结构分析
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目录是特别的,因为其中包含了<pid>目录,因而不能对该目录使用通用处理函数
.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))//PID
return 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_COMPAT
long (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
#endif
int (*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内核》
《Linux内核深度解析》