本文主要介绍了Linux高级字符设备驱动 poll方法(select多路监控原理与实现)相关的知识,希望对你有一定的参考价值。
1、什么是Poll方法,功能是什么?

2、Select系统调用(功能)
Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程。
int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)
Select系统调用(参数)
1)Maxfd:
文件描述符的范围,比待检测的最大文件描述符大1
2)Readfds:
被读监控的文件描述符集
3)Writefds:
被写监控的文件描述符集
4)Exceptfds:
被异常监控的文件描述符集;
5)Timeout:
定时器,Timeout取不同的值,该调用有不同的表现:
1>Timeout值为0,不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值。
2>Timeout为NULL,select将阻塞进程,直到某个文件满足要求
3>Timeout 值 为 正 整 数 , 就 是 等 待 的 最 长 时 间 , 即select在timeout时间内阻塞进程。
3、Select系统调用(返回值)
Select调用返回时,返回值有如下情况:
1)正常情况下返回满足要求的文件描述符个数;
2)经过了timeout等待后仍无文件满足要求,返回值为0;
3)如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4)如果出错,返回-1并设置相应的errno。
4、Select系统调用(使用方法)
1)将要监控的文件添加到文件描述符集
2)调用Select开始监控
3)判断文件是否发生变化
系统提供了4个宏对描述符集进行操作:
#include <sys/select.h>
void FD_SET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
宏FD_ZERO清空文件描述符集fdset;
在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。
FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //设置描述符
FD_SET(fd2,&fds); //设置描述符
maxfdp=fd1+1; //描述符最大值加1,假设fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select错误,退出程序
case 0:break;
default:
if(FD_ISSET(fd1,&fds)) //测试fd1是否可读
5、poll方法
应用程序常常使用select系统调用,它可能会阻塞进程。这个调用由驱动的 poll 方法实现,原型为:unsigned int (*poll)(struct file *filp,poll_table *wait)
Poll设备方法负责完成:
1)使用poll_wait将等待队列添加到poll_table中。
2)返回描述设备是否可读或可写的掩码。
位掩码
1>POLLIN 设备可读
2>POLLRDNORM数据可读
3>POLLOUT设备可写
4>POLLWRNORM数据可写
设备可读通常返回(POLLIN|POLLRDNORM )
设备可写通常返回(POLLOUT|POLLWRNORM )
6、范例
static unsigned int mem_poll(struct file *filp,poll_table *wait)
{
struct scull_pipe dev =filp->private_data;
unsigned int mask =0;
/ 把等待队列添加到poll_table */
poll_wait(filp,&dev->inq,wait);
/ 返回掩码/
if (有数据可读)
mask = POLLIN |POLLRDNORM;/ 设备可读/
return mask;
}
7、工作原理
Poll方法只是做一个登记,真正的阻塞发生在select.c 中的 do_select函数。
8、实例分析
1)poll型设备驱动memdev.h源码
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#ifndef _MEMDEV_H_ #define _MEMDEV_H_ #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0 /*预设的mem的主设备号*/ #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*设备数*/ #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem设备描述结构体*/ struct mem_dev { char *data; unsigned long size; wait_queue_head_t inq; }; #endif /* _MEMDEV_H_ */
2)Poll型设备驱动memdev.c源码
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/poll.h> #include "memdev.h" static mem_major = MEMDEV_MAJOR; bool have_data = false; /*表明设备有足够数据可供读*/ module_param(mem_major, int, S_IRUGO); struct mem_dev *mem_devp; /*设备结构体指针*/ struct cdev cdev; /*文件打开函数*/ int mem_open(struct inode *inode, struct file *filp) { struct mem_dev *dev; /*获取次设备号*/ int num = MINOR(inode->i_rdev); if (num >= MEMDEV_NR_DEVS) return -ENODEV; dev = &mem_devp[num]; /*将设备描述结构指针赋值给文件私有数据指针*/ filp->private_data = dev; return 0; } /*文件释放函数*/ int mem_release(struct inode *inode, struct file *filp) { return 0; } /*读函数*/ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*判断读位置是否有效*/ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p; while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while */ { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; wait_event_interruptible(dev->inq,have_data); } /*读数据到用户空间*/ if (copy_to_user(buf, (void*)(dev->data + p), count)) { ret = - EFAULT; } else { *ppos += count; ret = count; printk(KERN_INFO "read %d bytes(s) from %dn", count, p); } have_data = false; /* 表明不再有数据可读 */ /* 唤醒写进程 */ return ret; } /*写函数*/ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*分析和获取有效的写长度*/ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p; /*从用户空间写入数据*/ if (copy_from_user(dev->data + p, buf, count)) ret = - EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "written %d bytes(s) from %dn", count, p); } have_data = true; /* 有新的数据可读 */ /* 唤醒读进程 */ wake_up(&(dev->inq)); return ret; } /* seek文件定位函数 */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = offset; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + offset; break; case 2: /* SEEK_END */ newpos = MEMDEV_SIZE -1 + offset; break; default: /* can‘t happen */ return -EINVAL; } if ((newpos<0) || (newpos>MEMDEV_SIZE)) return -EINVAL; filp->f_pos = newpos; return newpos; } unsigned int mem_poll(struct file *filp, poll_table *wait) { struct mem_dev *dev = filp->private_data; unsigned int mask = 0; /*将等待队列添加到poll_table表中 */ poll_wait(filp, &dev->inq, wait); if (have_data) mask |= POLLIN | POLLRDNORM; /* readable */ return mask; } /*文件操作结构体*/ static const struct file_operations mem_fops = { .owner = THIS_MODULE, .llseek = mem_llseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, .poll = mem_poll, }; /*设备驱动模块加载函数*/ static int memdev_init(void) { int result; int i; dev_t devno = MKDEV(mem_major, 0); /* 静态申请设备号*/ if (mem_major) result = register_chrdev_region(devno, 2, "memdev"); else /* 动态分配设备号 */ { result = alloc_chrdev_region(&devno, 0, 2, "memdev"); mem_major = MAJOR(devno); } if (result < 0) return result; /*初始化cdev结构*/ cdev_init(&cdev, &mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; /* 注册字符设备 */ cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); /* 为设备描述结构分配内存*/ mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL); if (!mem_devp) /*申请失败*/ { result = - ENOMEM; goto fail_malloc; } memset(mem_devp, 0, sizeof(struct mem_dev)); /*为设备分配内存*/ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devp[i].size = MEMDEV_SIZE; mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devp[i].data, 0, MEMDEV_SIZE); /*初始化等待队列*/ init_waitqueue_head(&(mem_devp[i].inq)); //init_waitqueue_head(&(mem_devp[i].outq)); } return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } /*模块卸载函数*/ static void memdev_exit(void) { cdev_del(&cdev); /*注销设备*/ kfree(mem_devp); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/ } MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料!进群私聊管理领取 内核资料包(含视频教程、电子书、实战项目及代码)

