例说linux内核与应用数据通信(三):读写内核设备驱动文件

释放双眼,带上耳机,听听看~!

        读写设备文件也就是调用系统调用read()和write(),系统调用就是内核提供给应用程序的接口,应用程序对底层的操作大部分都
是通过系统调用来完成。几乎所有的系统调用都涉及到内核和应用的数据交换,本节并非讲述如何添加一个系统调用(那是第一节的内容),而是讲解如何利用现有系统调用来实现特定的内核与应用交互需求

建立字符设备驱动有如下步骤:

第一步、注册设备号。

可以
使用如下函数分别静态和动态注册
:


1
2
3
1int register_chrdev_region(dev_t from, unsigned count, const char *name);
2int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
3

第二步、
初始化设备结构体。

使用函数:


1
2
1void cdev_init(struct cdev *cdev, const struct file_operations *fops);
2

在调用该函数之前,还需要初始化file_operations结构体,该结构体成员函数是字符设备驱动设计的主要内容,当在应用上调用read和write等函数时,该结构体中对应的函数会被调用。

    
   

第三步、添加设备。

使用函数:


1
2
1int cdev_add(struct cdev *p, dev_t dev, unsigned count);
2

第四步、实现
file_operations
结构体中
open()、read()、write()、ioctl()等函数。

 
  
 
    
file_
operations
中的read
()和write()函数,就是用来在驱动程序和应用程序间交换数据
的。通过数据交换,驱动程序和应用程序可以彼此了解对方的情况。但是驱动程序和应用程序属
于不同的地址空间。
驱动程序不能直接访问应用程序的地址空间;同样应用程序也不能
直接访问驱动程序的地址空间,
否则会破坏彼此空间中的数据,从而造成系统崩溃,或
者数
据损坏。安
全的方法是使用内核提供的专用函数,完成数据在应用程序空间和驱动程序空间
的交换。
这些函数对用户程序传过来的指针进行了严格的检查和必要的转换,从
而保证用户程序与驱动程序交换数据的安全性。这些函数有:


1
2
3
4
1unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
2unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); put_user(local,user);
3get_user(local,user);
4

当不是该设备时,应当删除该设备、释放申请的设备号。


