cmqy 发表于 2016-04-07 15:18

proc探究记录

本帖最后由 cmqy 于 2016-04-14 15:12 编辑

/proc是个用于与内核进行交流的ramfs,渊源不作介绍,仅记录自己的认知

在以往的迷糊理解中,认为seq_file是对在/proc创建文件的另一种实现,然而并不是,先说说seq_file的/proc的关系seq_file仅仅是/proc文件向用户层输出的一种实现,为了克服之前不能输出大于1页数据的缺陷理解/proc文件系统之前,最好先看看VFS结构。
创建/proc文件的步骤为:
1、创建/proc条目struct proc_dir_entry *entry;
entry=create_proc_entry("proctest",0666,NULL);struct proc_dir_entry是内核保存/proc条目的结构体,看看create_proc_entry的声明109 extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
110                                                 struct proc_dir_entry *parent);@name是创建的proc文件的名字,@mode是访问权限,@parent是父目录的proc_dir_entry结构体

2、关联操作集
看看proc_dir_entry的结构体原型 2.6.32的struct proc_dir_entry {
52         unsigned int low_ino;
53         unsigned short namelen;
54         const char *name;
55         mode_t mode;
56         nlink_t nlink;
57         uid_t uid;
58         gid_t gid;
59         loff_t size;
60         const struct inode_operations *proc_iops;
61         /*
62          * NULL ->proc_fops means "PDE is going away RSN" or
63          * "PDE is just created". In either case, e.g. ->read_proc won't be
64          * called because it's too late or too early, respectively.
65          *
66          * If you're allocating ->proc_fops dynamically, save a pointer
67          * somewhere.
68          */
69         const struct file_operations *proc_fops;
70         struct proc_dir_entry *next, *parent, *subdir;
71         void *data;
72         read_proc_t *read_proc;
73         write_proc_t *write_proc;
74         atomic_t count;         /* use count */
75         int pde_users;/* number of callers into module in progress */
76         spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
77         struct completion *pde_unload_completion;
78         struct list_head pde_openers;   /* who did ->open, but not ->release */
79 };
3.10的
32 struct proc_dir_entry {
33         unsigned int low_ino;
34         umode_t mode;
35         nlink_t nlink;
36         kuid_t uid;
37         kgid_t gid;
38         loff_t size;
39         const struct inode_operations *proc_iops;
40         const struct file_operations *proc_fops;
41         struct proc_dir_entry *next, *parent, *subdir;
42         void *data;
43         atomic_t count;         /* use count */
44         atomic_t in_use;      /* number of callers into module in progress; */
45                         /* negative -> it's going away RSN */
46         struct completion *pde_unload_completion;
47         struct list_head pde_openers;   /* who did ->open, but not ->release */
48         spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
49         u8 namelen;
50         char name[];
51 };挂操作集的方式有使用file_operations和外面的write_proc和read_proc,新版本的内核不提供write_proc/read_proc了。
seq_file的操作方式struct seq_operations normal_seq_ops =
{
      .start=nor_start,
      .show=nor_show,
      .next=nor_next,
      .stop=nor_stop
};

int normal_open(struct inode *inode, struct file *file)
{
      return seq_open(file,&normal_seq_ops);
}

