打印

select() 与 poll()两个函数接口的作用

[复制链接]
147|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
你画我瞎|  楼主 | 2018-9-29 08:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
select()函数的作用

   系统调用select和poll的后端实现,用这两个系统调用来查询设备是否可读写,或是否处于某种状态。如果poll为空,则驱动设备会被认为即可读又可写,返回值是一个状态掩码

如何使用select()函数?

   select()函数的接口主要是建立在一种叫'fd_set'类型的基础上。它('fd_set')是一组文件描述符(fd)的集合。由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量:

    fd_setset;

   FD_ZERO(&set);     

    FD_SET(fd,&set);  

    FD_CLR(fd,&set);  

    FD_ISSET(fd,&set);

   

在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。*这个值是系统相关的*,同时检查你的系统中的select()的man手册。有一些系统对多于1024个文件描述符的支持有问题。[译者注:Linux就是这样的系统!你会发现sizeof(fd_set)的结果是128(*8 =FD_SETSIZE=1024) 尽管很少你会遇到这种情况。]

select的基本接口十分简单:

    intselect(int nfds, fd_set *readset, fd_set *writeset,

              fd_set *exceptset, struct timeval *timeout);

其中:



nfds   

    需要检查的文件描述符个数,数值应该比是三组fd_set中最大数

    更大,而不是实际文件描述符的总数。

readset  

    用来检查可读性的一组文件描述符。

writeset

    用来检查可写性的一组文件描述符。

exceptset

    用来检查意外状态的文件描述符。(注:错误并不是意外状态)

timeout

    NULL指针代表无限等待,否则是指向timeval结构的指针,代表最

    长等待时间。(如果其中tv_sec和tv_usec都等于0, 则文件描述符

    的状态不被影响,但函数并不挂起)

   

函数将返回响应操作的对应操作文件描述符的总数,且三组数据均在恰当位置被修改,只有响应操作的那一些没有修改。接着应该用FD_ISSET宏来查找返回的文件描述符组。

这里是一个简单的测试单个文件描述符可读性的例子:

    int isready(int fd)

    {

        int rc;

        fd_set fds;

        struct timeval tv;

  

        FD_ZERO(&fds);

        FD_SET(fd,&fds);

       //tv.tv_sec = tv.tv_usec = 0;

  

       //rc = select(fd+1, &fds, NULL, NULL,&tv);

        rc = select(fd+1, &fds, NULL, NULL, NULL);

        if (rc < 0)

          return -1;

  

        return FD_ISSET(fd,&fds) ? 1 : 0;

     }

   

当然如果我们把NULL指针作为fd_set传入的话,这就表示我们对这种操作的发生不感兴趣,但select()还是会等待直到其发生或者超过等待时间。

[译者注:在Linux中,timeout指的是程序在非sleep状态中度过的时间,而不是实际上过去的时间,这就会引起和非Linux平台移植上的时间不等问题。移植问题还包括在SystemV风格中select()在函数退出前会把timeout设为未定义的 NULL状态,而在BSD中则不是这样,Linux在这点上遵从System V,因此在重复利用timeout指针问题上也应该注意]



Linux

下select调用的过程:

1.用户层应用程序调用select(),底层调用poll())

2.核心层调用sys_select() ------> do_select()

  最终调用文件描述符fd对应的struct file类型变量的structfile_operations *f_op的poll函数。

  poll指向的函数返回当前可否读写的信息。

  1)如果当前可读写,返回读写信息。

2)如果当前不可读写,则阻塞进程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。

3.驱动需要实现poll函数。

  当驱动发现有数据可以读写时,通知核心层,核心层重新调用poll指向的函数查询信息。

poll_wait(filp,&wait_q,wait)   // 此处将当前进程加入到等待队列中,但并不阻塞

在中断中使用wake_up_interruptible(&wait_q)唤醒等待队列





阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后在进行操作。非阻塞操作的进程在不能进行设备操作时并不挂起,它或者被放弃,或者不停的查询,直到可以进行操作为止



唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断



在用户程序中,select()和poll()也是与设备阻塞与非阻塞访问息息相关的,使用非阻塞I/O的应用程序通常会使用select和poll系统调用查询是否可对设备进行无阻塞的访问。select和poll系统调用最终会引发设备驱动中的poll函数被执行



Poll函数原型:

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

第一个参数为file结构体指针,第二个参数为轮询表指针。这个函数应该进行以下两项工作

1、对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应等待队列添加到         poll_table         

2、返回表示是否能对设备进行无阻塞、写访问的掩码

Poll_wait()函数不会引起阻塞。它所做的工作就是把当前进程添加到wait参数指定的等待列表(poll_table)中



驱动程序poll函数应该返回设备资源的可获取状态



FD_ZERO(fd_set*fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。

FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。

FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。

FD_ISSET(int fd,fd_set *fdset);判断文件描述符是否被置位



Linux下select调用的过程:

1、用户层应用程序调用select(),底层调用poll())

2、核心层调用sys_select() ------> do_select()

最终调用文件描述符fd对应的struct file类型变量的struct file_operations*f_op的poll函数。

poll指向的函数返回当前可否读写的信息。

1)如果当前可读写,返回读写信息。

2)如果当前不可读写,则阻塞进程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

395

主题

395

帖子

0

粉丝