为什么_exit(0)(由syscall退出)阻止我收到任何标准内容?

时间:2012-03-18 00:34:15

标签: linux assembly interrupt system-calls gas

我有一个Linux x86-32 GAS汇编程序,终止如下:

movl $1, %eax
movl $0, %ebx # argument for _exit
int $0x80

当我像这样退出时,程序正常运行,但如果我尝试读取stdout输出,我什么也得不到(使用ie less或wc)。

我尝试编译一个最小的C程序并比较strace输出。我发现的唯一区别是,GCC使得C程序(int main() { printf("donkey\n"); })在strace输出中隐含地退出exit_group(0)

我尝试修改我的ASM程序以退出call exit而不是原始系统调用。 stdout现在可以正常阅读。

测试用例

.data
douout: .string "monkey\n"
.text
.globl main

main:

pushl $douout
call printf
# Exit
movl $1, %eax
movl $0, %ebx
int $0x80

编译并运行:

$ yasm -g dwarf2 -f elf -p gas t.asm && gcc -g -melf_i386 -o t t.o && ./t | wc -c
0

预期:

7

编辑:

我尝试同时调用tcflushfflush,我仍然遇到问题。使用fflush我甚至会遇到段错误。

0xb7e9e7c9 in _IO_fflush (fp=0x804a018) at iofflush.c:42
42  iofflush.c: No such file or directory.
    in iofflush.c
(gdb) bt
#0  0xb7e9e7c9 in _IO_fflush (fp=0x804a018) at iofflush.c:42
#1  0x08048434 in main () at t.asm:12
(gdb) frame 1
#1  0x08048434 in main () at t.asm:12
12  call fflush
(gdb) list
7   
8   pushl $douout
9   call printf
10  # Exit
11  movl $0, %eax
12  call fflush
13  movl $1, %eax
14  movl $0, %ebx
15  int $0x80

EDIT2:

好的,它现在适用于所有人。我使用了从这里复制的错误的调用约定:Printf without newline in assembly

fflush的参数应该像往常一样在堆栈中。

$ cat t.asm 
.data
douout: .string "monkey\n"
.text
.globl main

main:

pushl $douout
call printf
# Exit
pushl $0
call fflush
movl $1, %eax
movl $0, %ebx
int $0x80
$ yasm -g dwarf2 -f elf -p gas t.asm && gcc -g -melf_i386 -o t t.o && ./t | wc -c
7
$

谢谢大家,特别是没有。

3 个答案:

答案 0 :(得分:3)

当你将stdout传递给wc时,stdout变得完全缓冲。

_exit立即终止进程,不运行atexit()和其他清理处理程序。运行时将注册这些处理程序,以便在退出时运行,以刷新打开的FILE *,例如stdout。当这些处理程序在退出时没有执行时,缓冲的数据将会丢失。

如果在printf调用之后调用fflush(stdout),或者如果你只是在一个consol中运行程序而没有将输出传递给另一个程序,你应该看到输出 - 在这种情况下,stdout通常是行缓冲的,所以stdout每当你写一个\ n

时都会刷新

答案 1 :(得分:2)

发送到标准输出的输出通常是缓冲的。如果您在致电fflush(stdout)之前致电_exit,则应获得输出。

exit工作的原因是因为在调用_exit本身实际终止程序之前,该函数保证关闭并刷新任何打开的流(例如stdout)。

答案 2 :(得分:1)

根据_exit(2)

的手册页
  

是否刷新标准I / O缓冲区并删除临时文件   使用tmpfile(3)创建的是依赖于实现的。在另一   手,_exit()确实关闭打开的文件描述符,这可能会导致   未知延迟,等待待处理输出完成。如果延迟是   不希望的,之前调用tcflush(3)之类的函数可能很有用   调用_exit()。是否有任何挂起的I / O被取消,以及哪些   挂起的I / O可能会在_exit()时被取消,与实现有关。

因此除非你刷新了stdout,否则它可能会被丢弃。