我是靠谱客的博主 难过小天鹅,最近开发中收集的这篇文章主要介绍从select的一个死循环谈epoll的ET模式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

最近写程序遇到一个问题,就是发现select监听标准输出的时候遇到了死循环,具体程序如下程序一。程序的意图是每当用户在控制台有任何输入,就输出”hello world!”。

程序一:

#include 

#include 

#include 

#include 


int 

main(int argc, char *argv[])

{

    int maxfdp1;

    char buf[256];

    fd_set rset;


    maxfdp1=STDIN_FILENO+1;

    for(;;)

    { 

        FD_ZERO(&rset);

        FD_SET(STDIN_FILENO,&rset);

        select(maxfdp1,&rset,NULL,NULL,NULL);


        if(FD_ISSET(STDIN_FILENO,&rset))

        {

           printf("hello world!n");

        }

    }

    return 0;

}

运行结果:

结果会循环输出“hello world!”,这是为什么呢?也就是当我们输入任意字符后,select每次都判断标准输入的描述符就绪。造成这种情况的原因要从select的机制说起。如下图所示:

没一个文件描述符(fd)与一个缓冲关联,select对fd的监听其实就是监听fd的缓冲,当缓冲中有数据要读的时候,select就认为该fd可读就绪,当缓冲中有数据待写的时候,select就认为该fd可写就绪。

下面我们在分析一下我们的程序一,当输入任意字符,比如:“abc”,则“abc”被放在标准输入的缓冲当中,此时缓冲中有数据(abc)待读,所以select返回STDIN_FILENO就绪,程序输出“hello World!”。紧接着进入下一次循环,select重新将STDIN_FILENO加入监听的描述符集,由于刚刚的“abc”并没有被读出,所以仍在缓冲中,此时STDIN_FILENO的缓冲中仍有数据等待读,所以select又返回STDIN_FILENO可读就绪,又一次输出“hello world!”。

之后循环情况类似,由于缓冲的的“待读”数据始终还在,所以每次select都直接返回STDIN_FILENO就绪,每次都输出“hello world!”,这就是造成程序死循环的原因。那么让偶们如何解决呢?

     方法一:将缓冲区中的“待读”数据读出,程序修改如下所示:

        if(FD_ISSET(STDIN_FILENO,&rset))

    {

read(STDIN_FILENO,buf,sizeof(buf));//将缓冲区的数据读出(读入buf数组)

printf("hello world!n");

    }

修改后程序正常运行(任意输入后,输出“hello world!”)。我们在做如下实验——输入多个字符,制度出部分字符。修改程序做如下修改:

                char buf[2];//将buf长度改为2,每次从缓冲区读入两个字符

程序运行结果如下:

分析:

(1) 输入一个字符’s’,s被放入缓冲区,同时放入缓冲区中的还有换行符’n’,缓冲区中有待读数据,select返回读就绪,read将’sn’读出,缓冲区清空,select再次阻塞。

(2) 输入两个”dd”,缓冲区中的数据变为”ddn”,select返回读就绪,read读出两个字符——”ss”,输出hello world!此时缓冲区中还有’n’,所以下一次select依然返回读就绪,之后read将’n’读出,输出hello world!,缓冲区清空,select阻塞。

(3) 之后输入三个字符,四个字符的情况类似,不在分析。

 

到此,我们将程序一基本分析清楚,但我们的讨论远没有结束。因为,这个程序的现象令我想起了另一个知识点——epoll的LT和ET模式。

关于ET和LT模式的介绍,之前的博文已经写得很详细了,这里不再重复。这里想说的是,以上程序一的现象正式LT模式的一个典型实例,也是LT模式的一个缺陷。我们知道select和poll都是采用LT模式,并且只有这一种模式。所以,使用select或者poll要想解决程序一的问题只能采用方法一。下面我们采用epoll的ET模式解决,也就是方法二。代码如下。

程序二:

#include 

#include 

#include 

using namespace std;


int main(void)

{

int epfd,nfds;

struct epoll_event ev,events[5];//ev用于注册事件,数组用于返回要处理的事件

    epfd=epoll_create(1);//只需要监听一个描述符——标准输入

ev.data.fd=STDIN_FILENO;

ev.events=EPOLLIN|EPOLLET;//监听读状态同时设置ET模式

epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);//注册epoll事件

for(;;)

{

nfds=epoll_wait(epfd,events,5,-1);

for(int i=0;i

{

if(events[i].data.fd==STDIN_FILENO)

               cout<<"hello world!"<

}

}

}

运行结果:

可以发现,使用ET模式,程序正常运行,虽然输入缓冲区的数据并没有被读出,但是只要没有新的数据进入,epoll就不再被通知(只被通知一次),当再次输入数据,又有新的数据进入缓冲时才会触发epoll,再次返回读就绪,输出”hello world!”。

我们再看看使用LT模式的情况,将程序二以下修改:

    ev.events=EPOLLIN;//默认使用LT模式

运行结果:

可以发现和select的结果一样。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

最后

以上就是难过小天鹅为你收集整理的从select的一个死循环谈epoll的ET模式的全部内容,希望文章能够帮你解决从select的一个死循环谈epoll的ET模式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部