ARM64 Linux下不同大小struct timeval调用select为何均正常工作?
为什么ARM64 Linux上的32位ARM select()能正常休眠10秒?
测试场景与问题
你编写了如下测试代码:
#include <sys/select.h> #include <stdio.h> int main() { struct timeval t = {10, 999999}; printf("sizeof timeval is %d, sizeof(tv_sec) is %d, sizeof(tv_usec) is %d\nstart select()\n", (int) sizeof(struct timeval), (int)sizeof(t.tv_sec), (int)sizeof(t.tv_usec)); select(0,0,0,0, &t); printf("select() ends\n"); }
分别用ARMv7(32位)和ARMv8(64位)工具链编译出select32和select64,在ARM64 Linux上运行后,发现两个程序都正常休眠了约10秒。你疑惑:32位程序传递的struct timeval与64位内核期望的结构大小不同,为什么还能正确工作?
答案是:ARM64 Linux的内核兼容层+32位C库的适配,共同完成了32位与64位数据结构的转换,让32位应用的系统调用能被64位内核正确解析。下面具体拆解:
1. ARM64内核的AArch32兼容层是核心
ARM64架构原生支持AArch32执行状态——简单说就是64位内核可以直接运行32位ARM程序。针对系统调用,内核内置了一套专门的兼容处理逻辑:
- 当32位应用调用
select()时,它传递的是32位版本的struct timeval(8字节,tv_sec和tv_usec都是4字节)。 - 内核的兼容层会自动识别这是32位系统调用请求,将32位的
struct timeval转换为内核内部使用的64位版本(16字节,两个字段均为8字节):把32位的时间数值做零扩展(因为时间值都是非负整数),转换成64位整数。这样内核就能正确识别你设置的10秒+999999微秒的超时时间,自然会让程序休眠对应的时长。
2. 32位C库的封装辅助
你的32位程序使用的是ARMv7版本的C库(比如libc-armhf),它在封装select()函数时,已经针对64位内核的兼容场景做了适配:
- 它会调用对应ARM64兼容的32位系统调用号,而非直接使用64位系统调用接口,确保内核能正确识别并触发参数转换逻辑。
- 对于一些涉及时间溢出的场景,现代32位C库还会自动切换到
time64系列系统调用(比如select_time64),但在这个案例中,基础的参数转换已经足够让select()正常工作。
额外说明:兼容性的普遍性
这种参数转换并非select()独有,ARM64内核的兼容层对绝大多数系统调用都做了类似处理——比如涉及文件描述符、结构体大小差异的场景,都会自动完成32位到64位的适配,保证32位应用无需修改就能在64位ARM系统上平稳运行。
内容的提问来源于stack exchange,提问作者xiaokaoy




