首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否有一种非阻塞方法比select()和poll()更快地检查数据?

是否有一种非阻塞方法比select()和poll()更快地检查数据?
EN

Stack Overflow用户
提问于 2014-05-28 22:21:44
回答 5查看 2.8K关注 0票数 8

我有一个C程序尽可能快地发送数据,它使用sendto()方法从发送方发送到接收方,接收端使用recvfrom()方法接收数据。数据被封装到第二层以太网帧中,应用程序将以太网帧直接写到线路上(没有TCP、UDP甚至IP)。这是在x86_64 Linux上的(开发机器只是股票Ubuntu14.04)。我不打算移植到任何其他操作系统,应用程序设计范围是为Linux设计的,所以其他OSes并不重要。

发件人:

代码语言:javascript
复制
    while (true)
    {
        sendResult = sendto(sockFD, txBuffer, fSize+headersLength, 0,
            (struct sockaddr*)&socket_address, sizeof socket_address);
    }

接收机:

代码语言:javascript
复制
    while (true)
    {
        recvfrom(sockFD, rxBuffer, fSizeTotal, 0, NULL, NULL);
    }

我希望发送方能够检查接收到的数据包;如果接收方应用程序退出,它会将数据发送回发送方,说“我退出”,这样发送方将停止发送数据。我在发送方上使用了轮询()来检查接收到的消息,如下所示,但这大大降低了传输速度,从大约1 1Gbps (968 10Mbps )降低到大约10 10Mbps。我正在测试两台带有1Gbps网卡的PC机之间的交叉电缆。发送者统计发送的帧和帧的大小,接收者统计接收到的帧和帧的大小,所以要确认的是,应用程序实际上是以足够近的线速率接收到的,我不只是看NIC的使用情况或类似的。

Poll()方法:

代码语言:javascript
复制
int rv;
struct pollfd ufds[1];
ufds[0].fd = sockFD;
ufds[0].events = POLLIN


while (true)
{
    sendResult = sendto(sockFD, txBuffer, fSize+headersLength, 0, (struct sockaddr*)&socket_address, sizeof(socket_address));

    // wait for events on the sockets, 1ms timeout
    rv = poll(ufds, 1, 1);

    if (rv > 0) {
        if (ufds[0].revents & POLLIN) {
            recvfrom(sockFD, rxBuffer, fSizeTotal, 0, NULL, NULL);
        }
    }

}

1毫秒是可为轮询()方法设置的最低超时。这就是为什么我的传输程序只能以10 10Mbps传输。应用程序可以轻松地使1 1Gbps链路饱和--尽管CPU使用率很低,但正如我前面所说的那样,我得到了968 1Gbps(顺便说一下,我并不是指峰值,即持续吞吐量)。

我删除了轮询()调用,并使用下面的示例切换到select(),但是,在这里使用最小的延迟,我的传输应用程序只能获得175 my。不接近最初的968 close;

Select()方法:

代码语言:javascript
复制
fd_set readfds;
struct timeval tv;
int rv, n;

FD_ZERO(&readfds);
FD_SET(sockFD, &readfds);
n = sockFD + 1;


while (true)
{
    sendResult = sendto(sockFD, txBuffer, fSize+headersLength, 0, (struct sockaddr*)&socket_address, sizeof(socket_address));

    tv.tv_sec = 0;
    tv.tv_usec = 000001;
    rv = select(n, &readfds, NULL, NULL, &tv);
    if (rv > 0) {
        if (FD_ISSET(sockFD, &readfds)) {
            recvfrom(sockFD, rxBuffer, fSizeTotal, 0, NULL, NULL);
    }

}

对于今天的系统来说,这两种方法似乎都太慢了(我的cpu使用率在以上所有测试中都在2%左右)。我希望将这个应用程序移到一些10GigE机器上,并在那里开始测试,但我显然不能使用这两种方法中的任何一种。没有更快的方法可以查到吗?

我认为这些应该是非阻塞的,但是通过要求超时,它们在某种程度上是阻塞的;我已经看到了这条线,但这不是我需要的答案。没有任何方法,我可以简单地调用检查,在那个时候,它被调用,对于等待被读取的数据,然后如果没有等待被读取的数据立即返回吗?

作为一个边节点,我还没有阅读recvfrom方法()来查看延迟在哪里,然后才发布,但我确实尝试了以下操作,因为更改代码只需30秒,结果是最糟糕的结果不到1 1Mbps;

发件人:

代码语言:javascript
复制
    while (true)
    {
        // Continually send a frame then check for a frame, send a frame then check for a frame...
        sendResult = sendto(sockFD, txBuffer, fSize+headersLength, 0, (struct sockaddr*)&socket_address, sizeof(socket_address));
        recvfrom(sockFD, rxBuffer, fSizeTotal, 0, NULL, NULL);
    }
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-06-04 16:48:15

不需要pollselect阻塞任何一段时间。如果pollselectselect参数设置为零,则两个调用都会立即返回,并显示i/o可用性。这样就消除了计时器和随后的四舍五入。

注意,如果您只监视一个文件描述符,为什么这比简单的非阻塞轮询读取要快得多呢?当多个FDs发挥作用时,我本以为这种方法会带来任何好处,所以您的测试发现了这一点是很有趣的。

票数 2
EN

Stack Overflow用户

发布于 2014-05-28 23:18:13

我根本不会使用非阻塞模式。只需在阻塞模式下指定一个线程即可。这样,您只需要执行一个系统调用:recvfrom(),所以您要将上下文切换到内核中。

票数 8
EN

Stack Overflow用户

发布于 2014-05-29 02:15:33

正如您所想的,您的性能受到影响的原因是您将自己限制在每秒发送不超过1000个数据包。

如果您愿意使用两个线程,那么EJP的回答是最好的选择。如果您真的只想使用单个线程,最好的选择是使用select()poll()来让您知道在传输队列饱和时是否有需要做的事情。因此,您可以将套接字设置为非阻塞模式,也可以在执行I/O时使用MSG_DONTWAIT标志。收到EAGAIN/EWOULDBLOCK通知时停止执行该I/O操作,然后在select()poll()中再次执行阻塞等待适当的事件(不要设置超时)。在简化错误处理的伪码中:

代码语言:javascript
复制
writeable = true;
readable = false;
make_nonblock(s);
for (;;) {
    if (readable) {
        while (recvfrom(s,...) > 0) {
            done = done_check();
        }
        if (done) break;
        assert(errno == EAGAIN);
        readable = false;
    }
    if (writeable) {
        while (sendto(s,...) > 0) {}
        assert(errno == EAGAIN);
        writeable = false;
    }
    poll_socket(s, &readable, &writeable);
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23922842

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档