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;
}
其实在非阻塞中,我们可以直接打开一个设备,进行读取和写入操作,但是这样做很不好,因为使用的是非阻塞,所以无论能不能写入或者读取都会返回,就像小明去买衣服,去到商店,商店开门了,买了衣服回来,这固然是好,可是万一商店老板来晚了一分钟,小明看到一关门就回去了,要是他等待多一分钟,就可以买到了,所以我们都希望无论是读还是写,都不要无功而返,可是非阻塞又不会等待或者休眠,那怎么办,这时就要在应用程序是调用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;
}
相关文章推荐
- Linux源码安装的文件默认的存放路径
- Linux系统的时间管理及优化
- PAM-常用的Linux可插拔认证模块(PAM)应用举例(一)
- fastdfs图片服务器搭建
- Linux framebuffer显示bmp图片
- PAM-Linux可插拔认证模块(PAM)的配置文件、工作原理与流程 .
- Archlinux On Raspberrypi B+
- Make 命令教程
- 跨linux 服务器,远程ip文件共享 SSHFS
- Linux Advance--文件IO--创建一个具有空洞的文件
- Linux命令date日期时间和Unix时间戳互转
- linux C++ scandir 的使用
- linux 多线程编程-读写者问题
- 详解Linux下iptables中的DNAT与SNAT设置
- linux后台运行和关闭、查看后台任务
- Linux 文本的^M问题
- linux命令学习笔记:cut详解
- linux命令学习:echo详解,格式化输出,不换行输出
- Linux目录操作命令
- XEN虚拟机在Linux上的安装和使用教程