现在的位置: 首页 > 综合 > 正文

驱动程序学习(五)select and poll

2013年10月10日 ⁄ 综合 ⁄ 共 3349字 ⁄ 字号 评论关闭

Select()系统调用

在驱动程序中一个进程同时处理多个文件描述符是很常见的情况。select()系统调用可以使进程检测同时等待的多个I/O设备,当没有设备准备好时,select()阻塞,其中任一设备准备好时,select()就返回。

select()的调用形式为:
#include <sys/select.h>
#include <sys/time.h>

int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout);
select的第一个参数是文件描述符集中要被检测的比特数,这个值必须至少比待检测的最大文件描述符大1;
参数readfds指定了被读监控的文件描述符集;
参数writefds指定了被写监控的文件描述符集;
而参数exceptfds指定了被例外条件监控的文件描述符集。
参数timeout起了定时器的作用:到了指定的时间,无论是否有设备准备好,都返回调用。timeval的结构定义如下:
struct timeval{
long tv_sec; //表示几秒
long tv_usec; //表示几微妙
}

timeout取不同的值,该调用就表现不同的性质:
1.timeout为0,调用立即返回;
2.timeout为NULL,select()调用就阻塞,直到知道有文件描述符就绪;
3.timeout为正整数,就是一般的定时器。

select调用返回时,除了那些已经就绪的描述符外,select将清除readfds、writefds和exceptfds中的所有没有就绪的描述符。select的返回值有如下情况:
1.正常情况下返回就绪的文件描述符个数;
2.经过了timeout时长后仍无设备准备好,返回值为0;
3.如果select被某个信号中断,它将返回‐1并设置errno为EINTR。
4.如果出错,返回‐1并设置相应的errno。

当用户调用select系统调用时,select系统调用会先调用poll_initwait(&table);,然后调用驱动程序中
struct file_operations下的fop->poll函数,在这个函数里应该调用poll_wait(),将current加到某个等待队列(这里调用poll_wait()),并检查是否有效,如果无效就调用schedule_timeout();去睡眠。事件发生后,schedule_timeout()回来,,调用fop->poll();,检查到可以运行,就调用poll_freewait(&table);从而完成select系统调用。重要的是fop->poll()里面要检查是否就绪,如果是,要返回相应标志。

Poll()系统调用

Linux中的字符设备驱动中有一个函数

unsigned int (*poll)(struct file * fp, struct poll_table_struct * table)

此函数在系统调用select内部被使用,作用是把当前的文件指针挂到设备内部定义的等待

队列中。这里的参数table可以不考虑,是在select函数实现过程中的一个内部变量。

函数具体实现时:

wait_queue_head_t t = ((struct mydev *)filp->private_data)->wait_queue;

poll_wait(filp, t, table);

return mask;

这里mask可以是:

POLLIN | POLLRDNORM

POLLOUT | POLLWRNORM

这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明:

#include <poll.h>

int poll(struct pollfd fds[], nfds_t nfds, int timeout);

参数说明:

fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符

nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
  
timeout:是poll函数调用阻塞的时间,单位:毫秒

返回值:

  >0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;

       ==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll()
函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
      -1: poll函数调用失败,同时会自动设置全局变量errno;
   
看完上面这些定义和介绍,我们做个实验来验证下。
根据select的定义,在调用他后
调用驱动程序中 struct file_operations下的fop->poll函数
我们修改的基础是驱动程序学习(四)并发控制(3)阻塞和非阻塞里的驱动源码
所以首先我们要在驱动程序中定义poll函数。

首先修改file_operations
在该结构体里添加
.poll = chardev.poll

然后添加处理函数
static  ssize_t int chardev_poll(struct *filp,poll_table *wait )
{
unsigned int mask = 0;
poll_wait(filp,&outq,wait);

if(flag !=0)
{
mask |= POLLIN | POLLRDNORM;
}
return mask;
}

然后编写我们的测试程序
chardevtest_select.c

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>

main()
{
int fd,num;
fd_set rfds;
struct timeval tv;

fd = open("/dev/chardev",O_RDWR,S_IRUSR | S_IWUSR);
if(fd != -1)
{
while(1)
{
/*查看chardev是否有输入*/
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
/*设置超时时间为5s*/
tv.tv_sec = 5;
tv.tv_usec = 0;
select(fd+1,&rfds,NULL,NULL,&tv);

if(FD_ISSET(fd,&rfds))
{
read(fd,&num,sizeof(int));
printf("The chardev is %d\n",num);

  if(num == 0)
{
close(fd);
break;
}
}
else
printf("NO data within 5 seconds.\n");
}
}
else
printf("Open file failure.\n");
}

首先来介绍一下该文件中用到的API
FD_ZERO(fd_set *set)用来清除一个文件描述符集
FD_SET (int fd,fd_set * set) 将一个文件描述符加入到文件描述符集中
FD_CLR (int fd,fd_set *set)      将一个文件描述符从文件描述符集中清除
FD_ISSET(int fd,fd_set *set)判断文件描述符是否被置位了。
通过这几个函数的讲解我们就能看明白测试程序了
FD_ZERO(&rfds);先清楚文件描述符集
FD_SET(fd,&rfds);然后将我们要检测的文件描述符添加到文件描述符集里。
接着设置时间,然后监听。
所以这个函数的实际效果就是5s内没有输入则打印信息
NO data within 5 seconds.



抱歉!评论已关闭.