select函数

异步I/O:  

使用select/poll函数可以实现异步I/O,系统并不主动告诉我们描述符的状态,我们需要通过select或poll主动的查询。信号提供了一种异步形式通知某种事件发生的方法,但是每个进程只有一个信号,如果要同时对几个描述符进行异步I/O,那么进程接收到信号时并不知道这个信号对应于哪个文件描述符


阻塞与非阻塞:

阻塞方式block,是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。使用select就可以完成非阻塞(non-block,进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,效率较高),它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。


select函数使我们可以执行I/O多路转接(多路复用)

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

  (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。

  (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。

  (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。

  (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。

  (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

  与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。


传向select函数的参数告诉内核

1 我们所关心的描述符。

2 对于每个描述符我们关心的条件(读,写,异常)

3 希望等待多长时间(永久等待,等一段时间,不等待)


select函数返回,内核告诉我们

1 已准备好的描述符数量。

2 哪个描述符已准备好读、写、或异常。

使用该返回值,就可以调用相应的I/O函数(read,write),并且明确知道该函数不会阻塞。


int select(int maxfdp1,fd_set* readfds,fd_set* writefds,fd_set *exceptfds,struct timeval* tvptr);

第1个参数maxfdp1:说明最大描述符数(经常256或1024),对大部分程序来说太大,可以设置为最高描述符加1。

第2,3,4个参数readfds,writefds,exceptfds:指向描述符集的指针,每个描述符集放在一个fd_set数据类型中。如果三个指针都是空指针,则select提供了比sleep更精确的计时器。sleep等待整数秒,select可以小于1秒

底下的宏提供了处理这三种描述词组的方式:

FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd的位

FD_ISSET(int fd,fd_set* set);用来测试描述词组set中相关fd的位是否为真

FD_SET(int fd,fd_set* set);用来设置描述词组set中相关fd的位

FD_ZERO(fd_set* set); 用来清除描述词组set的全部位


第5个参数tvptr: 说明等待的时间。可以是永久等待(将select置于阻塞状态)、等待一段时间精确到us(这段时间内阻塞),完全不等待(非阻塞)

struct timeval

{

    time_t tv_sec;//秒

    time_t tv_usec;//微秒

};


select函数有3种返回值

1 返回-1,表示出错,例如指定的描述符集都没准备好时捕捉到一个信号。

2 返回0,表示没有描述符准备好,指定的时间已经超过。

3 返回正值,表示已经准备好的描述符数,三个描述符集中仍旧打开的位是对应已准备好的描述符位。

所以除非返回正值,否则返回后检查描述符集都是没有意义的。


准备好的意思是

1 对于读集中一个描述符read,不会阻塞,则它是准备好的。

2 对于写集中一个描述符write,不会阻塞,则它是准备好的。

3 对于异常条件集中的一个描述符有一个未决的异常条件,则它是准备好的。


一个描述符阻塞与否并不影响select是否阻塞,即如果希望读一个非阻塞的描述符,并且超时值设为5秒,则select最多阻塞5s。如果设为永久等待,则select阻塞直到该描述符数据准备好或者捕捉到信号。

如果在一个描述符上碰到文件结束,则select认为该描述符时可读的。调用read返回0,这是UNIX指示文件结尾的办法。


例子:

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

#define BUFFSIZE 1000
int main ()
{
    int fd;
    int ret;
    char c[BUFFSIZE];
    fd_set readfd;
    struct timeval timeout;
    fd = open("/dev/tty",O_RDONLY | O_NONBLOCK);
    while(1)
    {
        timeout.tv_sec=3;
        timeout.tv_usec=0;
        FD_ZERO(&readfd);
        FD_SET(fd,&readfd);

        ret=select(fd+1,&readfd,NULL,NULL,&timeout);
        if(ret == -1)   
            printf("error\n");
        else if(ret){   
            if(FD_ISSET(fd,&readfd))
            {
                int n=read(fd,c,BUFFSIZE);
                write(STDOUT_FILENO,c,n);
                if (strcmp(c,"end")==0)
                    break;
            }
        }
        else    //超时
        {
            printf("time out\n");
            continue;
        }
    }
}


  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值