又开学了呢..寒假在家沉迷血源,并没怎么看书.新学期继续努力吧.
最近在看文件系统相关的东西,于是想起之前写的读取/proc下的文件监控系统数据的程序,proc也是一种文件系统,不过他只存在于内存中,所以叫做伪文件系统.也是早期用户能简单和内核进行交互的方式之一,现在也许用在调试内核程序比较多(?).
还是以模块的形式加载我们制作的动态文件系统.先是注册模块,
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#define PROC_NAME "hello_proc"
static int __init my_proc_init(void) {
printk(KERN_INFO "init and create proc");
proc_create(PROC_NAME, 0, NULL, &hello_proc_fops);
}
static void __exit my_proc_exit(void) {
remove_proc_entry(PROC_NAME, NULL);
}
MODULE_LICENSE("GPL");
module_init(my_proc_init);
module_exit(my_proc_exit);
注册my_proc_init为模块的入口,在加载的时候就会调用my_proc_init函数,同理卸载的时候调用my_proc_exit.
之后调用proc_create这个接口为我们在/proc下创建一个名为hello_proc的文件.
此外看了下网上还有使用create_proc_entry的方法,但是我这边无法编译说找不到这个函数定义,差了下这个方法在3.10以上的版本的内核中已经被弃用了.so..//
先来看看这个函数的定义
static inline struct proc_dir_entry *proc_create(
const char *name,
mode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops)
第一个name是你的proc文件的名字,第二个是你创建的文件的权限,我们填0表示的权限是是0444,安装之后是这样的:
[root@localhost proc]# ls /proc/hello_proc -la
-r--r--r--. 1 root root 0 Feb 23 17:25 /proc/hello_proc
第三个参数是你创建的文件位置(可以这么说吧),你可以通过proc_mkdir现在/proc下创建一个文件夹项目,在把这个文件夹传入给parent,这样proc文件就生成在了你创建的目录项下了.NULL就表示直接创建.
最后一个file_operations就是该文件的操作函数了.我们通过一个结构体来定义.
static const struct file_operations hello_proc_fops = {
.owner = THIS_MODULE,
.open = hello_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
想看完整结构在linux/include/linux/fs.h中.
下面三个基本不会变,主要是有一个.open和.write,分别记录文件被读取和写入时的动作.
static int hello_proc_show(struct seq_file *m, void *v) {
seq_printf(m, "Hello proc!\n");
return 0;
}
static int hello_proc_open(struct inode *inode, struct file *file) {
return single_open(file, hello_proc_show, NULL);
}
先用seq_file对象打开文件,再使用seq_file中定义的标准输出的方法输出字符.
至于这个seq_file是个司马东西,大致就是虽然以前就有proc系统的存在,但是在数据大小使用方面很有局限性,于是某人就想了这个方法,用迭代器的形式迭代遍历元素,比如你有一个结构体的数组,就可以用pos来记录迭代的位置.
一个很典型的例子就是/proc/device,如果你cat这个文件就会打印出所有的设备名.看下源代码
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
static int devinfo_show(struct seq_file *f, void *v)
{
int i = *(loff_t *) v;
if (i < CHRDEV_MAJOR_HASH_SIZE) {
if (i == 0)
seq_puts(f, "Character devices:\n");
chrdev_show(f, i);
}
#ifdef CONFIG_BLOCK
else {
i -= CHRDEV_MAJOR_HASH_SIZE;
if (i == 0)
seq_puts(f, "\nBlock devices:\n");
blkdev_show(f, i);
}
#endif
return 0;
}
static void *devinfo_start(struct seq_file *f, loff_t *pos)
{
if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
return pos;
return NULL;
}
static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
return NULL;
return pos;
}
static void devinfo_stop(struct seq_file *f, void *v)
{
/* Nothing to do */
}
static const struct seq_operations devinfo_ops = {
.start = devinfo_start,
.next = devinfo_next,
.stop = devinfo_stop,
.show = devinfo_show
};
static int devinfo_open(struct inode *inode, struct file *filp)
{
return seq_open(filp, &devinfo_ops);
}
static const struct file_operations proc_devinfo_operations = {
.open = devinfo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int __init proc_devices_init(void)
{
proc_create("devices", 0, NULL, &proc_devinfo_operations);
return 0;
}
module_init(proc_devices_init);
devinfo_ops中记录着迭代的操作,顺序时start->show->next->show….一直到结束调用stop.在next中每次把*pos++记录循环的次数,然后返回给static int devinfo_show(struct seq_file *f, void *v)中的v.
当然如果数据够小也可以直接用for进行循环,这样就和我们平时写的没什么区别了.
之后写个Makefile编译成模块.
SOURCE = procfs.c
obj-m += procfs.o
proc-objs := $(SOURCE:.c=.o)
all:
make -C /usr/src/kernels/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
如果说找不到目录的话请检查下/usr/src/kernels/$(shell uname -r)/build指向的地址再把上面替换调(神tm内核版本和kernel-headers版本是不一样的.)
[root@localhost proc]# insmod procfs.ko
[root@localhost proc]# cat /proc/
[root@localhost proc]# cat /proc/hello_proc
Hello proc!
有兴趣可以读下https://github.com/jesstess/ldd4/blob/master/scull/main.c,写的方法比较全.
结束结束.