我们已经了解了如何使用 /proc 接口读取和写入 /proc 文件。但是,也可以使用 inode 管理 /proc 文件。主要的优点是可以使用高级功能,例如权限。
在 Linux 中,有一个用于文件系统注册的标准机制。由于每个文件系统都必须有自己的函数来处理 inode 和文件操作[1],因此有一个特殊的结构来保存指向所有这些函数的指针,struct inode_operations,其中包括指向struct file_operations的指针。在 /proc 中,每当我们注册一个新文件时,都允许我们指定哪个struct inode_operations将被用于访问它。这就是我们使用的机制,一个struct inode_operations其中包括指向一个struct file_operations的指针,后者包括指向我们的procfs_read和procfs_write函数的指针。
这里另一个有趣的方面是module_permission函数。每当进程尝试对/proc文件执行某些操作时,都会调用此函数,它可以决定是否允许访问。目前,它仅基于操作和当前用户的 uid(可在current中获得,这是一个指向结构的指针,该结构包含有关当前运行进程的信息),但它可以基于我们喜欢的任何内容,例如其他进程对同一文件执行的操作、一天中的时间或我们收到的最后一个输入。
重要的是要注意,在内核中,读取和写入的标准角色是相反的。读取函数用于输出,而写入函数用于输入。原因是读取和写入指的是用户的角度——如果进程从内核读取某些内容,那么内核需要输出它;如果进程向内核写入某些内容,那么内核会将其作为输入接收。
示例 5-3. procfs3.c
/* * procfs3.c - create a "file" in /proc, use the file_operation way * to manage the file. */ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ #include <linux/proc_fs.h> /* Necessary because we use proc fs */ #include <asm/uaccess.h> /* for copy_*_user */ #define PROC_ENTRY_FILENAME "buffer2k" #define PROCFS_MAX_SIZE 2048 /** * The buffer (2k) for this module * */ static char procfs_buffer[PROCFS_MAX_SIZE]; /** * The size of the data hold in the buffer * */ static unsigned long procfs_buffer_size = 0; /** * The structure keeping information about the /proc file * */ static struct proc_dir_entry *Our_Proc_File; /** * This funtion is called when the /proc file is read * */ static ssize_t procfs_read(struct file *filp, /* see include/linux/fs.h */ char *buffer, /* buffer to fill with data */ size_t length, /* length of the buffer */ loff_t * offset) { static int finished = 0; /* * We return 0 to indicate end of file, that we have * no more information. Otherwise, processes will * continue to read from us in an endless loop. */ if ( finished ) { printk(KERN_INFO "procfs_read: END\n"); finished = 0; return 0; } finished = 1; /* * We use put_to_user to copy the string from the kernel's * memory segment to the memory segment of the process * that called us. get_from_user, BTW, is * used for the reverse. */ if ( copy_to_user(buffer, procfs_buffer, procfs_buffer_size) ) { return -EFAULT; } printk(KERN_INFO "procfs_read: read %lu bytes\n", procfs_buffer_size); return procfs_buffer_size; /* Return the number of bytes "read" */ } /* * This function is called when /proc is written */ static ssize_t procfs_write(struct file *file, const char *buffer, size_t len, loff_t * off) { if ( len > PROCFS_MAX_SIZE ) { procfs_buffer_size = PROCFS_MAX_SIZE; } else { procfs_buffer_size = len; } if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) { return -EFAULT; } printk(KERN_INFO "procfs_write: write %lu bytes\n", procfs_buffer_size); return procfs_buffer_size; } /* * This function decides whether to allow an operation * (return zero) or not allow it (return a non-zero * which indicates why it is not allowed). * * The operation can be one of the following values: * 0 - Execute (run the "file" - meaningless in our case) * 2 - Write (input to the kernel module) * 4 - Read (output from the kernel module) * * This is the real function that checks file * permissions. The permissions returned by ls -l are * for referece only, and can be overridden here. */ static int module_permission(struct inode *inode, int op, struct nameidata *foo) { /* * We allow everybody to read from our module, but * only root (uid 0) may write to it */ if (op == 4 || (op == 2 && current->euid == 0)) return 0; /* * If it's anything else, access is denied */ return -EACCES; } /* * The file is opened - we don't really care about * that, but it does mean we need to increment the * module's reference count. */ int procfs_open(struct inode *inode, struct file *file) { try_module_get(THIS_MODULE); return 0; } /* * The file is closed - again, interesting only because * of the reference count. */ int procfs_close(struct inode *inode, struct file *file) { module_put(THIS_MODULE); return 0; /* success */ } static struct file_operations File_Ops_4_Our_Proc_File = { .read = procfs_read, .write = procfs_write, .open = procfs_open, .release = procfs_close, }; /* * Inode operations for our proc file. We need it so * we'll have some place to specify the file operations * structure we want to use, and the function we use for * permissions. It's also possible to specify functions * to be called for anything else which could be done to * an inode (although we don't bother, we just put * NULL). */ static struct inode_operations Inode_Ops_4_Our_Proc_File = { .permission = module_permission, /* check for permissions */ }; /* * Module initialization and cleanup */ int init_module() { /* create the /proc file */ Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL); /* check if the /proc file was created successfuly */ if (Our_Proc_File == NULL){ printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME); return -ENOMEM; } Our_Proc_File->owner = THIS_MODULE; Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File; Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File; Our_Proc_File->mode = S_IFREG | S_IRUGO | S_IWUSR; Our_Proc_File->uid = 0; Our_Proc_File->gid = 0; Our_Proc_File->size = 80; printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_FILENAME); return 0; /* success */ } void cleanup_module() { remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root); printk(KERN_INFO "/proc/%s removed\n", PROC_ENTRY_FILENAME); } |
仍然渴望获得更多 procfs 示例吗?好吧,首先请记住,有传言说 procfs 即将淘汰,请考虑使用 sysfs 代替。其次,如果您真的无法满足,那么在下面有一个强烈推荐的 procfs 奖励级别linux/Documentation/DocBook/。在您的顶层内核目录中使用 make help 获取有关如何将其转换为您喜欢的格式的说明。示例: make htmldocs 。如果您想自己记录一些与内核相关的内容,请考虑使用此机制。
[1] | 两者之间的区别在于,文件操作处理文件本身,而 inode 操作处理引用文件的方式,例如创建指向它的链接。 |