proc系统使用

13 篇文章 0 订阅
8 篇文章 0 订阅

1.proc系统

1.1 proc系统简介

/proc 文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。可以通过/proc系统中的一些节点了解系统的运行状况和系统配置信息。

1.2 /proc节点的创建和使用

1.创建和删除节点
要在 /proc 文件系统中创建一个虚拟文件,请使用 create_proc_entry 函数。这个函数可以接收一个文件名、一组权限和这个文件在 /proc 文件系统中出现的位置。create_proc_entry 的返回值是一个 proc_dir_entry 指针(或者为 NULL,说明在 create 时发生了错误)。然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。create_proc_entry 的原型和 proc_dir_entry 结构如下所示。
struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode,
                                             struct proc_dir_entry *parent );
struct proc_dir_entry {
    const char *name;           // virtual file name
    mode_t mode;                // mode permissions
    uid_t uid;              // File's user id
    gid_t gid;              // File's group id
    struct inode_operations *proc_iops; // Inode operations functions
    struct file_operations *proc_fops;  // File operations functions
    struct proc_dir_entry *parent;      // Parent directory
    ...
    read_proc_t *read_proc;         // /proc read function
    write_proc_t *write_proc;       // /proc write function
    void *data;             // Pointer to private data
    atomic_t count;             // use count
    ...
};
void remove_proc_entry( const char *name, struct proc_dir_entry *parent );
parent 参数可以为 NULL(表示 /proc 根目录),也可以是很多其他值,这取决于我们希望将这个文件放到什么地方。下面列出了可以使用的其他一些父 proc_dir_entry,以及它们在这个文件系统中的位置。
proc_root_fs:       /proc
proc_net:       /proc/net
proc_bus:       /proc/bus
proc_root_driver:   /proc/driver

2.写回调函数
int mod_write( struct file *filp, const char __user *buff, unsigned long len, void *data );
filp:该节点文件结构
buff:用户空间传递的字符串数据,需使用copy_from_user拷贝至内核空间
len:buff的长度
data:私有数据结构

3.读回调函数
int mod_read( char *page, char **start, off_t off, int count, int *eof, void *data );
page:数据写入位置,page的缓冲区在内核空间,无需使用copy_to_user
start/off:当写入多页数据(大文件)时,使用的参数(实现大文件又更好的方法,见seq_file)
count:允许写入的最大数据长度
eof:当所有数据写入完成后,设置eof参数(文件结束符)
data:私有数据结构

4.其它常用函数
/* Create a directory in the proc filesystem */
struct proc_dir_entry *proc_mkdir( const char *name,
                                     struct proc_dir_entry *parent );
/* Create a symlink in the proc filesystem */
struct proc_dir_entry *proc_symlink( const char *name,
                                       struct proc_dir_entry *parent,
                                       const char *dest );
/* Create a proc_dir_entry with a read_proc_t in one call */
struct proc_dir_entry *create_proc_read_entry( const char *name,
                                                  mode_t mode,
                                                  struct proc_dir_entry *base,
                                                  read_proc_t *read_proc,
                                                  void *data );

1.3 测试源码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>

#include <linux/proc_fs.h>// for the proc filesystem
#include <linux/seq_file.h>// for sequence files
#include <linux/jiffies.h>// for jiffies
#define MAX_COOKIE_SIZE PAGE_SIZE


static struct proc_dir_entry *proc_entry;
static char *cookie;
static int cookie_index=0;
static int read_pos=0;

ssize_t proc_write(struct file *filp,const char __user *buff,unsigned long len,void *data)
{
    int available_space=MAX_COOKIE_SIZE-cookie_index+1;
    printk("%s\n",__func__);
    if(len>available_space){
        printk("no space to write!\n");
        return -ENOSPC;
    }
    if(copy_from_user(&cookie[cookie_index],buff,len)){
        return -EFAULT;
    }
    printk("%s\n",&cookie[cookie_index]);
    cookie_index+=len;
    cookie[cookie_index-1]=0;
    return len;

}

ssize_t proc_read(char *page,char **start,off_t off,int count,int *eof,void *data)
{
    int len;
    printk("%s\n",__func__);
    if(off>0){
        *eof=1;
        return 0;
    }
    if(read_pos>=cookie_index) read_pos=0;
    len=sprintf(page,"%s\n",&cookie[read_pos]);
    read_pos+=len;
    return len;
}

static int proc_init()
{
    int ret=0;
    printk(KERN_ERR "HELLO INIT!\n");
    cookie=(char *)vmalloc(MAX_COOKIE_SIZE);
    if(cookie==NULL){
        printk("create cookie error!\n");
        ret=-ENOMEM;
    }
    proc_entry=create_proc_entry("cookie_test",0666,NULL);
    if(proc_entry==NULL){
        printk("create_proc_entry error!\n");
        ret=-ENOMEM;
        vfree(cookie);
    }else{
        cookie_index=0;
        read_pos=0;
        proc_entry->read_proc=proc_read;
        proc_entry->write_proc=proc_write;
        //proc_entry->owner = THIS_MODULE;
        printk("proc init success!\n");
    }

    return ret;
}