1
2
3
1void cdev_del(struct cdev *dev);
2void unregister_chrdev_region(dev_t from, unsigned count);
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
1#include <linux/module.h>
2#include <linux/types.h>
3#include <linux/fs.h>
4#include <linux/errno.h>
5#include <linux/mm.h>
6#include <linux/sched.h>
7#include <linux/init.h>
8#include <linux/cdev.h>
9#include <asm/io.h>
10#include <asm/system.h>
11#include <asm/uaccess.h>
12
13#include <linux/kernel.h>
14#include "chrdev.h"
15
16int             chr_major;
17struct chr_dev  *chr_devp;
18
19int chr_open(struct inode *inode, struct file *filp)
20{
21    filp->private_data = chr_devp;
22    
23    return 0;
24}
25
26static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
27{
28    struct chr_dev* dev = filp->private_data;
29
30    printk(KERN_ALERT"=========%s()=========\n", __func__);
31
32    switch(cmd) {
33    case MEM_CLEAR:
34        memset(dev->data, 0, CHAR_DEV_DATA_SIZE);
35        break;
36    case MEM_RESET:
37        snprintf(dev->data, CHAR_DEV_DATA_SIZE, "%s", "hello, user!");
38        break;
39
40    default:
41        return -EINVAL;
42    }
43
44    return 0;
45}
46
47static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos)
48{
49    unsigned long p = *ppos;
50    unsigned int count = size;
51    int ret = 0;
52    struct chr_dev* dev = filp->private_data;
53
54    printk(KERN_ALERT"=========%s()=========\n", __func__);
55    if(p >= CHAR_DEV_DATA_SIZE) {
56        return 0;
57    }
58
59    if(count > CHAR_DEV_DATA_SIZE - p) {
60        return 0;
61    }
62    //将内核中数据dev->data,读取到用户空间buf中,读取count字节
63    if(copy_to_user(buf, (void*)(dev->data + p), count)) {
64        return -EINVAL;
65    } else {
66        *ppos += count;
67        ret = count;
68    }
69
70    return ret;
71}
72
73static ssize_t chr_write(struct file* filp, const char __user* buf, size_t size, loff_t *ppos)
74{
75    unsigned long p = *ppos;
76    unsigned int count = size;
77    int ret = 0;
78    struct chr_dev* dev = filp->private_data;
79
80    printk(KERN_ALERT"=========%s()=========\n", __func__);
81    if(p >= CHAR_DEV_DATA_SIZE) {
82        return 0;
83    }
84
85    if(count > CHAR_DEV_DATA_SIZEE - p) {
86        count = CHAR_DEV_DATA_SIZE - p;
87    }
88    //将用户空间buf中数据copy到内核空间dev->data中,copy count字节数据
89    if(copy_from_user(dev->data + p, buf, count)) {
90        ret = -EINVAL;
91    } else {
92        *ppos += count;
93        ret = count;
94    }
95
96    return ret;
97}
98
99static loff_t chr_llseek(struct file* filp, loff_t offset, int orig)
100{
101    loff_t ret = 0;
102
103    printk(KERN_ALERT"=========%s()=========\n", __func__);
104    /* orig can be SEEK_SET, SEEK_CUR, SEEK_END */
105    switch(orig) {
106    case 0:
107        if(offset < 0) {
108            ret = -EINVAL;
109            break;
110        }
111
112        if((unsigned int) offset > CHAR_DEV_DATA_SIZE) {
113            ret = -EINVAL;
114            break;
115        }
116
117        filp->f_pos = (unsigned int) offset;
118        ret = filp->f_pos;
119        break;
120
121    case 1:
122        if((filp->f_pos + offset) > CHAR_DEV_DATA_SIZE) {
123            ret = -EINVAL;
124            break;
125        }
126
127        if((filp->f_pos + offset) < 0) {
128            ret = -EINVAL;
129            break;
130        }
131
132        filp->f_pos += offset;
133        ret = filp->f_pos;
134        break;
135
136    default:
137        ret = - EINVAL;
138        break;
139    }
140
141    return ret;
142}
143
144int chr_release(struct inode *inode, struct file *filp)
145{
146  return 0;
147}
148
149static const struct file_operations chr_fops =
150{
151  .owner    = THIS_MODULE,
152  .open     = chr_open,
153  .release  = chr_release,
154  .read     = chr_read,
155  .write    = chr_write,
156  .llseek   = chr_llseek,
157  .ioctl    = chr_ioctl,
158
159};
160
161static int chr_dev_init(void)
162{
163    int result;
164    dev_t devno;
165
166    /* 注册设备号 */
167    result = alloc_chrdev_region(&devno, 0, 1, "chardev");
168    if (result < 0) {
169        return result;
170    }
171
172    // 分配自定义设备结构体内存
173    chr_devp = kmalloc(CHAR_DEV_NO * sizeof(struct chr_dev), GFP_KERNEL);
174    if (!chr_devp) {
175        result =  - ENOMEM;
176        goto err;
177    }
178    memset(chr_devp, 0, sizeof(struct chr_dev));
179
180    /*初始化设备*/
181    cdev_init(&chr_devp->cdev, &chr_fops);
182    chr_devp->cdev.owner = THIS_MODULE;
183
184    /* 添加设备 */
185    chr_major = MAJOR(devno);
186    cdev_add(&chr_devp->cdev, MKDEV(chr_major, 0), CHAR_DEV_NO);
187  
188    /*初始自定义设备结构体内存数据*/
189    chr_devp->data = kmalloc(CHAR_DEV_DATA_SIZE, GFP_KERNEL);
190    memset(chr_devp->data, '*', CHAR_DEV_DATA_SIZE / 100 ); //为避免输出太多影响结果显示,此处仅仅初始化40个字节。
191    
192    return 0;
193
194err:
195  unregister_chrdev_region(devno, 1);
196
197  return result;
198}
199
200static void chr_dev_exit(void)
201{
202    cdev_del(&chr_devp->cdev); //delete device
203    kfree(chr_devp); // release device memory
204    unregister_chrdev_region(MKDEV(chr_major, 0), 1); // unregister char device No.
205}
206
207module_init(chr_dev_init);
208module_exit(chr_dev_exit);
209
210MODULE_LICENSE("GPL");
211MODULE_AUTHOR("shallnet");
212MODULE_DESCRIPTION("blog.csdn.net/shallnet");
213