3)测试程序app-read.c源码
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#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> int main() { int fd; fd_set rds; //声明描述符集合 int ret; char Buf[128]; /*初始化Buf*/ strcpy(Buf,"memdev is char dev!"); printf("BUF: %sn",Buf); /*打开设备文件*/ fd = open("/dev/memdev0",O_RDWR); FD_ZERO(&rds); //清空描述符集合 FD_SET(fd, &rds); //设置描述符集合 /*清除Buf*/ strcpy(Buf,"Buf is NULL!"); printf("Read BUF1: %sn",Buf); ret = select(fd + 1, &rds, NULL, NULL, NULL);//调用select()监控函数 if (ret < 0) { printf("select error!n"); exit(1); } if (FD_ISSET(fd, &rds)) //测试fd1是否可读 read(fd, Buf, sizeof(Buf)); /*检测结果*/ printf("Read BUF2: %sn",Buf); close(fd); return 0; }
以上是关于Linux高级字符设备驱动 poll方法(select多路监控原理与实现)的主要内容
最后
以上就是顺心导师最近收集整理的关于Linux之字符设备驱动-poll方法(select多路监控原理与实现)的全部内容,更多相关Linux之字符设备驱动-poll方法(select多路监控原理与实现)内容请搜索靠谱客的其他文章。
发表评论 取消回复