1. 简介

     procfs文件系统(/proc)在linux内核中算是一个比较特殊的文件系统。它是一个虚拟的文件系统:它并没有关联到具体的块设备,而是存在于内存中。procfs中的文件存在的目的在于允许用户程序从内核获取信息(例如proc目录下以数字开头的文件)以及debug程序(如/proc/ksyms.

    本文介绍procfs文件系统在linux 内核的使用。首先介绍所有用于管理procfs文件系统文件的函数。然后介绍与用户程序的交互和某些技巧。最后展示一个完整的例子。

注意:/proc/sys目录下的文件是sysctl文件。他们不属于procfs,由完全不同的API管理(参见其他kernel api的相关书籍)

2.管理procfs

     这部分介绍内核空间在procfs目录下中创建文件、符号链接、设备节点和目录所用到的函数。

注意: 必须包含头文件:

        #include <linux/proc_fs.h>
创建常规文件:

struct proc_dir_entry* create_proc_entry(

const char* name,

 

mode_t mode,

 

struct proc_dir_entry* parent;

该函数用于在目录parent下创建名叫name的常规文件,访问权限由mode指定。 parent NULL则在/procfs目录下创建文件。 成功时,函数返回指向新创建的结构体 struct  proc_dir_entry的指针,失败返回NULL。文章的第三部分将介绍对新创建的文件的一些操作。

 

注意: 你可以传递一个多级目录,例如 create_proc_entry("dirvers/via0/info") 必要时会以权限0755创建via0目录。

如果想创建只读文件,则可以考虑使用函数create_proc_read_entry来一次性创建并初始化procfs项。

创建符号链接

struct proc_dir_entry* proc_symlink(

const char* name,

 

struct proc_dir_entry* parent,

 

const char* dest);

该函数在目录prarent下创建指向dest的符号链接name。相当于用户模式的:ln -s

 

创建目录

struct proc_dir_entry* proc_mkdir(

const char* name,

 

struct proc_dir_entry* parent);

 

该函数在parent目录下创建目录name

删除项

void remove_proc_entry(

const char* name,

 

struct proc_dir_entry* parent);

该函数在procfs系统的parent目录下删除name 节点通过名字来删除,而不是通过创建时返回的proc_dir_entry结构体。本函数并不递归删除项目

注意: 必须在调用remove_proc_entry之前释放掉相对应的结构体struct proc_dir_enrty

3.用户进程交互

procfs文件系统不直接从内核内存中读写数据,而是使用文件的回调函数:当特定文件被读写时嗦调用的函数。这些函数必须在procfs文件创建以后通过设置结构体struct proc_dir_entry(由create_proc_entry函数返回) 中的read_procwrite_proc字段来进行初始化。示例代码如下:

struct proc_dir_entry* entry;

entry->read_proc = read_proc_foo;

entry->write_proc = write_proc_foo;

如果仅使用read_proc.则可以考虑使用函数create_proc_read_entry来一次性创建并初始化procfs项。

 

读取数据:

read函数是允许用户程序从内核中读取数据的回调函数,它的声明如下:

int read_func(

char* buffer,

 

char** start,

 

off_t off,

 

int count,

 

int* peof,

 

void* data);

read函数把它所提供的信息吸入buffer指向的内存,这块内存的大小为PAGE_SIZE

*peof置为1用作到达文件结尾的标识。

参数data可以被用来创建为多个文件创建同一个回调函数。

函数返回值金和剩余参数在文件fs/proc/generic.c的注释中有所描述,这些描述如下:

*start置为NULL(默认就这样)。将请求的数据写到buffer 返回世界写入的字节数

 

写入数据

写回调函数允许用户程序向内核写入数据,因此可以控制内核的部分。写函数的声明如下:

int write_func(

struct file* file,

 

const char* buffer,

 

unsigned long count,

 

void* data);

 

写回调函数从buffer中读取count字节。注意buffer并不在内核内存空间中,所以必须先用copy_from_user拷贝到内科空间,参数file通常被忽略。文章的第五部分有例子。

为多个文件建立单个回调函数

当存在大量几乎相等的文件的时候,为每个文件建立单独的回调函数就很不方便。更好的办法是为这些文件使用同一个回调函数,而这个回调函数通过结构体proc_dir_entrydata字段来区分不通的文件。首先,应该初始化data字段:

struct proc_dir_entry* entry;

struct my_file_data *file_data;

file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL);

entry->data = file_data;

data字段的类型是void* 所以可以初始化成任何值。

 

设置好data字段以后,read_procwrite_proc函数就可以用它来区分不同的文件,因为这个字段通过data参数传入:

int foo_read_func(char *page, char **start, off_t off,
                  int count, int *eof, void *data)
{
        int len;

        if(data == file_data) {
                /* special case for this file */
        } else {
                /* normal processing */
        }

        return len;
}
     
在删除procfs节点时请确保释放data字段。

 4. 技巧

方便的函数:

struct proc_dir_entry* create_proc_read_entry(

const char* name,

 

mode_t mode,

 

struct proc_dir_entry* parent,

 

read_proc_t* read_proc,

 

void* data);

 

该函数跟create_proc_entry函数一样创建普通文件,但该函数同时设置读回调函数和data字段。

 

模块

如果在模块内使用procfs,请将结构体struct proc_dir_entry中的owner字段设置成THIS_MODULE

struct proc_dir_entry* entry;

entry->owner = THIS_MODULE;
访问权限和所有关系(ownership):

有时候需要改变某个procfs项的访问权限,示例代码如下:

struct proc_dir_entry* entry;

entry->mode =  S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH;
entry->uid = 0;
entry->gid = 100;

5.例子

/*
* procfs_example.c: an example proc interface
*
* Copyright (C) 2001, Erik Mouw (mouw@nl.linux.org)
*
* This file accompanies the procfs-guide in the Linux kernel
* source. Its main use is to demonstrate the concepts and
* functions described in the guide.
*
* This software has been developed while working on the LART
* computing board (http://www.lartmaker.nl), which was sponsored
* by the Delt University of Technology projects Mobile Multi-media
* Communications and Ubiquitous Communications.
*
* This program is free software; you can redistribute
* it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA  02111-1307  USA
*
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/jiffies.h>
#include <asm/uaccess.h>


#define MODULE_VERS "1.0"
#define MODULE_NAME "procfs_example"

#define FOOBAR_LEN 8

struct fb_data_t {
        char name[FOOBAR_LEN + 1];
        char value[FOOBAR_LEN + 1];
};


static struct proc_dir_entry *example_dir, *foo_file,
        *bar_file, *jiffies_file, *symlink;


struct fb_data_t foo_data, bar_data;


static int proc_read_jiffies(char *page, char **start,
                             off_t off, int count,
                             int *eof, void *data)
{
        int len;

        len = sprintf(page, "jiffies = %ld/n",
                      jiffies);

        return len;
}


static int proc_read_foobar(char *page, char **start,
                            off_t off, int count,
                            int *eof, void *data)
{
        int len;
        struct fb_data_t *fb_data = (struct fb_data_t *)data;

        /* DON'T DO THAT - buffer overruns are bad */
        len = sprintf(page, "%s = '%s'/n",
                      fb_data->name, fb_data->value);

        return len;
}


