在《使用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文件里有如下定义:
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文件里有该函数的定义:
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文件里有如下定义:
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文件里有如下声明:
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文件里有如下定义:
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文件里有如下的结构体:
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
#include
#include
#include
#include
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/