应用程序实现代码如下:


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
1#include <stdio.h>
2#include <fcntl.h>
3#include <unistd.h>
4#include <string.h>
5#include <sys/ioctl.h>
6#include <sys/mman.h>
7#include <errno.h>
8
9#define SHR_MEMSIZE           4096
10#define MEM_CLEAR               0x0
11#define MEM_RESET               0x1
12#define CHAR_DEV_FILENAME       "/dev/sln_chardev"
13
14int main()
15{
16        int     fd;
17        char    shm[SHR_MEMSIZE];
18
19        /* 打开设备文件 */
20        fd = open(CHAR_DEV_FILENAME, O_RDWR);
21        if(fd < 0) {
22                printf("open <%s> failed!\n", CHAR_DEV_FILENAME);
23                return -1;
24        }
25
26        /* 直接设置共享内存数据 */
27        snprintf(shm, sizeof(shm), "this data is writed by user!");
28
29        /* 写入数据 */
30        printf("======== Write data========\n");
31        if (write(fd, shm, strlen(shm)) < 0) {
32            printf("write(): %s\n", strerror(errno));
33            return -1;
34        }
35
36        /* 再读取数据,以验证应用上设置成功 */
37        printf("======== Read data========\n");
38        if (lseek(fd, 0, SEEK_SET) < 0) {
39            printf("llseek(): %s\n", strerror(errno));
40            return -1;
41        }
42
43        if (read(fd, shm, SHR_MEMSIZE) < 0) {
44            printf("read(): %s\n", strerror(errno));
45            return -1;
46        }
47        printf("read data: %s\n", shm);
48
49        /* 再清空数据之后再读取 */
50        printf("========= Clear it now: =======\n");
51        if (ioctl(fd, MEM_CLEAR, NULL) < 0) {
52            printf("ioctl(): %s\n", strerror(errno));
53            return -1;
54        }
55
56        if (lseek(fd, 0, SEEK_SET) < 0) {
57            printf("llseek(): %s\n", strerror(errno));
58            return -1;
59        }
60
61        if (read(fd, shm, SHR_MEMSIZE) < 0) {
62            printf("read(): %s\n", strerror(errno));
63            return -1;
64        }
65        printf("read data: %s\n", shm);
66
67
68        /* reset all data, read it and check whether it is ok */
69        printf("========= Reset it now: =======\n");
70        if (ioctl(fd, MEM_RESET, NULL) < 0) {
71            printf("ioctl(): %s\n", strerror(errno));
72            return -1;
73        }
74
75        if (lseek(fd, 0, SEEK_SET) < 0) {
76            printf("llseek(): %s\n", strerror(errno));
77            return -1;
78        }
79
80        if (read(fd, shm, SHR_MEMSIZE) < 0) {
81            printf("read(): %s\n", strerror(errno));
82            return -1;
83        }
84        printf("read data: %s\n", shm);
85
86        close(fd);
87        return 0;
88}
89

在编译驱动和应用程序之后
就可以验证内核和应用的数据交换是否成功了。运行验证
    
如下:


1
2
3
4
5
6
1# insmod chardev.ko  //首先插入模块
2# cat /proc/devices | grep chardev //查看驱动模块对应主设备号
3248 chardev
4# mknod  /dev/sln_chardev c 248 0     //为设备创建对应节点
5#
6

然后再
运行应用程序:


1
2
3
4
5
6
7
8
9
10
1# ./read_app
2======== Write data========
3======== Read data========
4read data: this data is writed by user!************     //读取应用设置数据成功
5========= Clear it now: =======     //在内核清空数据,应用读取数据为空
6read data:
7========= Reset it now: =======     //在内核重设空间数据为hello, user!,应用上再读取该数据,输出预期值!
8read data: hello, user!
9#
10

本节只实现了
file_operations
结构体中
部分函数,在后面我们还可以实现其它的函数,也可以实现内核和应用交互数据。

给TA打赏
共{{data.count}}人
人已打赏
安全运维

WordPress网站专用docker容器环境带Waf

2020-7-18 20:04:44

安全运维

运维安全-Gitlab管理员权限安全思考

2021-9-19 9:16:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索