2009年11月26日 星期四

Linux Kernel(3)- procfs


(III)將和大家介紹procfs,雖然後來procfs已經失控,也不再建議大家使用,但是還是有一定的用途,所以,有興趣的人可以一起探討一下。

/proc是一個特殊的檔案系統,當讀取/proc底下的檔案時,其內容由kernel動態產生,很多應用程式也都是存取/proc底下的檔案,我們藉由兩個範例來說明。第一個用於當/proc只提供read-only的時候,第二個用於/proc提供具有write功能的時候。

Read-Only

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* read file operations */
static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

/* private data */
static uint32_t *lines;

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    /* create a procfs entry for read-only */
    ent = create_proc_read_entry ("brook", S_IRUGO, NULL, read_proc, lines);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    /* remove procfs entry */
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

在這個例子中我們使用一個globa variable "lines",用於存放上次列印到第幾個數值,MAX_LINE是上限。一開始載入module就先分配記憶體給lines,接著呼叫create_proc_read_entry()建立一個唯讀的procfs檔案。

static inline 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);


name是proc底下的檔案名稱。mode是檔案的mode,可以直接用0666的表示法表示,如果是建立目錄則要or S_IFDIR。base如果為null,則會把/proc當根目錄,可以是其他的proc_dir_entry pointer,那麼就會以proc_dir_entry當根目錄開始往下長。read_proc則是當user讀取該檔案時的call-back function。data則用於存放額外資訊的地方,可以先alloc,然後每次執行read時,都可以拿出來使用。

typedef int (read_proc_t)(char *page, char **start, off_t off,
    int count, int *eof, void *data);

page是系統給的buffer,也是我們要寫入的地方,寫到buffer裡面的資料,會被當成proc的內容。start如果只要一次就可以read完,可以忽略start,否則只要將page設定給start就可以了。count則是user-space想要讀取的資料量。offset指示目前檔案位置。eof指示是否已經讀到結尾。data用於存放額外的資訊。


Writable

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

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* write file operations */
static int write_proc(struct file *file, const char __user *buf,
                       unsigned long count, void *data)
{
    char num[10], *p;
    int nr, len;

    /* no data be written */
    if (!count) {
        printk("count is 0\n");
        return 0;
    }

    /* Input size is too large to write our buffer(num) */
    if (count > (sizeof(num) - 1)) {
        printk("input is too large\n");
        return -EINVAL;
    }

    if (copy_from_user(num, buf, count)) {
        printk("copy from user failed\n");
        return -EFAULT;
    }

    /* atoi() */
    p = num;
    len = count;
    nr = 0;
    do {
        unsigned int c = *p - '0';
        if (*p == '\n') {
            break;
        }
        if (c > 9) {
            printk("%c is not digital\n", *p);
            return -EINVAL;
        }
        nr = nr * 10 + c;
        p++;
    } while (--len);
    *(uint32_t*) data = nr;

    return count;
}

static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

static uint32_t *lines;

static int __init init_modules(void)
{

    struct proc_dir_entry *ent;
    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    ent = create_proc_entry("brook", S_IFREG | S_IRWXU, NULL);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    } else {
        ent->write_proc = write_proc;
        ent->read_proc = read_proc;
        ent->data = lines;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

第二個範例則提供了write的功能,所以不能直接使用create_proc_read_entry()建立procfs檔案,而是要使用create_proc_entry()建立procfs檔案,並且設定read/write file operations,以及"data"。該範例中的write是設定line。

typedef int (write_proc_t)(struct file *file, const char __user *buffer,
    unsigned long count, void *data);




沒有留言:

張貼留言

熱門文章