使用procfs
在《使用sysctl》一文中说到sysctl也是基于procfs设计的,那这次我们来对procfs进行一番了解吧。 在源码的include/linux/proc_fs.h文件里有procfs相关API的声明,其实现在fs/proc/generic.c文件中,由于从linux-3.10版本内核起,create_proc_entry()和create_proc_read_entry()函数被去除了,需使用proc_create()来代替了,先了解下本次学习涉及的内容:
1.proc_create()
在include/linux/proc_fs.h文件里有如下定义:
1.proc_create()
在include/linux/proc_fs.h文件里有如下定义:
static inline struct proc_dir_entry *proc_create( const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops) { return proc_create_data(name, mode, parent, proc_fops, NULL); }
其中,name是创建在/proc目录下的节点(文件、目录)名称,mode是创建的节点的权限,其在include/uapi/linux/stat.h有相关宏(如S_IRWXU),parent指定当前节点的父目录节点,如果没有就设为NULL,proc_fops是设置对该节点的操作回调函数。 该函数默认创建文件节点,如果要创建目录节点呢?那么修改mode参数,把S_IFDIR宏“|”上即可。
2.proc_remove
有了创建函数,那么对应的就是移除函数了,在fs/proc/generic.c文件里有该函数的定义:
2.proc_remove
有了创建函数,那么对应的就是移除函数了,在fs/proc/generic.c文件里有该函数的定义:
void proc_remove(struct proc_dir_entry *de) { if (de) remove_proc_subtree(de->name, de->parent); }
其在删除创建的节点时会把挂在该节点上相应的子节点一并删除。
3.proc_mkdir和proc_mkdir_mode
在上面proc_create函数里说到创建目录节点要修改mode,其实在fs/proc/generic.c文件里有如下定义:
3.proc_mkdir和proc_mkdir_mode
在上面proc_create函数里说到创建目录节点要修改mode,其实在fs/proc/generic.c文件里有如下定义:
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode, struct proc_dir_entry *parent) { return proc_mkdir_data(name, mode, parent, NULL); } struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) { return proc_mkdir_data(name, 0, parent, NULL); }
其最终调用的proc_mkdir_data函数在同一文件里定义如下:
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, struct proc_dir_entry *parent, void *data) { struct proc_dir_entry *ent; if (mode == 0) mode = S_IRUGO | S_IXUGO; ent = __proc_create(&parent, name, S_IFDIR | mode, 2); if (ent) { ent->data = data; if (proc_register(parent, ent) < 0) { kfree(ent); ent = NULL; } } return ent; }
看到了吧,就在mode这里的S_IFDIR,该函数不用多说,也是可以看得懂了。
4.proc_symlink
在fs/proc/generic.c文件里有相应的定义,在include/linux/proc_fs.h文件里有如下声明:
4.proc_symlink
在fs/proc/generic.c文件里有相应的定义,在include/linux/proc_fs.h文件里有如下声明:
extern struct proc_dir_entry *proc_symlink(const char *, struct proc_dir_entry *, const char *);
其中,第一个参数name,第二个参数parent,第三个参数dest,在generic.c文件里是这样定义的,该函数就是在parent目录下创建name节点,该节点为指向dest的软链接,name与dest都以parent作为同级目录处理(如果parent为空则以/proc目录)。
5.struct proc_dir_entry
在fs/proc/internal.h文件里有如下定义:
5.struct proc_dir_entry
在fs/proc/internal.h文件里有如下定义:
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; const struct file_operations *proc_fops; struct proc_dir_entry *next, *parent, *subdir; void *data; atomic_t count; /* use count */ atomic_t in_use; /* number of callers into module in progress; */ /* negative -> it's going away RSN */ 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[]; };
该结构体便于我们对照,在这里不详细说明,下面的结构体也一样,我们本着实践的宗旨,在这里先学会用,后期有专栏来分析这些结构体。
6.struct file_operations
在include/linux/fs.h文件里有如下的结构体:
6.struct file_operations
在include/linux/fs.h文件里有如下的结构体:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); int (*show_fdinfo)(struct seq_file *m, struct file *f); };
下面还是我们的主要环节,上代码:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> struct proc_dir_entry * slam_dir = NULL; struct proc_dir_entry * slam_entry = NULL; static char * dir_name = "slam"; static char * entry_name = "xinu"; static char * symlink_name = "stxinu"; static ssize_t xinu_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char *str = "My name is xinu.\n"; int len = strlen(str); copy_to_user(buf, str, len); if (*ppos == 0) *ppos += len; else len = 0; return len; } static ssize_t xinu_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char str[128]; if (count > PAGE_SIZE) //procfs write and read has PAGE_SIZE limit count = 128; if (copy_from_user(str, buf, count)) { printk("copy_from_user failed!\n"); return -EFAULT; } str[count-1] = '\0'; printk("Your enter :\n%s\n", str); return count; } static const struct file_operations slam_fops = { .owner = THIS_MODULE, .read = xinu_proc_read, .write = xinu_proc_write, }; static __init int procfs_example_init(void) { #ifdef CONFIG_PROC_FS slam_dir = proc_mkdir(dir_name, NULL); if (!slam_dir) { printk("Create directory \"%s\" failed.\n", dir_name); return -1; } slam_entry = proc_create(entry_name, 0666, slam_dir, &slam_fops); if (!slam_entry) { printk("Create file \"%s\" and symlink \"%s\" failed.\n", entry_name, symlink_name); return -1; } proc_symlink(symlink_name, slam_dir, entry_name); #else printk("This module requests the kernel to support procfs,need set CONFIG_PROC_FS configure Y\n"); #endif return 0; } static __exit void procfs_example_exit(void) { #ifdef CONFIG_PROC_FS proc_remove(slam_entry); proc_remove(slam_dir); #endif } module_init(procfs_example_init); module_exit(procfs_example_exit);
对应的Makefile文件内容如下:
obj-m += procfs_example.o CUR_PATH:=$(shell pwd) LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6 all: make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules clean: make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean
对应的源码文件目录树如下:
/home/xinu/xinu/linux_kernel_driver_l1/procfs_example/
├── Makefile
└── procfs_example.c
当编译加载驱动后,我们可以看到在/proc目录下有slam目录,slam目录下有xinu和stxinu两个文件节点,而stxinu则是指向xinu的软链接,即/proc/slam/stxinu->/proc/slam/xinu。
当我们对相应文件节点操作时,有如下输出:
xinu@slam:~$ ls -l /proc/slam/stxinu
lrwxrwxrwx 1 root root 4 May 4 17:30 /proc/slam/stxinu -> xinu
xinu@slam:~$ ls -l /proc/slam/xinu
-rw-rw-rw- 1 root root 0 May 4 17:30 /proc/slam/xinu
xinu@slam:~$ cat /proc/slam/stxinu
My name is xinu.
xinu@slam:~$ cat /proc/slam/xinu
My name is xinu.
xinu@slam:~$ echo “Hello” > /proc/slam/xinu
xinu@slam:~$ echo “Hello” > /proc/slam/stxinu
当我们执行最后两句后,可通过dmesg看到如下输出:
[31451.840871] Your enter :
[31451.840871] Hello
[31462.441119] Your enter :
[31462.441119] Hello
至此,我们再次体验了另一种内核态与用户态交换方式了,继续加油!
参考网址:
https://www.ibm.com/developerworks/cn/linux/l-kerns-usrs2/
http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/17-2552.html
http://brightconan.com/?p=14
http://nano-chicken.blogspot.com/2009/11/linux-modulesiii.html
http://nano-chicken.blogspot.com/2009/12/linux-modules31-procfsvector.html
http://nano-chicken.blogspot.com/2010/04/linux-modules32-procfssymlinkmkdir_16.html
https://lwn.net/Articles/549737/
http://reverland.org/linux-tech/2013/12/24/writing-drivers-in-linuxa-brief-tutorial/
/home/xinu/xinu/linux_kernel_driver_l1/procfs_example/
├── Makefile
└── procfs_example.c
当编译加载驱动后,我们可以看到在/proc目录下有slam目录,slam目录下有xinu和stxinu两个文件节点,而stxinu则是指向xinu的软链接,即/proc/slam/stxinu->/proc/slam/xinu。
当我们对相应文件节点操作时,有如下输出:
xinu@slam:~$ ls -l /proc/slam/stxinu
lrwxrwxrwx 1 root root 4 May 4 17:30 /proc/slam/stxinu -> xinu
xinu@slam:~$ ls -l /proc/slam/xinu
-rw-rw-rw- 1 root root 0 May 4 17:30 /proc/slam/xinu
xinu@slam:~$ cat /proc/slam/stxinu
My name is xinu.
xinu@slam:~$ cat /proc/slam/xinu
My name is xinu.
xinu@slam:~$ echo “Hello” > /proc/slam/xinu
xinu@slam:~$ echo “Hello” > /proc/slam/stxinu
当我们执行最后两句后,可通过dmesg看到如下输出:
[31451.840871] Your enter :
[31451.840871] Hello
[31462.441119] Your enter :
[31462.441119] Hello
至此,我们再次体验了另一种内核态与用户态交换方式了,继续加油!
参考网址:
https://www.ibm.com/developerworks/cn/linux/l-kerns-usrs2/
http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/17-2552.html
http://brightconan.com/?p=14
http://nano-chicken.blogspot.com/2009/11/linux-modulesiii.html
http://nano-chicken.blogspot.com/2009/12/linux-modules31-procfsvector.html
http://nano-chicken.blogspot.com/2010/04/linux-modules32-procfssymlinkmkdir_16.html
https://lwn.net/Articles/549737/
http://reverland.org/linux-tech/2013/12/24/writing-drivers-in-linuxa-brief-tutorial/
评论
发表评论