多路复用都是在阻塞模式下有效!
linux中的系统调用函数默认都是阻塞模式,例如应用层读不到驱动层的数据时,就会阻塞等待,直到有数据可读为止。
问题:在一个进程中,同时打开了两个或者两个以上的文件,读第一个文件时没有数据阻塞了。程序就停止在此位置等待,可是第二个文件有数据可读了,数据读不到怎么办?
回答:此种问题可以用多路复用 select / poll 完美解决。
多路复用 select / poll :对多个文件描述符进行监控,一旦有文件描述符可读或者可写,就会通知应用程序进行读写。
1、select
// 监控多个文件描述符属性变化,包括可读,可写,出错异常
// 参数1:监控的最大描述符加1
// 参数2:监控的可读描述符集合
// 参数3:监控的可写描述符集合
// 参数4:监控的出错异常描述符集合
// 参数5:监控时间 NULL-永远等待 0-不阻塞 固定时间
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
// 检查指定的文件描述符是否可读写
int FD_ISSET(int fd, fd_set *set);
// 将一个文件描述符加入到监控集合中
void FD_SET(int fd, fd_set *set);
// 将指定文件描述符从集合中删除
void FD_CLR(int fd, fd_set *set);
// 清空集合
void FD_ZERO(fd_set *set);
2、poll
struct pollfd {
int fd; /* 监控的文件描述符 */
short events; /* 等待的事件 */
short revents; /* 发生的事件 */
};
// 监控多个文件描述符的属性变化
// 参数1:文件描述符集合
// 参数2:文件描述符数量
// 参数3:超时时间
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
POLLIN:可读
POLLOUT:可写
POLLERR:出错
------------------------
应用:实现一个poll的接口:
struct pollfd pfd[2]; pfd[0].fd = fd; pfd[0].events = POLLIN; // 0为标准输入 pfd[1].fd = 0; pfd[1].events = POLLIN; ret = poll(pfd, 2, -1); if(ret > 0) { // 查询 if(pfd[0].revents & POLLIN) ret = read(fd, &event, sizeof(struct key_event)); if(pfd[1].revents & POLLIN) fgets(kbd_buf, 64, stdin); }
------------------------
驱动:实现一个poll的接口:
unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts) { // 如果没有数据返回一个0, 有数据返回一个POLLIN unsigned int mask = 0; //将当前的等待队列头,注册到vfs层 //参数1--文件对象 //参数2--等待队列头 //参数3--当前函数第二参数 poll_wait(filp, &key_dev->wq_head, pts); if(key_dev->have_data) mask |= POLLIN; return mask; }