strace系统调用顺序与预期不符的原因排查
问题:printf的write系统调用为何晚于open执行?
根据反汇编结果,我原本预期运行测试代码后,strace输出里的write调用会在open之前,但实际显示open先于write执行。想请教是puts以某种方式异步执行write,还是strace对输出进行了重排?
测试代码
#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main() { close(1); printf("Interesting ordering\n"); int fd = open("temp.txt", O_RDONLY); int fd1 = open("temp.txt", O_RDONLY); }
Strace片段
munmap(0x7fe46ef45000, 115047) = 0 close(1) = 0 newfstatat(1, "", 0x7fffeea1b980, AT_EMPTY_PATH) = -1 EBADF (Bad file descriptor) getrandom("\x96\xa6\x6b\xd0\x3c\xde\x09\x28", 8, GRND_NONBLOCK) = 8 brk(NULL) = 0xb18000 brk(0xb39000) = 0xb39000 openat(AT_FDCWD, "temp.txt", O_RDONLY) = 1 openat(AT_FDCWD, "temp.txt", O_RDONLY) = 3 write(1, "Interesting ordering\n", 21) = -1 EBADF (Bad file descriptor) exit_group(0) = ?
main函数反汇编
0000000000401146 <main>: 401146: 55 push %rbp 401147: 48 89 e5 mov %rsp,%rbp 40114a: 48 83 ec 10 sub $0x10,%rsp 40114e: bf 01 00 00 00 mov $0x1,%edi 401153: e8 e8 fe ff ff call 401040 <close@plt> 401158: bf 10 20 40 00 mov $0x402010,%edi 40115d: e8 ce fe ff ff call 401030 <puts@plt> 401162: be 00 00 00 00 mov $0x0,%esi 401167: bf 25 20 40 00 mov $0x402025,%edi 40116c: b8 00 00 00 00 mov $0x0,%eax 401171: e8 da fe ff ff call 401050 <open@plt> 401176: 89 45 fc mov %eax,-0x4(%rbp) 401179: be 00 00 00 00 mov $0x0,%esi 40117e: bf 25 20 40 00 mov $0x402025,%edi 401183: b8 00 00 00 00 mov $0x0,%eax 401188: e8 c3 fe ff ff call 401050 <open@plt> 40118d: 89 45 f8 mov %eax,-0x8(%rbp) 401190: b8 00 00 00 00 mov $0x0,%eax 401195: c9 leave 401196: c3 ret
解答
既不是puts异步执行write,也不是strace重排了输出,核心原因是C标准库的stdio缓冲区机制:
- 调用
close(1)关闭stdout对应的文件描述符后,执行puts时,C标准库首先会通过newfstatat系统调用检查该文件描述符的状态(从strace输出能看到这个调用返回了EBADF错误)。此时标准库已发现文件描述符无效,但不会立即触发write系统调用,而是将输出内容暂存到stdio的用户态缓冲区中。 - 直到程序执行即将退出时,C标准库会自动触发所有打开的
FILE结构体的缓冲区刷新操作,这时才会调用write系统调用尝试写入数据。而此时两次open调用已经执行完毕,所以strace里会看到write出现在open之后。
验证方法:如果在puts之后手动调用fflush(stdout)强制刷新缓冲区,你会看到write调用会立即出现在open之前,因为缓冲区被提前强制刷新了。
内容的提问来源于stack exchange,提问作者user3882729
相关产品推荐
相关产品推荐

