трубопроводная команда ./a.out |cat, printf ("строка \ n") в a.out не выполняется и не выводится - PullRequest
1 голос
/ 28 октября 2019

Очень странная вещь: a.out просто printf() строка затем переходит в мертвый цикл, когда a.out выполняется одиночно, я могу увидеть строку в терминале, но если конвейер a.out с cat, томы ничего не видим.

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        printf("----------\n");
        while (1) {
                sleep(1000);
        }
        return 0;
}

результат выполнения

$ cc test.c
$ ./a.out 
----------
^C
$ ./a.out | cat
^C

, если я выпрямлю a.out |cat, write (1) системный вызов не вызывается

$ strace ./a.out | cat
execve("./a.out", ["./a.out"], 0x7ffdaa23b200 /* 65 vars */) = 0
brk(NULL)                               = 0x5567446dd000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=94391, ...}) = 0
mmap(NULL, 94391, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f977ba22000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ba20000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f977b835000
mmap(0x7f977b85a000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f977b85a000
mmap(0x7f977b9cd000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7f977b9cd000
mmap(0x7f977ba16000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7f977ba16000
mmap(0x7f977ba1c000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f977ba1c000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f977ba21500) = 0
mprotect(0x7f977ba16000, 12288, PROT_READ) = 0
mprotect(0x556743cd8000, 4096, PROT_READ) = 0
mprotect(0x7f977ba64000, 4096, PROT_READ) = 0
munmap(0x7f977ba22000, 94391)           = 0
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
brk(NULL)                               = 0x5567446dd000
brk(0x5567446fe000)                     = 0x5567446fe000
nanosleep({tv_sec=1000, tv_nsec=0}, ^C{tv_sec=994, tv_nsec=769383373}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
strace: Process 5050 detached

, если я связываю a.out сингл, то имеет запись (1)

$ strace ./a.out 
execve("./a.out", ["./a.out"], 0x7ffe09a7c360 /* 65 vars */) = 0
brk(NULL)                               = 0x564b085a8000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=94391, ...}) = 0
mmap(NULL, 94391, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fac37df5000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac37df3000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fac37c08000
mmap(0x7fac37c2d000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fac37c2d000
mmap(0x7fac37da0000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7fac37da0000
mmap(0x7fac37de9000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7fac37de9000
mmap(0x7fac37def000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fac37def000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fac37df4500) = 0
mprotect(0x7fac37de9000, 12288, PROT_READ) = 0
mprotect(0x564b076e3000, 4096, PROT_READ) = 0
mprotect(0x7fac37e37000, 4096, PROT_READ) = 0
munmap(0x7fac37df5000, 94391)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
brk(NULL)                               = 0x564b085a8000
brk(0x564b085c9000)                     = 0x564b085c9000
write(1, "----------\n", 11----------
)            = 11
nanosleep({tv_sec=100

Почему?

1 Ответ

2 голосов
/ 28 октября 2019

Выходной поток может находиться в одном из трех различных режимов: без буферизации , с линейной буферизацией или с полной буферизацией . В режиме без буферизации вывод записывается немедленно. В режиме с буферизацией строки выходные данные сначала записываются во внутренний буфер потока, пока буфер не заполнится или не будет записана новая строка, а затем буфер сбрасывается на выход. В режиме полностью буферизированный выходные данные сначала записываются во внутренний буфер потока до тех пор, пока буфер не заполнится, а затем буфер сбрасывается на выход. (Некоторые реализации могут также сбрасывать вывод в другое время, например, при чтении ввода с интерактивного устройства.)

Входные потоки также могут находиться в тех же трех режимах, и это определяет, когда ввод для чтения становится доступным длявызывающая функция, которая читает потоки.

Реализация инициализирует режим стандартных потоков ввода (stdin), стандартного вывода (stdout) и вывода стандартных ошибок (stderr) перед main функция вызывается (или, по крайней мере, перед любым доступом к потокам). При определенных обстоятельствах реализация может инициализировать стандартный ввод или стандартный вывод в режиме с полной буферизацией . Стандартные потоки ввода и вывода инициализируются в полностью буферизованный режим тогда и только тогда, когда реализация может определить, что они не связаны с интерактивным устройством (таким как терминал). (Стандартный поток вывода ошибок никогда не инициализируется в полностью буферизованном режиме.)

Как правило, библиотека времени выполнения C системы POSIX будет вызывать isatty в базовых файловых дескрипторах для стандартных потоков ввода и вывода и устанавливатьпотоки переходят в режим полной буферизации, если isatty возвращает 0. Это происходит до вызова функции main.

Когда вы запускаете "./a.out" с выводом, идущим на терминал, среда выполнения Cбиблиотека определяет, что вывод идет на интерактивное устройство, и не устанавливает stdout в режим полной буферизации. Он будет установлен в один из других режимов, обычно в режим линейной буферизации. Однако, когда вы запускаете "/a.out" с выводом, идущим в канал, библиотека времени выполнения C определяет, что вывод не направляется на интерактивное устройство, а делает установить stdoutв полностью буферизованный режим. Вот почему вывод не записывается в канал немедленно.

Есть два способа решения вашей проблемы. Первый - изменить стандартный поток вывода на режим с буферизацией строки * режим 1041 * или режим без буферизации перед первым вызовом на printf:

setvbuf(stdout, NULL, _IOLBF, 0); // set standard output to line buffered mode

setvbuf(stdout, NULL, _IONBF, 0); // set standard output to unbuffered mode

Другой способчтобы сбросить стандартный вывод по требованию:

fflush(stdout); // write buffered standard output contents
...