源自:http://blog.chinaunix.net/uid-317451-id-92670.html
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
static struct mutex lock;
static struct list_head head;
struct my_data {
struct list_head list;
int value;
};
static void add_one(void)
{
struct my_data *data;
mutex_lock(&lock);
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (data!=NULL)
list_add(&data->list,&head);
mutex_unlock(&lock);
}
static ssize_t _seq_write(structfile*file,constchar __user* buffer,
size_t count, loff_t *ppos)
{
add_one();
return count;
}
static int _seq_show(struct seq_file*m,void *p)
{
struct my_data *data = list_entry(p,struct my_data,list);
seq_printf(m,"value: %d\n", data->value);
return 0;
}
static void *_seq_start(struct seq_file*m, loff_t*pos)
{
mutex_lock(&lock);
return seq_list_start(&head,*pos);
}
#if 0
其中seq_list_start在内核中的实现
struct list_head *seq_list_start(struct list_head *head, loff_t pos)
{
struct list_head *lh;
list_for_each(lh, head)
if (pos-- == 0)
return lh;
return NULL;
}
#endif
static void *_seq_next(struct seq_file*m,void *p, loff_t*pos)
{
return seq_list_next(p,&head, pos);
}
#if 0
struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos)
{
struct list_head *lh;
lh = ((struct list_head *)v)->next;
++*ppos;
return lh == head ? NULL : lh;
}
#endif
static void _seq_stop(struct seq_file*m,void *p)
{
mutex_unlock(&lock);
}
static struct seq_operations _seq_ops={
.start = _seq_start,
.next = _seq_next,
.stop = _seq_stop,
.show = _seq_show
};
static int _seq_open(struct inode*inode,struct file *file)
{
return seq_open(file,&_seq_ops);
}
static struct file_operations _seq_fops={
.open = _seq_open,
.read = seq_read,
.write = _seq_write,
.llseek = seq_lseek,
.release = seq_release
};
static void clean_all(struct list_head*head)
{
struct my_data *data;
while (!list_empty(head)){
data = list_entry(head->next,struct my_data,list);
list_del(&data->list);
kfree(data);
}
}
static int __init init(void)
{
struct proc_dir_entry *entry;
mutex_init(&lock);
INIT_LIST_HEAD(&head);
add_one();
add_one();
add_one();
entry = create_proc_entry("my_data", S_IWUSR| S_IRUGO,NULL);
if (entry==NULL) {
clean_all(&head);
return -ENOMEM;
}
entry->proc_fops=&_seq_fops;
return 0;
}
static void __exit fini(void)
{
remove_proc_entry("my_data",NULL);
clean_all(&head);
}
module_init(init);
module_exit(fini);
注:以上代码需要内核版本不小于2.6.23,小版本内核可以从2.6.23内核代码中抽取函数seq_list_start和seq_list_next。
上面的这段程序创建了一个结构体my_data的双向循环链表(head),并且通过my_data proc文件将其导出到用户空间,用户空间的程序除了可以通过对my_data的读操作来获取my_data链表中各个结构体中的value域的值之外,还能通过写(write)系统调用添加一个具有随机value值的my_data结构体到双向循环链表中(从链表的头部添加)。
结果:
gentux kernel # cat /proc/my_data
value: 474615376
value: 474615632
value: 474615568
gentux kernel # echo 0 > /proc/my_data
gentux kernel # cat /proc/my_data
value: 758528120
value: 474615376
value: 474615632
value: 474615568
补充:seq_printf函数的实现
int seq_printf(struct seq_file *m, const char *f, ...)
{
va_list args;
int len;
if (m->count < m->size) {
va_start(args, f);
len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
va_end(args);
if (m->count + len < m->size) {
m->count += len;
return 0;
}
}
m->count = m->size;
return -1;
}
其中seq_file结构如下:
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
u64 version;
struct mutex lock;
const struct seq_operations *op;
void *private;
};
proc_dir_entry结构如下:
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 */
};