proc目录是系统模拟出来的一个文件系统,本身并不存在于磁盘上,其中的文件都表示内核参数的信息,这些信息分两类,一类是可都可写的,这 类参数都在“/proc/sys”目录下,另一类是只读的,就是“/proc/sys”目录之外的其他目录和文件,当然这只是一种惯例,实际在其他目录下建立可读写的/proc文件也是可以的。
Linux内核在2.4以后/proc目录文件的建立已经变得很容易,以前版本都还需要构造文件操作结构来实现,现在只需要调用简单函数就可以了。Linux提供了一系列的创建函数
创建一个 proc 文件
根据对 proc 文件的不同使用,内核提供了多种包装函数来创建一个 proc 文件。
方法一:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent)
这是最直接,包装最少的创建方法。
参数 name 是要创建的 proc 文件名。mode 是该文件权限值,例如 S_IRUGO,可传入0表示采用系统默认值。parent 指定该文件的上层 proc 目录项,如果为 NULL,表示创建在 /proc 根目录下。
create_proc_entry() 完成的任务主要包括:检测 mode 值,分配 proc_dir_entry 结构,注册 proc_dir_entry。其定义如下:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent;
nlink_t nlink;
/* 要创建的项是目录,设置其mode */
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
nlink = 2;
} else { //非目录
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
nlink = 1;
}
/* 注意此处:这是create_proc_entry的主要实现函数,该函数也可以用来单独创
* 建一个目录项,二者区别就在于create_proc_entry函数调用下边的
* proc_register函数注册了proc_dir_entry,也就是使用默认的
* proc_file_operations。 */
ent = proc_create(&parent,name,mode,nlink);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}
方法二:
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base);
if (res) {
res->read_proc=read_proc;
res->data=data;
}
return res;
}
如果要创建一个只读的 proc 文件,可以采用 create_proc_read_entry() 这个接口。这个接口其实就是给 proc_dir_entry 多赋了两个值,其中 read_proc 是一个函数指针, data 是 read_proc 调用时传给它一个参数。关于 read_proc 函数,接下来会另行分析。
方法三:
static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent const char *name, mode_t mode, nlink_t nlink)
如果要创建一个 proc 文件,并且不用 proc_fs 默认提供的 file_operations 的话,可以使用 proc_create() 这个函数。
每个 proc 文件也都会用到 file_operations,在调用 create_proc_entry() 创建 proc 文件时,其中一步是调用 proc_register(),proc_register() 会为 proc_dir_entry 提供一个默认的 file_operations,而 proc_create() 与 create_proc_entry() 唯一差别就是不用调用proc_register(),这样在 proc_register() 时就不会设置使用 proc_fs 默认的 file_operations 了。
proc_fs 默认的 file_operations 定义如下:
static const struct file_operations proc_file_operations = {
.llseek = proc_file_lseek,
.read = proc_file_read,
.write = proc_file_write,
};
关于这个 proc_file_operations,后面会继续讲到。
proc_create() 一般在创建使用 seq_file 接口的 proc 文件时会使用。
创建一个 proc 目录
创建一个 proc 目录就调用 struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) 这个函数。参数name 是目录名,parent 是该目录的上层 proc 目录项,如果为 NULL,表示创建在 /proc 根目录下。
删除一个 proc 项
要删除一个 proc 文件或目录就调用
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
注意事项
proc 项创建时要避免已经存在同名的 proc 项,否则注册时内核会报错(但还是会成功返回那个 proc 项),在删除时有时会删不掉(尤其是 proc 目录)。删除一个 proc 目录会把这个目录所有的 proc 文件都删除。
读 proc 文件
proc 文件的读需要自己提供一个 read_proc_t 类型的函数放在 proc_dir_entry 结构中供 proc_file_read() 函数调用。下面是 read_proc_t 类型函数的定义:
typedef int (read_proc_t)(char *page, char **start, off_t off, int count,
int *eof, void *data);
这个函数的定义很复杂,不过当 proc 文件返回的数据量总是小于一个 PAGE_SIZE 时可以简化使用。关于这个函数以及它的那一大串参数我觉得再怎么解释都解释不全,所以还是留给自己去看 proc_file_read() 的代码就明白了。
写 proc 文件
proc 文件的写很简单。write_proc_t 类型函数的定义如下:
typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);
注册目录项
前边说过要使用默认的proc_file_operations,就要注册目录项,也就是create_proc_entry函数中调用的proc_register()函数,该函数的实现主要有三部分:
1.调用get_inode_number函数生成一个唯一的inode号
2.根据不同的目录项类型,初始化不同的文件操作函数集。
3.设置next与parent,使目录项并入目录树中
其定义如下:
static int proc_register(struct proc_dir_entry* dir, struct proc_dir_entry * dp)
{
unsigned int i;
//生成唯一的inode号
i = get_inode_number();
if (i == 0)
return -EAGAIN;
dp->low_ino = i;
//设置合适的文件操作函数
if (S_ISDIR(dp->mode)) {
if (dp->proc_iops == NULL) {
dp->proc_fops = &proc_dir_operations;
dp->proc_iops = &proc_dir_inode_operations;
}
dir->nlink++;
} else if (S_ISLNK(dp->mode)) {
if (dp->proc_iops == NULL)
dp->proc_iops = &proc_link_inode_operations;
} else if (S_ISREG(dp->mode)) {
if (dp->proc_fops == NULL)
dp->proc_fops = &proc_file_operations;
if (dp->proc_iops == NULL)
dp->proc_iops = &proc_file_inode_operations;
}
//将目录项合并到proc目录树中
spin_lock(&proc_subdir_lock);
dp->next = dir->subdir;
dp->parent = dir;
dir->subdir = dp;
spin_unlock(&proc_subdir_lock);
return 0;
}