Unix中I/O之I/O复用——select

参看——UNIX环境高级编程中p606I/O多路转接

Unix中的函数select和poll用来,支持Unix中I/O复用的功能,在Unix中I/O模型可以分为以一几种:

(1)阻塞I/O

(2)非阻塞I/O

(3)I/O复用(select和poll)

(4)信号驱动I/O(SIGIO)

(5)异步I/O

现在比较流行的I/O模型是阻塞I/O模型.阻塞I/O是当应用程序和内核交换数据时,由于内核还没有准备好数据,那么应用程序必须进行阻塞,不能继续执行,直到内核的数据准备好!应用程序取到数据返回后,阻塞过程结束!但返回的结果也并不一定是正确的!这里只是举一个简单的例子!也许情况会更加的 复杂!

      非阻塞I/O,例如在和内核交换数据时,如果内核的数据没有准备好,那么应用程序不会一真等待,会有一个返回信息,以判断是那里出了问题!这样有助于确认是在那个阶段出了问题!

这里我们介绍另一种,I/O复用之select

select的API介绍

  1. #include <sys/types.h>

  2. #include<sys/time.h>

  3. int select (int maxfdp1,fd_set *readset,fd_set * writeset,fd_set excpetset,const struct timeval *timeout);

select 是休眠检测,由内核唤醒占用CPU内存少,能够同时监视多个文件描述符(可读,可写,异常文件)的变法, 也支持超时返回。当返回值大于0时,表示说明所检测的文件至少有一个变动了。

参数maxfdp1:最大文件符+1
参数fd_set *readset:检测的可读文件
参数fd_set excpetset:检测的异常文件
参数 timeval *timeout:设置超时返回

timeval结构体定义如下:

struct timeval
{      
    long tv_sec;   /*秒 */
    long tv_usec;  /*微秒 */   
};

返回值:超时返回0;失败返回-1;成功返回大于0的整数,这个整数表示就绪描述符的数目。

select使用范例:
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:

系统提供了4个宏对描述符集进行操作:

fd_set set;

FD_ZERO(&set); /*将set清零使集合中不含任何fd*/

FD_SET(fd, &set); /*将fd加入set集合*/

FD_CLR(fd, &set); /*将fd从set集合中清除*/

FD_ISSET(fd, &set); /*在调用select()函数后,用FD_ISSET来检测fd是否在set集合(可读,可写,异常)中,当检测到fd在set中则返回真,否则,返回假(0)*/

操作如下:

fd_set rset;   
int fd;   
FD_ZERO(&rset);   
FD_SET(fd, &rset);   
FD_SET(stdin, &rset);

然后调用select函数,拥塞等待文件描述符事件的到来;如果超过设定的时间,则不再等待,继续往下执行。

select(fd+1, &rset, NULL, NULL,NULL);

select返回后,用FD_ISSET测试给定位是哪个参数(可读,可写,异常文件)被置位:

if(FD_ISSET(fd, &rset)   
{ 
    ... 
    //do something  
}

下面是一个最简单的select的使用例子:

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

int main()
{
    fd_set rd;
    struct timeval tv;
    int err;
    

    FD_ZERO(&rd);
    FD_SET(0,&rd);
    
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    err = select(1,&rd,NULL,NULL,&tv);
    
    if(err == 0) //超时
    {
        printf("select time out!\n");
    }
    else if(err == -1)  //失败
    {
        printf("fail to select!\n");
    }
    else  //成功
    {
        printf("data is available!\n");
    }

    
    return 0;
}

//FD_SET(stdin, &rset);文件描述符0、1和2分别代表stdin、stdout和stderr。

其中FD_SET(0,&rd);是标准输入

我们运行该程序并且随便输入一些数据,程序就提示收到数据了。

select来解决socket中的多客户问题

使用select的优势还有用户可以在一个线程内同时处理多个socket的IO请求。在网络编程中,当涉及到多客户访问服务器的情况,我们首先想到的办法就是fork出多个进程来处理每个客户连接。现在,我们同样可以使用select来处理多客户问题,而不用fork。

猜你喜欢

转载自blog.csdn.net/weixin_40535588/article/details/89354999
今日推荐