proc系统使用

Posted feisonzl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了proc系统使用相关的知识,希望对你有一定的参考价值。

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");

以上是关于proc系统使用的主要内容,如果未能解决你的问题,请参考以下文章

Linux下实现脚本监测特定进程占用内存情况

Linux有问必答:如何检查Linux的内存使用状况

proc文件系统介绍以及常见使用方法

RK3399平台开发系列讲解(内存篇)18.10/proc/meminfo 解析

Linux Shell常用技巧 系统运行状况

Linux mpstat字段解析