static void proc_exit()
{
    remove_proc_entry(proc_entry,NULL);
    vfree(cookie);
    printk(KERN_ERR "HELLO EXIT!\n");

}

module_init(proc_init);
module_exit(proc_exit);
MODULE_LICENSE("Dual BSD/GPL");

2.seq_file

经过第一节,我们发现完成一个proc节点有点复杂,为了方便开发者,内核提供了一组跟简单的seq_file接口。

2.1 seq_file接口

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos); 
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

调用流程:
1.首先调用start函数,通常为申请内存等操作,整个序列开始,如果start返回值不为NULL则进入第2步;
2.调用show函数,它将数据值写入供用户读取的缓冲区中;
3.调用next函数,该函数是一个迭代器,其目的是为了遍历所有数据,next函数返回NULL则结束调用,序列结束,进入第4步;
4.调用stop函数,一般是释放内存等操作;之后回到start函数继续下一个序列操作,直到start函数返回NULL;

执行调用的核心函数是static int traverse(struct seq_file *m, loff_t offset),该函数在seq_read和seq_lseek中被调用,以下是源码:

static int traverse(struct seq_file *m, loff_t offset)
{
    loff_t pos = 0, index;
    int error = 0;
    void *p;

    m->version = 0;
    index = 0;
    m->count = m->from = 0;
    if (!offset) {
        m->index = index;
        return 0;
    }
    if (!m->buf) {
        m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
        if (!m->buf)
            return -ENOMEM;
    }
    p = m->op->start(m, &index);
    while (p) {
        error = PTR_ERR(p);
        if (IS_ERR(p))
            break;
        error = m->op->show(m, p);
        if (error < 0)
            break;
        if (unlikely(error)) {
            error = 0;
            m->count = 0;
        }
        if (seq_overflow(m))
            goto Eoverflow;
        if (pos + m->count > offset) {
            m->from = offset - pos;
            m->count -= m->from;
            m->index = index;
            break;
        }
        pos += m->count;
        m->count = 0;
        if (pos == offset) {
            index++;
            m->index = index;
            break;
        }
        p = m->op->next(m, p, &index);
            }
    m->op->stop(m, p);
    m->index = index;
    return error;

Eoverflow:
    m->op->stop(m, p);
    kvfree(m->buf);
    m->count = 0;
    m->buf = seq_buf_alloc(m->size <<= 1);
    return !m->buf ? -ENOMEM : -EAGAIN;
}

2.2 其他常用接口

对于简单的虚拟文件(只需实现show函数),single_open是一个非常有用的接口:
int single_open(struct file *file,
                    int (*show)(struct seq_file *m, void *p),
                    void *data);
实现show函数一般可以使用seq_printf函数,等价于printf函数:
int seq_printf(struct seq_file *, const char *, ...);

以下函数均由内核实现:
int seq_open(struct file *, const struct seq_operations *);
ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
loff_t seq_lseek(struct file *, loff_t, int);
int seq_release(struct inode *, struct file *);
int seq_escape(struct seq_file *, const char *, const char *);
int seq_putc(struct seq_file *m, char c);
int seq_puts(struct seq_file *m, const char *s);
int seq_write(struct seq_file *seq, const void *data, size_t len);

2.3 测试源码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>// for the proc filesystem
#include <linux/seq_file.h>// for sequence files
#include <linux/jiffies.h>// for jiffies

static struct proc_dir_entry *proc_entry;

static int seq_proc_show(struct seq_file *s,void *v) 
{
    return seq_printf(s,"%s\n","it is a test!");
}

static struct seq_operations seq_seq_ops={
    .show=seq_proc_show,        
};

static int seq_proc_open(struct inode *inode,struct file *filp)
{
    //return seq_open(filp,&seq_seq_ops);
    return single_open(filp,seq_proc_show,NULL);
}
static struct file_operations seq_ops={
    .owner=THIS_MODULE,
    .open = seq_proc_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release
};

static int proc_init()
{
    int ret=0;
    printk(KERN_ERR "HELLO INIT!\n");

    /* you can create proc node in one of 2 ways */
    /*  
     * 1.
     * proc_entry=create_proc_entry("proc_seq",0666,NULL);
     * if(proc_entry)
     *      proc_entry->proc_fops=&seq_ops;
     * 2.
     * proc_entry=proc_create("proc_seq",0666,NULL,&seq_ops);
     * */
    proc_entry=proc_create("proc_seq",0666,NULL,&seq_ops);
        if(proc_entry==NULL){
        printk("create_proc_entry error!\n");
        ret=-ENOMEM;
    }else{
        printk("proc init success!\n");
    }

    return ret;
}

static void proc_exit()
{
    remove_proc_entry(proc_entry,NULL);
    printk(KERN_ERR "HELLO EXIT!\n");

}
module_init(proc_init);
module_exit(proc_exit);
MODULE_LICENSE("Dual BSD/GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值