前面讲到了阻塞I/O,它不得不让进程睡眠,若该进程就只负责一个文件操作,
那还好,可偏偏有时一个进程得负责好几个文件,而因为一个阻塞IO而导致了其他
事情也做不来,这是不可取的方法。而有时用非阻塞IO,即read没数据可读立刻返
回,write没内存可写立刻返回,那有时我们就想这样下来岂不想做的事情的都没做
啊。这时我们就想尽办法,要把我们所想做的事情给做完,我就一个一个文件判断
是否能满足我的要求,如果一遍不行,那我再来一遍,就这样轮流判断啊==>这引出
了非阻塞IO的操作方法-->轮回操作(poll)。而对应用户程序是 select 系统调
用。
1、系统调用 select 使用方法
(1)构造文件描述符表集
(2)清除文件描述符表集
(3)测试文件描述符是否在添加的集合中
(4)清空描述符表集
- void FD_CLR(int fd, fd_set *fdset)
- void FD_SET(int fd, fd_set *fdset)
- void FD_ZERO(fd_set *fdset)
- int FD_ISSET(int fd, fd_set *fdset)
- Return:若fd在描述符表中,则返回非0,否则返回0
select 函数原型
- #include <sys/seclect.h>
- /*
- * @brief 非阻塞IO轮询操作
- * @param[in] maxfd 需要检查的号码最高的文件描述符加 1
- * @param[in] readfds 监视读的文件描述符集
- * @param[in] writefds 视写的文件描述符集
- * @param[in] exceptfds 监视异常的文件描述符集
- * @param[in] timeout 则是一个时间上限值,超过该值后,即使仍没有描述符准备好也会返回
- * struct timeval
- {
- int tv_sec; //秒
- int tv_usec; //微秒
- }
- * @return @li == -1 表示出错
- * @li == 0 表示没有描述符准备好
- * @li >> 0 表示已有几个描述符准备好了
- */
- int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
- struct timeval *tptr)
2、poll 设备方法
这个驱动方法被调用, 无论何时用户空间程序进行一个 poll, select, 或者
epoll 系统调用, 涉及一个和驱动相关的文件描述符. 这个设备方法负责这两
步:
(1)在一个或多个可指示查询状态变化的等待队列上调用 poll_wait. 如果
没有文件描述符可用作 I/O, 内核使这个进程在等待队列上等待所有的传递给系统
调用的文件描述符.
(2)返回一个位掩码, 描述可能不必阻塞就立刻进行的操作.
这 2 个操作常常是直接的, 并且趋向与各个驱动看起来类似. 但是, 它们依
赖只能由驱动提供的信息, 因此, 必须由每个驱动单独实现.
- #include <linux/poll.h>
- typedef struct poll_table_struct
- {
- poll_queue_proc qproc;
- unsigned long key;
- } poll_table;
- /*
- * @brief poll 设备驱动
- * @param[in] filp 文件指针==>获取设备结构指针
- * @param[in] wait poll轮询操作的等待队列
- * @return 返回是位掩码
- */
- unsigned int (*poll) (struct file *filp, poll_table *wait);
- 第一步:【将用户程序的要监听的描述符集<等待队列>添加到 poll_table 中】
- /*
- * @brief
- * @param[in] filp 文件指针==>获取设备结构指针==>从而获取要监听的描述符集
- * @param[in] queue 监听的描述符集<等待队列>
- * @param[in] wait poll轮询操作的等待队列
- * @return no
- */
- void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);
- 第二步:位掩码
- POLLIN 如果设备可被不阻塞地读, 这个位必须设置.
- POLLRDNORM 这个位必须设置, 如果"正常"数据可用来读.
- 一个可读的设备返回 (POLLIN|POLLRDNORM).
- POLLOUT 这个位在返回值中设置, 如果设备可被写入而不阻塞.
- POLLWRNORM 这个位和 POLLOUT 有相同的含义, 并且有时它确实是相同的数.
- 一个可写的设备返回( POLLOUT|POLLWRNORM).
- POLLRDBAND 这个位指示带外数据可用来从设备中读取. 当前只用在 Linux 内核的一个地方
- (DECnet代码)并且通常对设备驱动不可用.
- POLLPRI 高优先级数据(带外)可不阻塞地读取. 这个位使 select 报告在文件上遇到一个异
- 常情况, 因为 selct 报告带外数据作为一个异常情况.
- POLLHUP 当读这个设备的进程见到文件尾, 驱动必须设置 POLLUP(hang-up). 一个调用
- select的进程被告知设备是可读的, 如同 selcet 功能所规定的.
- POLLERR 一个错误情况已在设备上发生. 当调用 poll, 设备被报告位可读可写, 因为读写
- 都返回一个错误码而不阻塞.
- POLLWRBAND 如同 POLLRDBAND , 这个位意思是带有零优先级的数据可写入设备. 只有 poll 的
- 数据报实现使用这个位, 因为一个数据报看传送带外数据
- eg、
- static unsigned int XXX_poll(struct file *filp, poll_table *wait)
- {
- unsigned int mask = 0;
- struct XXX_dev *dev = filp->private_data; //获得设备结构指针
-
- poll_wait(filp, &dev->r_fds, wait); //加读等待对列头
- poll_wait(filp ,&dev->w_fds, wait); //加写等待队列头
-
- if(...)//可读
- {
- mask |= POLLIN | POLLRDNORM; //标识数据可获得
- }
- if(...)//可写
- {
- mask |= POLLOUT | POLLRDNORM; //标识数据可写入
- }
-
- return mask;
- }
3、poll 实现源代码
在头文件/include/linux/poll.h 和 跟目录下/fs/select.c 中
阅读(1146) | 评论(0) | 转发(1) |