Chinaunix首页 | 论坛 | 博客
  • 博客访问: 338266
  • 博文数量: 88
  • 博客积分: 907
  • 博客等级: 准尉
  • 技术积分: 1230
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-26 13:27
文章分类

全部博文(88)

文章存档

2017年(1)

2014年(3)

2013年(29)

2012年(21)

2011年(26)

2010年(8)

分类: LINUX

2013-05-10 14:40:05


poll用来做什么,就是用于在应用层select时候,驱动检查有没有数据可以读的函数。在很多函数里面都要实现。目的就是告诉应用层数据到了。
当select的时,是否直接返回,还得取决于传入的fd的属性,如果当然属性是阻塞型,会使用驱动的poll函数做检查,有数据就会返回相应的掩码,如果没有,那么select就在会阻塞,当驱动层有数据到来时,会再一次的调用poll函数做检查,如果有数据了,那么立马会唤醒阻塞的进程或者线程。那么select就会返回。如果不阻塞,当poll检查没有数据时,会立即返回错误的掩码。

至于进程或者线程阻塞不是发生在驱动,而是发生在vfs层,因此在驱动的poll函数调用的poll_wait函数是没有阻塞进程的,只是将其加入一个队列而已。

对于在select是这样操作的,那么read函数是怎么操作的,如果没有数据可读,那么read函数也是分为阻塞和非阻塞的情况。

在阻塞的情况下,会使用 wait_event_interruptible函数来等待条件发生。当条件发生了,驱动内的read函数会继续执行。把数据从内核层copy到用户层。
非阻塞会立即返回错误。 

而对于write要简单一些,上边写什么,下面就怎么发生数据。

而在文件被读取的时候还有这么几个函数
FD_ZERO(&rds);
FD_SET(fd, &rds);
FD_ISSET(fd, &rds);

在调用select()函数后,用FD_ISSET来检测fd在fdset集合中的状态是否变化
返回整型,当检测到fd状态发生变化时返回真,否则,返回假(0)。



该文章转载的,原出处http://blog.csdn.net/hanchaoman/article/details/7444337
阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。非阻塞操作的进程在不能进行设备操作时,并不挂起。被挂起的进程进入sleep状态,被从调度器的运行队列移走,直到等待的条件被满足。

  在Linux点击放大此图片

  关于上述例程,我们补充说一点,如果将驱动程序中的read函数改为:

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
 //获取信号量:可能阻塞
 if (down_interruptible(&sem))
 {
  return - ERESTARTSYS;
 }

 //等待数据可获得:可能阻塞
 if (wait_event_interruptible(outq, flag != 0))
 {
  return - ERESTARTSYS;
 }
 flag = 0;

 //临界资源访问
 if (copy_to_user(buf, &global_var, sizeof(int)))
 {
  up(&sem);
  return - EFAULT;
 }

 //释放信号量
 up(&sem);

 return sizeof(int);
}


  即交换wait_event_interruptible(outq, flag != 0)和down_interruptible(&sem)的顺序,这个驱动程序将变得不可运行。实际上,当两个可能要阻塞的事件同时出现时,即两个wait_event或down摆在一起的时候,将变得非常危险,死锁的可能性很大,这个时候我们要特别留意它们的出现顺序。当然,我们应该尽可能地避免这种情况的发生!

  +还有一个与设备阻塞与非阻塞访问息息相关的论题,即select和poll,select和poll的本质一样,前者在BSD Unix中引入,后者在System V中引入。poll和select用于查询设备的状态,以便用户程序获知是否能对设备进行非阻塞的访问,它们都需要设备驱动程序中的poll函数支持。

  驱动程序中poll函数中最主要用到的一个API是poll_wait,其原型如下:

void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);

  poll_wait函数所做的工作是把当前进程添加到wait参数指定的等待列表(poll_table)中。下面我们给globalvar的驱动添加一个poll函数:

static unsigned int globalvar_poll(struct file *filp, poll_table *wait)
{
 unsigned int mask = 0;

 poll_wait(filp, &outq, wait);

 //数据是否可获得? 
 if (flag != 0)
 {
  mask |= POLLIN | POLLRDNORM; //标示数据可获得
 }
 return mask;
}

  需要说明的是,poll_wait函数并不阻塞,程序中poll_wait(filp, &outq, wait)这句话的意思并不是说一直等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的。select/poll会在一个循环中对每个需要监听的设备调用它们自己的poll支持函数以使得当前进程被加入各个设备的等待列表。若当前没有任何被监听的设备就绪,则内核进行调度(调用schedule)让出cpu进入阻塞状态,schedule返回时将再次循环检测是否有操作可以进行,如此反复;否则,若有任意一个设备就绪,select/poll都立即返回。

  我们编写一个用户态应用程序来测试改写后的驱动。程序中要用到BSD Unix中引入的select函数,其原型为:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1。timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。struct timeval数据结构为: 

struct timeval 

  int tv_sec; /* seconds */ 
  int tv_usec; /* microseconds */ 
};

  除此之外,我们还将使用下列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)――判断文件描述符是否被置位。

  下面的用户态测试程序等待/dev/globalvar可读,但是设置了5秒的等待超时,若超过5秒仍然没有数据可读,则输出"No data within 5 seconds":

#include
#include
#include
#include
#include
#include
#include

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

 fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
 if (fd != - 1)
 {
  while (1)
  {
   //查看globalvar是否有输入
   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 globalvar is %d\n", num);

    //输入为0,退出
    if (num == 0)
    {
     close(fd);
     break;
    }
   }
   else
    printf("No data within 5 seconds.\n");
  }
 }
 else
 {
  printf("device open failure\n");
 }
}

  开两个终端,分别运行程序:一个对globalvar进行写,一个用上述程序对globalvar进行读。当我们在写终端给globalvar输入一个值后,读终端立即就能输出该值,当我们连续5秒没有输入时,"No data within 5 seconds"在读终端被输出,如下图:

点击放大此图片
阅读(1321) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~