如何将_exit(0)(通过系统调用退出)阻止我接收任何标准输出内容?

问题描述:

我有一个Linux的x86-32 GAS的汇编程序终止这样的:如何将_exit(0)(通过系统调用退出)阻止我接收任何标准输出内容?

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

当我退出这个样子,该程序的功能,如正常,但如果我尝试读取标准输出的输出,我什么也没得到(使用即少或wc)。

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

我试着修改我的ASM程序退出call exit而不是原始的系统调用。标准输出现在可以正常读取。

测试用例

.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 

编辑:

我试过CAL凌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 
$ 

谢谢大家,特别是nos。

当您将stdout管道传输到wc时,标准输出将完全缓冲。

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

应该能看到输出,如果你的printf调用之后调用fflush(stdout),或者如果你只是运行在一个统一公债程序而不输出管道到另一个程序 - 在这种情况下,标准输出通常会行缓冲,所以stdout是刷新时你写一个\ n

+0

你会怎么称呼''fflush(stdout)''?根据''unistd.h'',fileno是1,但根据http://stackoverflow.com/questions/8502945/printf-without-newline-in-assembly,它是0. – 2012-03-18 00:58:39

+1

stdout在这个上下文中是全局FILE *命名为stdout(默认情况下连接到文件描述符1,标准输出)。您不能使用文件描述符(0或1)作为需要FILE *的函数的参数。你可以用NULL作为参数调用fflush()来刷新所有打开的FILE *。 – nos 2012-03-18 01:10:59

+0

但我不认为程序集中有NULL?在C中,NULL == 0.它如何知道它的区别? – 2012-03-18 01:16:08

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

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

+0

但是如果我不管它,我会得到输出。另外,'man_exit''注意我可以使用''tcflush''。我试过了,但没有奏效。我如何调用''fflush(stdout)''? ''pushl $ 1;打电话给fflush''? – 2012-03-18 00:46:30

+0

我在''unistd.h''中找到了stdout fileno。另外,我只是用堆栈中的1和''%eax''来尝试它们,它们都是段错误。 – 2012-03-18 00:50:36

+1

回复自己:这需要一个FILE *,而不是文件描述符。 – 2012-03-18 01:34:44

按照手册页_exit(2)

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

所以,除非你刷新标准输出,它可能会被丢弃。

+0

我试着调用''tcflush'',它似乎没有什么区别。 – 2012-03-18 00:52:20

+1

tcflush在终端设备上工作。你需要调用fflush(stdout)。 (标准输出是由libc管理的全局文件*,不能使用文件描述符)。如果你遇到麻烦,请调用fflush(NULL),它将刷新所有文件*的 – nos 2012-03-18 01:05:27

+0

请参阅编辑的问题。 – 2012-03-18 01:09:09