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
相关产品推荐
相关产品推荐

