您的位置:首页 > 运维架构 > Linux

linux设备驱动中的阻塞与非阻塞(二)

2015-11-23 13:07 253 查看
上一节我们分析了linux驱动中阻塞的实现,利用等待队列的休眠和唤醒机制实现,这一节我们探讨一下非阻塞的实现!

其实在非阻塞中,我们可以直接打开一个设备,进行读取和写入操作,但是这样做很不好,因为使用的是非阻塞,所以无论能不能写入或者读取都会返回,就像小明去买衣服,去到商店,商店开门了,买了衣服回来,这固然是好,可是万一商店老板来晚了一分钟,小明看到一关门就回去了,要是他等待多一分钟,就可以买到了,所以我们都希望无论是读还是写,都不要无功而返,可是非阻塞又不会等待或者休眠,那怎么办,这时就要在应用程序是调用select或者poll函数了,这两个函数会先进行判断,看文件能否进行非阻塞的读写(即能否获取资源),若能则返回,否则一直在等待,但是不休眠,直到条件满足或者等待时间到达了再返回。

实际上,这两个函数无论调用哪个,其结果都是调用底层驱动的poll()函数,所以我们要在驱动中实现poll()函数,现在我们先分析一下应用程序中的select()函数。

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

参数分析:fd_set可以看做是一个集合,里面存放了很多的文件描述符,所以readfds是存放读取的文件描述符,我们通过select监视这些文件描述符的读取变化,如果里面有文件可以读取了,select就会返回一个大于0的数,不然会一直等待,但是不会休眠,知道条件满足或者定义的时间timeout到达,就会返回。

writefds是写文件描述符的集合,原理同读的一样,只要有文件可以读取了,就会发生变化,返回一个大于0的数,不然也是一直等待或者时间到达才会返回,并且不会休眠。errorfds这个是异常文件描述符的集合,有文件出现异常时,会返回,与那里和其他的一样。

maxfdp是一个整数,是集合中所有文件描述符的范围,即集合中最大文件描述符的值加1.

struct
timeval,是一个常用的结构体,其原型如下所示:

struct
timeval{

long
tv_sec; //秒

long
tv_usec; //微秒

}

当定义了timeout后,时间到达,该函数也会返回。

返回值:负值,select发送错误;正值:有文件可读可写或者出现异常;0值:等待超时,没有可读可写或者异常的文件。

当select返回后,我们可以根据返回值进一步判断能够进行写操作,或者是读操作,其中select的使用,涉及到以下几个宏:

FD_ZERO(fd_set
*fdset):清空fdset与所有文件句柄的联系。

FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。

FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否 可读写,返回值大于0表示可读写。

一个通过调用select进行非阻塞读写的例子:

fd
= open("/dev/globalfifo",O_RDONLY | O_NONBLOCK);

FD_ZERO(&rfds);

FD_ZERO(&wfds);

FD_SET(fd,&rfds);

FD_SET(fd,&wfds);

.........

select(fd+1,&rfds,&wfds,NULL,NULL);
//函数会在这里一直轮询,知道有文件可读写才会返回,接着进行非阻塞访问

if(FD_ISSET(fd,&rfds))//
进一步判断,该文件是否可以进行读操作,若是,则进行非阻塞的读操作

read(...);

if(FD_ISSET(fd,&wfds))//
进一步判断,该文件是否可以进行写操作,若是,则进行非阻塞的写操作

write(...);

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是应用程序select的分析,现在我们来探讨下驱动中的poll函数如何实现,因为顶层的poll、select或者epoll,其底层驱动都是调用poll函数实现的。设备驱动中poll()函数的原型是:

unsigned int (*poll) (struct file *filp,poll_table *wait);

函数返回可以立即执行操作的位掩码,有这几种:POLLIN(设备可读),POLLRDNORM(数据可读),POLLOUT(设备可写),POLLWRNORM(数据可写),一般设备可读返回:POLLIN
| POLLRDNORM,可写则返回:POLLOUT | POLLWRNORM。

poll函数主要完成两个步骤:

步骤一:对可能引起设备文件状态变化的等待队列调用poll_wait函数,将对应的等待队列头部添加到poll_table中。

void poll_wait(struct file *filp, wait_queue_heat_t *queue,poll_table *wait); //使用poll_wait将等待队列添加到poll_table中

步骤二:返回一个用来描述操作是否可以立即执行,无阻塞读、写访问的掩码。

poll函数的驱动模板:

static unsigned int xxx_poll(struct file *filp, poll_table *wait){

....

unsigned int mask = 0;

down((&sem);

poll_wait(filp,&read,wait);

poll_wait(filp,&write,wait);

if(read_buffer_not_empty)

mask |= POLLIN | POLLRDNORM;

if(write_buffer_not_full)

mask |= POLLOUT | POLLWRNORM;

up(&sem);

return mask;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: