ILD

添加proc接口
作者:YUAN JIANPENG 邮箱:yuanjp@hust.edu.cn
发布时间:2018-7-29 站点:Inside Linux Development

很多内核模块都在/proc目录添加条目,用文件读写的方式来实现用户态和内核交换数据。


1 创建删除

在新的内核,创建proc条目的接口通常使用:

1
2
3
4
5
6
7
extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
                           struct proc_dir_entry *,
                           const struct file_operations *,
                           void *);
 
struct proc_dir_entry *proc_create(const char *name, umode_t mode, 
    struct proc_dir_entry *parent, const struct file_operations *proc_fops);


proc_create()的第一个参数是文件名;mode是读写权限;parent是父目录,若为NULL,则创建在/proc下面。proc_fops是struct file_operations类型,指定文件操作回调函数。

proc目录可以用proc_mkdir()接口创建。

proc_create_data()比proc_create()多一个参数,传入一个指针,该指针存储在proc_inode.data。用户可以文件操作回调函数中读取该指针,这样就可以多个proc文件共享回调函数,根据data区分不同的proc文件。


删除proc条目,使用下述接口:

1
extern void proc_remove(struct proc_dir_entry *);


2 读写

读接口如下:

1
2
static ssize_t proc_read(struct file *file,
    char __user *data, size_t len, loff_t *off)


第一个参数表示打开的文件,data是用户存储数据的指针,len是要读取的长度,off是当前offset。

通过file->f_inode,可以获得inode指针,通过PDE_DATA(),可以获得创建时传入的data。

read接口需要自己负责off的移动。返回值0表示EOF,返回正数表示成功读取的长度,返回负数表示错误。


写接口如下:

1
2
static ssize_t proc_write(struct file *file,
    const char __user *data, size_t len, loff_t *off)


和读接口类似,返回正数表示成功写入的长度,返回负数表示错误,返回0,表示本次写入0,用户态程序可尝试下次再写,因此如果是buffer满,不要返回0,否则用户态会不停的写。

同样off需要本接口自己负责移动。


3 源码 

内核模块代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
 * A demo module demonstrating how to add a proc entry to /proc
 *
 * Copyright (C) 2018 Yuan Jianpeng <yuanjp@hust.edu.cn>
 *
 * 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.
 */
 
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
 
#define PROC_ENTRY_NUM          2
#define PROC_ENTRY_NAME         "test"
#define PROC_DATA_SIZE          16
 
static char test_data[PROC_ENTRY_NUM][PROC_DATA_SIZE];
 
static ssize_t proc_read(struct file *file,
    char __user *data, size_t len, loff_t *off)
{
    int remain, readed;
    long no;
     
    no = (long)PDE_DATA(file->f_inode);
    if (no < 0 || no >= PROC_ENTRY_NUM) 
        return -EINVAL;
     
    /* return 0 indicates EOF */
    if (*off >= PROC_DATA_SIZE)
        return 0;
 
    remain = PROC_DATA_SIZE - *off;
    readed = (len > remain) ? remain : len;
    if (copy_to_user(data, &test_data[no][*off], readed))
        return -EFAULT;
 
    /* move off for next read */
    *off += readed;
 
    /* return data length actually readed */
    return readed;
}
 
static ssize_t proc_write(struct file *file,
    const char __user *data, size_t len, loff_t *off)
{
    int remain, written;
    long no;
     
    no = (long)PDE_DATA(file->f_inode);
    if (no < 0 || no >= PROC_ENTRY_NUM) 
        return -EIO;
 
    /* return 0 is wrong, 
        the userspace command (echo for example) will attemp write again and again */
    if (*off >= PROC_DATA_SIZE)
        return -EFBIG;
 
    remain = PROC_DATA_SIZE - *off;
    written = (len > remain) ? remain : len;
    if (copy_from_user(&test_data[no][*off], data, written))
        return -EIO;
 
    /* move off for next writing more data */
    *off += written;
    return written;
}
 
static struct proc_dir_entry *proc_entry[PROC_ENTRY_NUM];
 
 
static const struct file_operations proc_file_fops = {
    .owner = THIS_MODULE,
    .read = proc_read,
    .write = proc_write,
};
 
static int __init mod_init(void)
{
    long i;
    char name[32];
    for (i = 0; i < PROC_ENTRY_NUM; i++) {
        snprintf(name, sizeof(name), PROC_ENTRY_NAME "%ld", i);
        proc_entry[i] = proc_create_data(name, 0644, NULL, &proc_file_fops, (void *)i);
        if (!proc_entry[i])
            goto err;
    }
    return 0;
 
err:
    for (i = 0; i < PROC_ENTRY_NUM; i++) {
        if (proc_entry[i])
            proc_remove(proc_entry[i]);
    }
    return -EBUSY;
}
 
static void __exit mod_exit(void)
{
    long i;
    for (i = 0; i < PROC_ENTRY_NUM; i++) {
        proc_remove(proc_entry[i]);
    }
}
 
module_init(mod_init);
module_exit(mod_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yuan Jianpeng");


Copyright © insidelinuxdev.net 2017-2021. Some Rights Reserved.