linux内核和用户通信方法中有一个叫做debugfs,它的作用类似于sysctl,但是是通过文件系统实现的,本文不准备再详细的说debugfs的实现,像前面seqfile那么详细的说,而是简单列出需要的代码做个纪念,证明今天(2009/2/19)我详细的读过了linux内核的debugfs。
#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) /
static int __fops ## _open(struct inode *inode, struct file *file) /
{ /
__simple_attr_check_format(__fmt, 0ull); /
return simple_attr_open(inode, file, __get, __set, __fmt); /
} /
static struct file_operations __fops = { /
.owner = THIS_MODULE, /
.open = __fops ## _open, /
.release = simple_attr_close, /
.read = simple_attr_read, /
.write = simple_attr_write, /
};
以上这个宏简直太帅了,它其实定义了debugfs的file_operations,因为debugfs不是通过一个file_operations来实现的,而是每一个数据类型一个,因此用到了上面的宏来为如此繁多的file_operations服务。
int simple_attr_open(struct inode *inode, struct file *file, u64 (*get)(void *), void (*set)(void *, u64), const char *fmt)
{
struct simple_attr *attr; //重要结构
attr = kmalloc(sizeof(*attr), GFP_KERNEL);
if (!attr)
return -ENOMEM;
attr->get = get;
attr->set = set;
attr->data = inode->u.generic_ip;
attr->fmt = fmt;
mutex_init(&attr->mutex);
file->private_data = attr;
return nonseekable_open(inode, file);
}
这个debugfs_create_XX就是定义一个特定数据类型的变量,内核和用户通过该变量进行通信,每一个变量在用户空间挂载的debugfs挂载点路径下都会有一个文件,对该文件的操作就触发了用上面宏定义的file_operations的回调函数调用事件。
struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *parent, u8 *value)
{
return debugfs_create_file(name, mode, parent, value, &fops_u8);
}
struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data, const struct file_operations *fops)
{
struct dentry *dentry = NULL;
int error;
error = simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count);
...
error = debugfs_create_by_name(name, mode, parent, &dentry);
...
if (dentry->d_inode) {
if (data)
dentry->d_inode->u.generic_ip = data;
if (fops)
dentry->d_inode->i_fop = fops;
}
...
}
int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count)
{
struct vfsmount *mnt = NULL;
spin_lock(&pin_fs_lock);
if (unlikely(!*mount)) {
spin_unlock(&pin_fs_lock);
mnt = vfs_kern_mount(type, 0, type->name, NULL);
...
spin_lock(&pin_fs_lock);
if (!*mount)
*mount = mnt;
}
...
return 0;
}
下面的这个read函数就是那个宏定义的file_operations中的read,真正实现在attr中的get例程
ssize_t simple_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
{
struct simple_attr *attr;
size_t size;
ssize_t ret;
attr = file->private_data;
if (!attr->get)
return -EACCES;
mutex_lock(&attr->mutex);
if (*ppos)
size = strlen(attr->get_buf);
else
size = scnprintf(attr->get_buf, sizeof(attr->get_buf), attr->fmt,(unsigned long long)attr->get(attr->data));
ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size);
mutex_unlock(&attr->mutex);
return ret;
}
最后来看看对于u8的file_operations是怎么实现的:
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu/n");
static void debugfs_u8_set(void *data, u64 val)
{
*(u8 *)data = val;
}
static u64 debugfs_u8_get(void *data)
{
return *(u8 *)data;
}
看完了这一切后,我不禁感叹,linux中如此简单就可以实现一个机制,太妙了。前面的seqfile是自成体系的一套机制,它强调一种抽象的思想,而这个debugfs是用文件系统实现的,linux的vfs简直太强大了,这都要归功于file_operations结构体,它就是一个适配器,适配了上面统一的调用层和下面不同的文件系统,file_operations是机制,它的实现是策略,既然是策略,那它就可以随意实现了,于是你想实现一个内核机制的时候,文件系统总会帮你,也正是因为如此,一般的内核新机制都是在文件系统上实现的。