Почему _exit (0) (выход из syscall) не позволяет мне получать содержимое stdout? - PullRequest
5 голосов
/ 18 марта 2012

У меня есть программа сборки Linux x86-32 GAS, завершающаяся следующим образом:

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

Когда я выхожу так, программа работает как обычно, но если я пытаюсь прочитать вывод stdout, я получаюничего (используя т.е. less или wc).

Я попытался скомпилировать минимальную C-программу и сравнить результаты работы strace.Единственное различие, которое я обнаружил, заключалось в том, что GCC заставил программу на C (int main() { printf("donkey\n"); }) неявно завершить работу с exit_group(0) в выводе strace.

Я попытался изменить свою программу 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

РЕДАКТИРОВАТЬ:

Я пытался позвонить и tcflush и fflush, и у меня все еще есть проблема.С fflush я даже получаю segfault.

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 без перевода строки в сборке

Аргумент для 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.

Ответы [ 3 ]

3 голосов
/ 18 марта 2012

когда вы отправляете стандартный вывод на wc, стандартный вывод становится полностью буферизованным.

_exit немедленно завершает процесс и не запускает atexit () и другие обработчики очистки. Среда выполнения зарегистрирует такие обработчики, которые будут запускаться при выходе, который сбрасывает открытый FILE *, такой как stdout. Когда эти обработчики не выполняются при выходе, буферизованные данные будут потеряны.

Вы должны увидеть вывод, если вы вызовете fflush(stdout) после вызова printf, или если вы просто запустите программу в консоле, не передавая вывод в другую программу - в этом случае stdout обычно будет буферизоваться строкой, поэтому stdout сбрасывается всякий раз, когда вы пишете \ n

2 голосов
/ 18 марта 2012

Вывод, отправляемый на стандартный выход, обычно буферизуется. Если вы позвоните fflush(stdout) до того, как позвоните _exit, вы должны получить свой вывод.

Причина, по которой exit работает, заключается в том, что эта функция гарантированно закрывает и сбрасывает любые открытые потоки (например, stdout) перед вызовом самого _exit, чтобы фактически завершить программу.

1 голос
/ 18 марта 2012

Согласно справочной странице для _exit(2)

Сбрасывает ли он стандартные буферы ввода / вывода и удаляет временные файлы созданный с помощью tmpfile (3) зависит от реализации. С другой hand, _exit () закрывает открытые файловые дескрипторы, и это может вызвать неизвестная задержка, ожидание ожидающего завершения вывода. Если задержка нежелательно, может быть полезно вызывать такие функции, как tcflush (3) перед вызов _exit (). Отменяется ли ожидающий ввод-вывод и какие ожидающий ввод-вывод может быть отменен при _exit (), зависит от реализации.

Так что, если вы не сбросили стандартный вывод, он может быть удален.

...