static struct file_operations fops =
{
      .owner=THIS_MODULE,
      .llseek=seq_lseek,
      .read=seq_read,
      .write=seq_write,
      .open=normal_open,
      .release=seq_release
};使用file_operations对文件进行操作,用于只读的话,只需要挂上open和write函数就行,其他函数可以使用内核提供,其中open简单的返回seq_open,秘密在于其参数struct *seq_operations,需要自己实现4个回调函数。用于迭代产生内容。其调用规则为:启动时调用start,start执行后,如果返回非NULL,则返回值作为show的参数v调用show用于向/proc文件中输出数据,然后调用next,参数v仍为start返回结果,如果next返回非空则调用show,如此往复循环,返回空则调用stop。之后再调用start,直到start返回为NULL,最后仍会调用一次stop。seq_operations的原型为35 struct seq_operations {
36         void * (*start) (struct seq_file *m, loff_t *pos);
37         void (*stop) (struct seq_file *m, void *v);
38         void * (*next) (struct seq_file *m, void *v, loff_t *pos);
39         int (*show) (struct seq_file *m, void *v);
40 };start返回一个存储了数据的空间,是自己创建的,并在调用show时作为v参数传给show,show负责向用户展示里面的内容。start的*pos是递增的,可以用于判断什么时候返回NULL结束迭代。
show可以对输出内容作格式化处理,参数v含有内容,参数m为输出接口,可使用以下函数82 int seq_putc(struct seq_file *m, char c);
83 int seq_puts(struct seq_file *m, const char *s);

86 int seq_printf(struct seq_file *, const char *, ...)对于路径及链表,还有其它函数可以使用,详情见内核文档Document/filesystem/seq_file.txt。其中还有一段话,说明其中指向seq_file的指针在调用seq_open的时候被存在struct file的private域中On a successful open, seq_open() stores the struct seq_file pointer in
file->private_data. If you have an application where the same iterator can
be used for more than one file, you can store an arbitrary pointer in the
private field of the seq_file structure; that value can then be retrieved
by the iterator functions对于2.6.32之类的老内核,可以使用proc_dir_entry里的write_proc和read_proc函数对proc文件进行操作46 typedef int (read_proc_t)(char *page, char **start, off_t off,
47                           int count, int *eof, void *data);
48 typedef int (write_proc_t)(struct file *file, const char __user *buffer,
49                            unsigned long count, void *data);对于read_proc
page 是内核创建的页,start是如果使用自己创建的内存空间,则将指针的地址赋给start,off是希望从proc中读开始取的偏移,count是page页提供的大小,eof 当传递结束时,应该把*eof赋1,data是使用proc_create_data创建proc_dir_entry时传的指针
对于write_proc
file是file结构体,buffer是用户空间,count是用户数据长度,data和read_proc一样。

3、创建目录和在指定目录下创建条目
使用proc_mkdir()可以在/proc下创建目录
而想要在/proc/net下创建文件,在内核2.6.32中的操作方式是讲create_proc_entry的parent参数设为init_net.proc_net,不同版本内核不一样


