注意:适用于2.7-3.7,3.10之后的版本procfs又做了改变,因此编译最后的源码会出现错误。如果编译出现”dereferencing pointer to incomplete type“类似的错误,那就是版本变更了。新版本里用的是file_operation。
首先要明白,procfs终究还是fs,也就是属于filesystem的一种。因此可以在内核文档中找到相关资料。如果你手头上有一份内核源码,那么可以根据以下相对路径找到这份说明:
./Documentation/filesystems/proc.txt
直接在网上也可以看到这份文档:
http://lxr.oss.org.cn/source/Documentation/filesystems/proc.txt
比较“官方”的procfs的API文档:
http://kernelnewbies.org/Documents/Kernel-Docbooks?action=AttachFile&do=get&target=procfs-guide_2.6.29.pdf
从url中可以看出,这是一份2.6版本内核所写的文档。目前而言,算是最新的了。毕竟2.6和2.4这两个版本之间的差距有点大。http://kernelnewbies.org这个网站里有很多适合Linux新手可以使用的东西,有兴趣可以收藏一下,以后查资料什么的也有个地方。
下面开始对一些关键API的解释,源码可以在以下相对路径中找到:
./kernel/fs/proc/generic.c
1.struct proc_dir_entry* create_proc_entry(const char* name, mode_t mode,struct proc_dir_entry* parent);
@name:创建出的procfs文件的名字
@mode:文件的权限,跟linux文件权限一样,默认为755
@parent:文件的父母节点,为NULL则会直接创建在’/proc‘目录下。可以不为空,详情参考下面一条API
创建一个procfs文件。表现是会在‘/proc’目录下多出一个名为@name的文件
2.struct proc_dir_entry* proc_mkdir(const char* name, struct proc_dir_entry*parent);
@name:文件夹的名字
@parent:父节点,与上面的API一样,可以为空
这个函数创建了一个文件夹,因此如果想在‘/proc’下创建一个文件夹,用于存放自定义的proc文件,则可以调用该函数创建一个文件夹。并将该函数的返回值作为‘create_proc_entry’函数的@parent参数。
3.void remove_proc_entry(const char* name, struct proc_dir_entry* parent);
@name:文件名称
@parent:父节点,指的是proc_mkdir创建出来的文件夹,或者为空
根据@name删除已创建的procfs文件,如果有调用上面提到的proc_mkdir
,则后面的parent不能为NULL
4.数据结构:struct proc_dir_entry;直接上源码
struct proc_dir_entry {
unsigned int low_ino;
umode_t mode;
nlink_t nlink;
kuid_t uid;
kgid_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;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
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 */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
u8 namelen;
char name[];
};
在使用的过程中,比较重要的成员有两个:
@read_proc
@write_proc
这两个成员需要直接调用赋值函数指针。代表的意思很明显,就是进行读和写创建的procfs文件时会调用到。另外,由于在系统内部处理过程中,真正读的过程中会调用到另外一个函数’__proc_file_read‘其中有一行:
n = dp->read_proc(page, &start, *ppos,count, &eof, dp->data);
因此赋值的函数需要注意格式,参数应该类似如下:
func(char *page, char **start,off_t off, int count,int *eof, void *data)
写的函数参数与读一致。简单说一下这些参数代表的意义:
@page:指向的是文件内容,也就是打开文件时可以在屏幕上看到的。如名字所示,空
间大小根据page_size决定,一般X86机器上均为4K页。有修改过页大小的则例外
@start:如果是用file_operation进行读写操作时候有用,这里可以忽略
@count:同上,忽略
@eof:需要赋值为’\0’,标明读写结束
@data:procfs的私有地址数据
最后,附上一份删减过的源代码
注意:内核版本3.10之后的版本procfs又做了改变,因此不能最后的源码会出现错误。如果编译出现”dereferencing pointer to incomplete type“类似的错误,那就是版本变更了。新版本里用的是file_operation。
#include
#include
#include
#include
#define MODULE_NAME"procfs_example"
#define MODULE_VERS"1.0"
#define FOOBAR_LEN 8
struct fb_data_t {
char name[FOOBAR_LEN + 1];
char value[FOOBAR_LEN + 1];
};
static struct proc_dir_entry *example_dir, *foo_file;
struct fb_data_t foo_data, bar_data;
static int proc_read_foobar(char *page, char **start,off_t off, int count,int *eof, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
/* DON’T DO THAT - buffer overruns are bad */
len = sprintf(page, "%s = ’%s’\n",fb_data->name, fb_data->value);
return len;
}
static int proc_write_foobar(struct file *file,const char *buffer,
unsigned long count,void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
if(count > FOOBAR_LEN)
len = FOOBAR_LEN;
else
len = count;
if(copy_from_user(fb_data->value, buffer, len))
return -EFAULT;
fb_data->value[len] = '\0';
return len;
}
static int __init init_procfs_example(void)
{
int rv = 0;
/* create directory */
example_dir = proc_mkdir(MODULE_NAME, NULL);
if(example_dir == NULL) {
rv = -ENOMEM;
goto out;
}
example_dir->owner = THIS_MODULE;
foo_file = create_proc_entry("foo", 0644, example_dir);
if(foo_file == NULL) {
rv = -ENOMEM;
goto no_foo;
}
strcpy(foo_data.name, "foo");
strcpy(foo_data.value, "foo");
foo_file->data = &foo_data;
foo_file->read_proc = proc_read_foobar;
foo_file->write_proc = proc_write_foobar;
foo_file->owner = THIS_MODULE;
return rv;
no_foo:
remove_proc_entry("foo", example_dir);
remove_proc_entry(MODULE_NAME, NULL);
out:
return rv;
}
static void __exit cleanup_procfs_example(void)
{
remove_proc_entry("foo", example_dir);
remove_proc_entry(MODULE_NAME, NULL);
printk(KERN_INFO "%s %s removed\n",
MODULE_NAME, MODULE_VERS);
}
module_init(init_procfs_example);
module_exit(cleanup_procfs_example);
MODULE_LICENSE("GPL");