1. 简介
procfs文件系统(/proc)在linux内核中算是一个比较特殊的文件系统。它是一个虚拟的文件系统:它并没有关联到具体的块设备,而是存在于内存中。procfs中的文件存在的目的在于允许用户程序从内核获取信息(例如proc目录下以数字开头的文件)以及debug程序(如/proc/ksyms).
本文介绍procfs文件系统在linux 内核的使用。首先介绍所有用于管理procfs文件系统文件的函数。然后介绍与用户程序的交互和某些技巧。最后展示一个完整的例子。
注意:/proc/sys目录下的文件是sysctl文件。他们不属于procfs,由完全不同的API管理(参见其他kernel api的相关书籍)
2.管理procfs项
这部分介绍内核空间在procfs目录下中创建文件、符号链接、设备节点和目录所用到的函数。
注意: 必须包含头文件:
#include <linux/proc_fs.h>
创建常规文件:
struct proc_dir_entry* create_proc_entry( |
const char* name, |
|
mode_t mode, |
|
struct proc_dir_entry* parent); |
该函数用于在目录parent下创建名叫name的常规文件,访问权限由mode指定。 parent 为NULL则在/procfs目录下创建文件。 成功时,函数返回指向新创建的结构体 struct proc_dir_entry的指针,失败返回NULL。文章的第三部分将介绍对新创建的文件的一些操作。
注意: 你可以传递一个多级目录,例如 create_proc_entry("dirvers/via0/info") 必要时会以权限0755创建via0目录。
如果想创建只读文件,则可以考虑使用函数create_proc_read_entry来一次性创建并初始化procfs项。
创建符号链接
struct proc_dir_entry* proc_symlink( |
const char* name, |
|
struct proc_dir_entry* parent, |
|
const char* dest); |
该函数在目录prarent下创建指向dest的符号链接name。相当于用户模式的:ln -s
创建目录
struct proc_dir_entry* proc_mkdir( |
const char* name, |
|
struct proc_dir_entry* parent);
|
该函数在parent目录下创建目录name。
删除项
void remove_proc_entry( |
const char* name, |
|
struct proc_dir_entry* parent); |
该函数在procfs系统的parent目录下删除name。 节点通过名字来删除,而不是通过创建时返回的proc_dir_entry结构体。本函数并不递归删除项目
注意: 必须在调用remove_proc_entry之前释放掉相对应的结构体struct proc_dir_enrty
3.用户进程交互
procfs文件系统不直接从内核内存中读写数据,而是使用文件的回调函数:当特定文件被读写时嗦调用的函数。这些函数必须在procfs文件创建以后通过设置结构体struct proc_dir_entry(由create_proc_entry函数返回) 中的read_proc跟write_proc字段来进行初始化。示例代码如下:
struct proc_dir_entry* entry;
entry->read_proc = read_proc_foo;
entry->write_proc = write_proc_foo;
如果仅使用read_proc.则可以考虑使用函数create_proc_read_entry来一次性创建并初始化procfs项。
读取数据:
read函数是允许用户程序从内核中读取数据的回调函数,它的声明如下:
int read_func( |
char* buffer, |
|
char** start, |
|
off_t off, |
|
int count, |
|
int* peof, |
|
void* data); |
read函数把它所提供的信息吸入buffer指向的内存,这块内存的大小为PAGE_SIZE。
*peof置为1用作到达文件结尾的标识。
参数data可以被用来创建为多个文件创建同一个回调函数。
函数返回值金和剩余参数在文件fs/proc/generic.c的注释中有所描述,这些描述如下:
将*start置为NULL(默认就这样)。将请求的数据写到buffer中 返回世界写入的字节数
写入数据
写回调函数允许用户程序向内核写入数据,因此可以控制内核的部分。写函数的声明如下:
int write_func( |
struct file* file, |
|
const char* buffer, |
|
unsigned long count, |
|
void* data); |
写回调函数从buffer中读取count字节。注意buffer并不在内核内存空间中,所以必须先用copy_from_user拷贝到内科空间,参数file通常被忽略。文章的第五部分有例子。
为多个文件建立单个回调函数
当存在大量几乎相等的文件的时候,为每个文件建立单独的回调函数就很不方便。更好的办法是为这些文件使用同一个回调函数,而这个回调函数通过结构体proc_dir_entry的data字段来区分不通的文件。首先,应该初始化data字段:
struct proc_dir_entry* entry;
struct my_file_data *file_data;
file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL);
entry->data = file_data;
data字段的类型是void* ,