最后附上示例代码:
/*
*创建/proc/net/testdir/目录下ro_read_proc,rw_normal,rw_write_proc三个文件
*rw_normal用seq_file实现,只读
*rw_write_proc用write_proc和read_proc实现,读写,用于输入需要在内核打开的文件名
*ro_read_proc用create_proc_read_entry实现的只读,用打开并输出从rw_write_proc输入的文件名,文件应小于3072字节
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <net/net_namespace.h> //init_net.proc_net
#include <linux/err.h>      //IS_ERR
#include <linux/dcache.h> //dentry

#include <asm/segment.h>      //use for operating files in kernel space
#include <linux/buffer_head.h>

MODULE_AUTHOR("Liaosp");
MODULE_LICENSE("Dual BSD/GPL");

#define MAXPATHSIZE 256
static char pathbuff;

void * nor_start(struct seq_file *m, loff_t *pos)
{
if(*pos>0)
return NULL;
return pathbuff;
}

void nor_stop(struct seq_file *m, void *v)
{
}

void * nor_next(struct seq_file *m, void *v, loff_t *pos)
{
return NULL;
}

int nor_show(struct seq_file *m, void *v)
{
seq_printf(m,"%s",(char *)v);
return 0;
}


struct seq_operations normal_seq_ops =
{
.start=nor_start,
.show=nor_show,
.next=nor_next,
.stop=nor_stop
};

int normal_open(struct inode *inode, struct file *file)
{
return seq_open(file,&normal_seq_ops);
}

static struct file_operations fops =
{
.owner=THIS_MODULE,
.llseek=seq_lseek,
.read=seq_read,
.write=seq_write,         //这里是个严重的BUG,write函数得自己写
.open=normal_open,
.release=seq_release
};

//operating files in kernel
struct file *file_open(const char *path, int flag,int right)
{
mm_segment_t oldfs;
struct file *filp=NULL;
int err=0;

oldfs=get_fs();
set_fs(get_ds());

filp=filp_open(path,flag,right);
if(IS_ERR(filp))
{
printk("open %s error\n",path);
err=PTR_ERR(filp);
return NULL;
}
set_fs(oldfs);
return filp;
}

void file_close(struct file *file)
{
filp_close(file,NULL);
}

int file_read(struct file *file,unsigned char *data, unsigned int size,unsigned long long offset)
{
mm_segment_t oldfs;
int ret;

oldfs=get_fs();
set_fs(get_ds());

ret=vfs_read(file, data, size, &offset);

set_fs(oldfs);
return ret;
}



// what is @start @off @eof @data mean?
static int read_ro(char *page,char **start, off_t off, int count, int *eof, void *date)
{
struct file *fp;
unsigned long re=0;
printk("%s\n",pathbuff);

fp=file_open(pathbuff,O_RDONLY,0);
printk("file open success :%s\n",fp->f_path.dentry->d_name.name);
re=file_read(fp,page,count,off);
file_close(fp);
if(re<=0)

//      re=snprintf(page,10,"in read_ro in ro\n");
*eof=1;
return re;
}
//@page 是内核创建的页
//@start 如果使用自己创建的内存空间,则将指针的地址赋给start
//@off 希望从proc中读开始取的偏移
//@count page页提供的大小
//@eof 当传递结束时,应该把*eof赋1
//@data proc_create_data传的指针
static int rw_r_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int re=0;
printk("in proc_read\noff:%d conut:%d\n",off,count);
re=snprintf(page,MAXPATHSIZE,"%s",pathbuff);
*eof=1;
return re;

}

static int rw_w_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
int re=0;
re=copy_from_user(pathbuff,buffer,count);
pathbuff='\0';
printk("%s",pathbuff);
return count;

}

struct proc_dir_entry *dir;      // /proc/net/<dir>

static int exproc_init(void)
{
memset(pathbuff,0,MAXPATHSIZE);
strcpy(pathbuff,"echo the path into this file more len \n");

struct proc_dir_entry *rw_normal; //using file_operations
struct proc_dir_entry *rw_write_proc; //rw using write_proc
struct proc_dir_entry *ro_read_proc; //ro using create_proc_read_entry



//      dir=create_proc_entry("/proc/net/testdir",0666,NULL);
//      don't work

//      struct file *filp = filp_open("/proc/net",O_RDONLY,0);
//      parent=PDE(filp->f_dentry->d_inode);
//      dir=create_proc_entry("testdir",0666,parent);
//      can't work in kernel 2.6.35

dir=proc_mkdir("testdir",init_net.proc_net);
rw_normal=create_proc_entry("rw_normal",0444,dir);
rw_write_proc=create_proc_entry("rw_write_proc",0666,dir);

ro_read_proc=create_proc_read_entry("ro_read_proc",0,dir,read_ro,NULL);

rw_write_proc->read_proc=rw_r_proc;
rw_write_proc->write_proc=rw_w_proc;

rw_normal->proc_fops=&fops;

}
static void exproc_exit(void)
{
remove_proc_entry("rw_normal",dir);
remove_proc_entry("rw_write_proc",dir);
remove_proc_entry("ro_read_proc",dir);
remove_proc_entry("testdir",init_net.proc_net);
}


module_init(exproc_init);
module_exit(exproc_exit);这个文本编辑器真恶心,害我写了两次,还差点要写三次

Godbach 发表于 2016-04-07 16:34

回复 1# cmqy
感谢分享。

   

_nosay 发表于 2016-04-07 17:00

大牛!别走!帮我签个名。
页: [1]
查看完整版本: proc探究记录