Ваше перенаправление правильно; проблема должна быть в сборке, которую вы создаете.
Инструмент для устранения таких проблем - strace
. Запуск вашей программы под strace
, показывает:
strace ./basic
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000
write(1, "10\n", 3) = 3
10
write(1, "z\377n\f\377\177\0\0\0\0\0\0\0\0\0\0\202\377n\f\377\177\0\0\362\377n\f\377\177\0\0"..., 139905561665008 <unfinished ... exit status 0>
Вы можете четко видеть желаемый результат, но также и некоторые "случайные" записи. Откуда эта запись?
ГБД на помощь:
gdb -q ./basic
Reading symbols from /tmp/basic...done.
(gdb) catch syscall write
Catchpoint 1 (syscall 'write' [1])
(gdb) r
Catchpoint 1 (call to syscall 'write'), 0x00007ffff7b32500 in __write_nocancel ()
(gdb) bt
#0 0x00007ffff7b32500 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:82
#1 0x00007ffff7acd133 in _IO_new_file_write (f=0x7ffff7dd7780, data=0x7ffff7ff8000, n=3) at fileops.c:1276
#2 0x00007ffff7ace785 in new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:530
#3 _IO_new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:503
#4 0x00007ffff7accd9e in _IO_new_file_xsputn (f=0x7ffff7dd7780, data=0x601023, n=1) at fileops.c:1358
#5 0x00007ffff7a9f9c8 in _IO_vfprintf_internal (s=0x7ffff7dd7780, format=<value optimized out>, ap=0x7fffffffda20) at vfprintf.c:1644
#6 0x00007ffff7aaa53a in __printf (format=0x7ffff7ff8000 "10\n") at printf.c:35
#7 0x000000000040054f in main ()
Хорошо, это ожидаемый звонок для записи.
(gdb) c
10
Catchpoint 1 (returned from syscall 'write'), 0x00007ffff7b32500 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:82
82 in ../sysdeps/unix/syscall-template.S
Это просто возвращение из системного вызова. Писать удалось? (Мы знаем, что это было сделано, так как мы видим его результаты выше, но давайте подтвердим.)
(gdb) p $rax
$1 = 3
Хорошо. Написать написал ожидаемых 3 символов.
(gdb) c
Catchpoint 1 (call to syscall 'write'), 0x0000000000400577 in os_return ()
Это запись, которую мы не ожидали. Откуда?
(gdb) bt
#0 0x0000000000400577 in os_return ()
#1 0x0000000000400557 in main ()
(gdb) disas
Dump of assembler code for function os_return:
0x0000000000400557 <+0>: movabs $0x1,%rax
0x0000000000400561 <+10>: movabs $0x0,%rbx
0x000000000040056b <+20>: movabs $0x5,%rcx
0x0000000000400575 <+30>: int $0x80
=> 0x0000000000400577 <+32>: nop
0x0000000000400578 <+33>: nop
0x0000000000400579 <+34>: nop
0x000000000040057a <+35>: nop
0x000000000040057b <+36>: nop
0x000000000040057c <+37>: nop
0x000000000040057d <+38>: nop
0x000000000040057e <+39>: nop
0x000000000040057f <+40>: nop
End of assembler dump.
(gdb) quit
Итак, ваш системный вызов выполнил write(2)
вместо ожидаемого exit(2)
. Почему это случилось?
Поскольку вы определили EXIT
неправильно:
grep 'define .*NR_exit' /usr/include/asm/unistd*.h
/usr/include/asm/unistd_32.h:#define __NR_exit 1
/usr/include/asm/unistd_32.h:#define __NR_exit_group 252
/usr/include/asm/unistd_64.h:#define __NR_exit 60
/usr/include/asm/unistd_64.h:#define __NR_exit_group 231
Сверху можно сказать, что EXIT
должно быть 1 в 32-битном режиме, но 60 в 64-битном режиме.
А как насчет NR_write? Это 1 в 64-битном режиме?
grep 'define .*NR_write' /usr/include/asm/unistd_64.h
#define __NR_write 1
#define __NR_writev 20
Действительно, это так. Итак, мы решили, «откуда взялась шальная запись?» головоломка. Исправив EXIT
до 60 и перезапустив под strace
, теперь мы видим:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000
write(1, "10\n", 3) = 3
10
_exit(1) = ?
Это все еще не правильно. Мы должны звонить _exit(0)
, а не _exit(1)
. Взгляд на x86_64
ABI показывает, что ваш регистр используется некорректно: номер системного вызова должен быть в %rax
, а аргументы в %rdi
, %rsi
, %rdx
и т. Д.
Исправив это (и удалив поддельные mov rcx, 5
), мы наконец-то получим желаемый результат из strace
:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000
write(1, "10\n", 3) = 3
10
_exit(0) = ?
Итак, , теперь , мы готовы посмотреть, не исправили ли вышеупомянутые исправления проблему перенаправления.
Повторный запуск в режиме strace с перенаправлением вывода:
strace ./basic > t
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f08161eb000
_exit(0) = ?
Очевидно, что наш звонок на write
отсутствует. Куда это делось?
Ну, вывод stdout
по умолчанию буферизован строкой и полностью буферизируется при перенаправлении в файл. Возможно, нам не хватает fflush
звонка?
Действительно, добавление вызова к fflush(NULL)
непосредственно перед выходом решает проблему:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8afd450000
write(1, "10\n", 3) = 3
_exit(0) = ?
Я надеюсь, что вы узнали что-то сегодня (я сделал; -)