(No output即使在输出不包括换行符的终端上也有同样的原因。
我试着在我的汇编代码中使用printf
,这是一个最小的例子,它应该只打印hello
到stdout:
.section .rodata
hello:
.ascii "hello\n\0"
.section .text
.globl _start
_start:
movq $hello, %rdi # first parameter
xorl %eax, %eax # 0 - number of used vector registers
call printf
#exit
movq $60, %rax
movq $0, %rdi
syscall
字符串
我用
gcc -nostdlib try_printf.s -o try_printf -lc
型
当我运行它的时候,它似乎工作了:打印出字符串hello
,退出状态为0
:
XXX$ ./try_printf
hello
XXX$ echo $?
0
XXX$
型
但当我试图捕捉文本时,很明显,有些东西无法正常工作:
XXX$ output=$(./try_printf)
XXX$ echo $output
XXX$
型
变量output
的值应为hello
,但为空。
我使用printf
有什么问题?
2条答案
按热度按时间nimxete21#
**在使用stdio函数(如printf)之后,使用
call exit
而不是原始_exit
系统调用。**这会在进行exit_group
系统调用之前刷新stdio缓冲区(write
系统调用)。(Or如果您的程序定义了
main
而不是_start
,则从main
返回等效于调用exit
。您不能从_start
调用ret
。)调用fflush(NULL)
应该也可以。正如Michael所解释的,动态链接C库是可以的。这也是"Programming bottom up"一书中介绍它的方式(请参见第8章)。
然而,从C库调用
exit
是很重要的,这样才能结束程序,而不是绕过它,这是我调用exit-syscall
的错误之处。正如Michael所暗示的那样,exit执行了很多clean up类似于刷新流的操作。事情的经过就是这样:如here所述,C库缓冲标准流,如下所示:
1.没有标准错误的缓冲区。
1.如果标准输出/输入是终端,则它是线路缓冲的。
1.如果标准输出/输入不是终端,则它是完全缓冲的,因此在原始出口系统调用之前需要刷新。
当第一次为数据流呼叫
printf
时,便会决定适用哪种情况。因此,如果在终端中直接调用
printf_try
,则可以看到程序的输出,因为hello
的末尾有\n
(在行缓冲模式下触发刷新),并且它是一个终端,也是2。的情况下。通过
$(./printf_try)
调用printf_try
意味着stdout不再是终端(实际上我不知道它是临时文件还是内存文件),因此3. case生效-需要显式刷新,即调用C-exit
。pzfprimi2#
C标准库通常包含用于标准I/O流的初始化代码-通过定义自己的入口点可以绕过这些初始化代码。尝试定义
main
而不是_start
:字符串
然后使用
gcc try_printf.s -o try_printf
构建(* 即 *,不使用-nostdlib
)。