static int proc_write_foobar(struct file *file,
                             const char *buffer,
                             unsigned long count,
                             void *data)
{
        int len;
        struct fb_data_t *fb_data = (struct fb_data_t *)data;

        if(count > FOOBAR_LEN)
                len = FOOBAR_LEN;
        else
                len = count;

        if(copy_from_user(fb_data->value, buffer, len))
                return -EFAULT;

        fb_data->value[len] = '/0';

        return len;
}


static int __init init_procfs_example(void)
{
        int rv = 0;

        /* create directory */
        example_dir = proc_mkdir(MODULE_NAME, NULL);
        if(example_dir == NULL) {
                rv = -ENOMEM;
                goto out;
        }
        /* create jiffies using convenience function */
        jiffies_file = create_proc_read_entry("jiffies",
                                              0444, example_dir,
                                              proc_read_jiffies,
                                              NULL);
        if(jiffies_file == NULL) {
                rv  = -ENOMEM;
                goto no_jiffies;
        }

        /* create foo and bar files using same callback
         * functions
         */
        foo_file = create_proc_entry("foo", 0644, example_dir);
        if(foo_file == NULL) {
                rv = -ENOMEM;
                goto no_foo;
        }

        strcpy(foo_data.name, "foo");
        strcpy(foo_data.value, "foo");
        foo_file->data = &foo_data;
        foo_file->read_proc = proc_read_foobar;
        foo_file->write_proc = proc_write_foobar;
               
        bar_file = create_proc_entry("bar", 0644, example_dir);
        if(bar_file == NULL) {
                rv = -ENOMEM;
                goto no_bar;
        }

        strcpy(bar_data.name, "bar");
        strcpy(bar_data.value, "bar");
        bar_file->data = &bar_data;
        bar_file->read_proc = proc_read_foobar;
        bar_file->write_proc = proc_write_foobar;
               
        /* create symlink */
        symlink = proc_symlink("jiffies_too", example_dir,
                               "jiffies");
        if(symlink == NULL) {
                rv = -ENOMEM;
                goto no_symlink;
        }

        /* everything OK */
        printk(KERN_INFO "%s %s initialised/n",
               MODULE_NAME, MODULE_VERS);
        return 0;

no_symlink:
        remove_proc_entry("bar", example_dir);
no_bar:
        remove_proc_entry("foo", example_dir);
no_foo:
        remove_proc_entry("jiffies", example_dir);
no_jiffies:                          
        remove_proc_entry(MODULE_NAME, NULL);
out:
        return rv;
}


static void __exit cleanup_procfs_example(void)
{
        remove_proc_entry("jiffies_too", example_dir);
        remove_proc_entry("bar", example_dir);
        remove_proc_entry("foo", example_dir);
        remove_proc_entry("jiffies", example_dir);
        remove_proc_entry(MODULE_NAME, NULL);

        printk(KERN_INFO "%s %s removed/n",
               MODULE_NAME, MODULE_VERS);
}


module_init(init_procfs_example);
module_exit(cleanup_procfs_example);

MODULE_AUTHOR("Erik Mouw");
MODULE_DESCRIPTION("procfs examples");
MODULE_LICENSE("GPL");

 

转自:http://leewez.blog.163.com/blog/static/29589546200972345039123/

 

Logo

更多推荐