You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下select()调用覆盖timeout参数值的问题咨询

select()超时参数的注意事项

核心结论

每次调用select()前必须重置timeout值,才能保证每次都使用你设定的固定超时时间。

为什么会这样?

POSIX标准明确规定,select()会修改传入的timeout参数,将其更新为实际等待后剩余的时间。比如你设置了0.85秒超时,如果0.1秒就有数据到达,timeout会被改成0秒750000微秒;哪怕是被信号中断(errno=EINTR),也会更新剩余时间。

你在Ubuntu 18.04没找到相关说明的话,可以查看man 2 select的NOTES部分,里面写着:On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do the same.

你的代码其实已经做对了——每次循环开头都重新初始化timeout,这样每次select()都会用完整的0.85秒超时,不会因为前一次的剩余时间导致等待时间越来越短。

格式化后的代码片段

void *main_udp_loop(void *arg)
{
    struct UDP_CTX *ctx = (UDP_CTX*)arg;
    
    fd_set readfds = {};

    struct sockaddr peer_addr = { 0 };

    int peer_addr_len = sizeof(peer_addr);

    while (1)
    {
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = 850000; // 等待0.85秒

        FD_ZERO(&readfds); 
        FD_SET(ctx->udp_socketfd, &readfds);
        
        int activity = select(ctx->udp_socketfd + 1, &readfds, NULL, NULL, &timeout);  

        if ((activity < 0) && (errno != EINTR))  
        {  
            printf("Select error: Exiting main thread\n");
            return NULL;
        }  

        if (timeout.tv_usec != 850000)
        {
            printf("Timeout changed: %ld %ld\n", (long)timeout.tv_sec, (long)timeout.tv_usec);
        }

        if (activity == 0) 
        {
            printf("No activity from select: %ld \n", (long)time(0));
            continue;
        }
        // 后续处理逻辑...
    }
}

额外提醒

如果不重置timeout,循环里后续的select()会沿用前一次剩下的时间,比如第一次剩0.75秒,第二次可能剩更少,直到timeout变成0,此时select()会变成非阻塞调用,完全达不到你想要的定时等待效果。

内容的提问来源于stack exchange,提问作者antonio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:45:24