Linux设备驱动中的阻塞和非阻塞I/0,简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。
一、基本概念:
二、两种操作
阻塞地读取一个字符:
char buf; fd = open("/dev/ttyS1",O_RDWR); ..... res = read(fd,&buf,1); //当串口上有输入时才返回,没有输入则进程挂起睡眠 if(res == 1) { printf("%c/n",buf); }
非阻塞地读一个字符:
char buf; fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK); //O_NONBLOCK 非阻塞标识 ..... while(read(fd,&buf,1)!=1); //串口上没有输入则返回,所以循环读取 printf("%c/n",buf);
阻塞操作常常用等待队列来实现,而非阻塞操作用轮询的方式来实现。非阻塞I/O的操作在应用层通常会用到select()和poll()系统调用查询是否可对设备进行无阻塞访问。select()和poll()系统调用最终会引发设备驱动中的poll()函数被调用。这里对队列就不多介绍了,大家可以看看数据结构里面的知识点。
应用层的select()原型为:
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptionfds, struct timeval *timeout);
numfds 的值为需要检查的号码最高的文件描述符加1,若select()在等待timeout时间后,若没有文件描述符准备好则返回。
应用程序为:
#inlcude------ int main(void) { int fd,num; char rd_ch[BUFFER_LEN]; fd_set rfds,wfds; //读写文件描述符集 //以非阻塞方式打开/dev/globalfifo设备文件 fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK); if(fd != -1) {
//FIFO 清零 if(ioctl(fd,FIFO_CLEAR,0) < 0) { printf("ioctl cmd failed /n"); } while(1) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd,&rfds); FD_SET(fd,&wfds); select(fd+1,&rfds,&wfds,null,null); } } }
下面说说设备驱动中的poll()函数,函数原型如下:
static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一个参数是file结构体指针,第三个参数是轮询表指针,这个函数应该进行两项工作
驱动函数中的poll()函数典型模板如下:
static
unsigned int
xxx_poll(struct
file *filp,struct
socket *sock,
poll_table *wait){
unsigned int
mask = 0;
struct
xxx_dev *dev = filp->private_data;
//获得设备结构体指针
...
poll_wait(filp,&dev->r_wait,wait);
//加读等待队列头到poll_table
poll_wait(filp,&dev->w_wait,wait);
//加写等待队列头到poll_table
...
if(...)
//可读
mask |= POLLIN | POLLRDNORM;
if(...)
//可写
mask |= POLLOUT | POLLRDNORM;
...
return
mask;
}
三、总结
阻塞与非阻塞操作: