I/O复用

SELECT

1.函数原型

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
函数功能

当readfds或writefds中映象的文件可读或可写或超时,本次select()
就结束返回。程序员利用一组系统提供的宏在select()结束时便可判
断哪一文件可读或可写。
宏:
FD_ZERO(fd_set *fds); 清空fds与所有文件句柄的联系
FD_ISSET(int fd, fd_set &read_fds); 检查fds联系的文件句柄fd是否
可读写,当>0表示可读写。
FD_SET(int fd, fd_set &); 建立文件句柄fd与fds的联系。

参数解释
  • nfd:指定被监听文件描述符总数
  • readfds,writefds,exceptfds:可读可写和异常等事件对应的文件描述符集合。
  • timeout:设置select函数的超时时间。
  • 主要的宏:
    FD_ZERO(fd_set *fds); 清空fds与所有文件句柄的联系
    FD_ISSET(int fd, fd_set &read_fds); 检查fds联系的文件句柄fd是否
    可读写,当>0表示可读写。
    FD_SET(int fd, fd_set &); 建立文件句柄fd与fds的联系。
使用实例
    char buf[1024];
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO( &read_fds );    
    FD_ZERO( &exception );

    while(1) 
    {
        memset(buf, '\0', sizeof(buf));

        FD_SET(connfd, &read_fds);     //将connfd加入read_fds集合
        FD_SET(connfd, &exception_fds);

        ret = select(connfd+1, &read_fds, NULL, &exception_fd, NULL);
        assert(ret != -1);

        if(FD_ISSET(connfd, &read_fds)) 
        {
            ret = recv(connfd, buf, sizeof(buf)-1, 0);
            if(ret < 0) 
            {
                break;
            }
            printf("get %d bytes of normal data:%s\n",ret ,buf);
        }
        else if(FD_ISSET(connfd, &exception)) 
        {
            ret = recv(connfd, buf, sizeof(buf)-1, MSG_OOB);
            if(ret <= 0) 
            {
                break;
            }
            printf("get %d bytes of obb data: %s\n", ret, buf);
        }
    }
    close(connfd);
    close(sock);

POLL & EPOLL

函数原型
* int poll(struct pollfd *fds, nfds_t nfds,  int timeout);
* int epoll_create(int size);
* int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
* int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
函数功能

poll 函数与select 类似,将保存文件描述符上发生的事件的数组轮询,以测试是否有就绪者。
epoll_create 函数创建一个文件描述符,唯一的标识内核中的一个可读事件表,把用户关心的文件描述符都写进去。

epoll_wait 函数与其他不同,将所有就绪的事件从内核事件表(epfd)中复制到第二个参数指定的数组(events)中。

区别:
  • 调用poll时返回就绪事件的个数,只用来判断是否有事件发生,如果有,再去数组中逐一通过POLLIN判断是否是发生事件的文件描述符。
  • 调用epoll_wait时不仅返回就绪事件个数,同时将就绪事件统统复制到一个数组中。然后通过返回值和数组来得到文件描述符。

ET模式和LT模式

概念

LT:电平触发模式,当epoll_wait检测到其上有事件发生时不立即处理该事件,当应用程序下次调用epoll_wait时再次向应用程序通知该事件。
ET:检测到事件发生时立即处理,后面不提醒。
代码实现中,LT模式当传输字节超过缓存区大小时,多次触发多次读取。
ET模式当传输字节超过时,触发一次,使用while循环多次读取。

EPOLLONESHOT事件

有时候可能出现这种情况,一个线程处理读取socket上的数据后开始处理这些数据,在数据处理过程重socket又可读,此时为了避免另一个线程处理此socket,就需要对该socket注册EPOLLONESHOT事件。此时,操作系统作多触发其上注册的一个可读,可写,可执行事件。但在处理完后,必须重置EPOLLONESHOT事件保证下一次正常触发。

将套接字设置为非阻塞
int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETEL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETEL, new_option );
    return old_option;
}

EPOLL工作原理:
epoll只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,一旦某个文件描述符就绪时,内核会采用回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
然后select()和poll()函数都是采用不断轮询所有文件描述符的方式判断有没有事件发生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值