找回密码
 注册
搜索
热搜: 回贴
微赢网络技术论坛 门户 服务器 Linux/BSD 查看内容

proc文件系统实例

2009-12-20 13:52| 发布者: admin| 查看: 134| 评论: 0|原作者: 云天青


最近这段时间从用户态程序转到kernel,这个过程是痛苦的,也是催人老的.在不知道抓狂多少次后,终于完成了入门.在这里记录下我的历程,大多数都是CU论坛上学到的,感谢dreamice版主的帮助.
以下程序在
[root@zj:~/Desktop/net]# uname -r
2.6.27.9
上测试通过
1、重要的数据结构:
struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
/*
* NULL ->proc_fops means "PDE is going away RSN" or
* "PDE is just created". In either case, e.g. ->read_proc won't be
* called because it's too late or too early, respectively.
*
* If you're allocating ->proc_fops dynamically, save a pointer
* somewhere.
*/
const struct file_operations *proc_fops;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int pde_users; /* number of callers into module in progress */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
};
2、创建函数:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,struct proc_dir_entry *parent);
name: 要创建的文件名称;
mode: 该文件的保护掩码;
parent: 确定文件所在目录,如果置NULL,则位置为/proc下。
3、读proc:
int read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data);
page:指示用来写入数据的缓冲区;
off和count:与read函数对应的参数相同;
start和eof:用于读取大于1个page数据时实现。
4、写proc:
int write_proc(struct file *file, const char __user *buffer,
unsigned long count, void *data);
filp 参数实际上是一个打开文件结构(可以忽略这个参数)。Buffer 参数是用户空间要写入的数据。缓冲区地址实际上是一个用户空间的缓冲区,不能直接读取它。len 参数定义了在 buffer 中有多少数据要被写入。data 参数是一个指向私有数据的指针。
5.删除
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
name: 要创建的文件名称;
parent: 确定文件所在目录,如果置NULL,则位置为/proc下。
6. 实现一个proc文件
(1)调用create_proc_entry创建一个struct proc_dir_entry,作为一个全局量。
(2)对创建的struct proc_dir_entry进行赋值:read_proc,mode,owner,size,write_proc等等。
示例:
proc_hello.c
#include linux/module.h> /* Specifically, a module */
#include linux/kernel.h> /* We're doing kernel work */
#include linux/proc_fs.h> /* Necessary because we use the proc fs */
#define procfs_name "helloworld"
struct proc_dir_entry *Our_Proc_File;
int
procfile_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof, void *data)
{
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name);
/*
* We give all of our information in one go, so if the
* user asks us if we have more information the
* answer should always be no.
*
* This is important because the standard read
* function from the library would continue to issue
* the read system call until the kernel replies
* that it has no more information, or until its
* buffer is filled.
*/
if (offset > 0) {
printk(KERN_INFO "offset > 0\n");
/* we have finished to read, return 0 */
ret = 0;
} else {
/* fill the buffer, return the buffer size */
printk(KERN_INFO "offset );
ret = sprintf(buffer, "HelloWorld!\n");
}
return ret;
}
int init_module()
{
Our_Proc_File = create_proc_entry(procfs_name, 0644, NULL);
if (Our_Proc_File == NULL) {
remove_proc_entry(procfs_name, NULL);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
procfs_name);
return -ENOMEM;
}
Our_Proc_File->read_proc = procfile_read;
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->mode = S_IFREG | S_IRUGO;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;
printk(KERN_INFO "/proc/%s created\n", procfs_name);
return 0; /* everything is ok */
}
void cleanup_module()
{
remove_proc_entry(procfs_name, NULL);
printk(KERN_INFO "/proc/%s removed\n", procfs_name);
}
Makefile:
obj-m := proc_hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(RM) *.o *.mod.c *.ko *.symvers
本示例只实现了一个简单的读proc操作。
编译后,执行:
[root@zj:~/Desktop/net/proc]# insmod proc_hello.ko
[root@zj:~/Desktop/net/proc]# dmesg | tail -n 1
[ 8995.458558] /proc/helloworld created
[root@zj:~/Desktop/net/proc]# cat /proc/helloworld
[root@zj:~/Desktop/net/proc]# dmesg | tail -n 10
[ 5107.603346] EXT3 FS on sda7, internal journal
[ 5107.603719] EXT3-fs: mounted filesystem with ordered data mode.
[ 8967.688297] /proc/net/helloworld removed
[ 8995.458558] /proc/helloworld created
[ 9027.266377] procfile_read (/proc/helloworld) called
[ 9027.266395] offset = 0
[ 9027.266400] procfile_read (/proc/helloworld) called
[ 9027.266404] offset > 0
[ 9027.266435] procfile_read (/proc/helloworld) called
[ 9027.266440] offset > 0
为什么在调用cat /proc/ helloworld后,会看到procfile_read被执行了三次,且只有第一次读到了数据?
因为通常cat,dd等,都是通过标准库函数来实现的,标准库函数往往会多次调用系统调用来读写数据,所以我们看到调用了3次。具体cat以及dd等调用一次,读取或写数据大小,这个需要具体查证一下。
我写了一个test程序,来验证这个多次读操作不是在系统调用里面实现的:
test.c
#include stdlib.h>
#include stdio.h>
#include unistd.h>
#include sys/types.h>
#include sys/stat.h>
#include fcntl.h>
#define PROC_FILE "/proc/helloworld"
int main()
{
char buf[50];
int fd = open(PROC_FILE, O_RDONLY);
if (fd == -1) {
printf("open err\n");
return -1;
}
if (read(fd, buf, sizeof("HelloWorld")) > 0) {
printf("read: %s\n", buf);
}
close(fd);
}
编译:
# gcc test.c -o test
加载模块后,执行
# ./test
然后,执行:
# dmesg
procfile_read (/proc/helloworld) called
offset = 0
确实只执行了一次,这说明多次调用procfile_read是在系统之上做的。
6、注意点
(1)切记不能访问已经卸载的一个proc文件,否则会导致kernel panic;
(2)注意不要删除正在调用的问proc文件。因为文件的入口项不存在关联的作者,文件的调用也并没有作用到模块的引用计数上;
(3)勿注册两个同名的proc文件,这样将导致入口项无法区分。





最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-29 23:38 , Processed in 0.158985 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部