Это, кажется, довольно общая вещь сделать, и мне удалось учить мне все, что я должен заставить ее работать, за исключением того, что у меня теперь есть единственная проблема, которая игнорирует мой поиск и устранение неисправностей.
int nonBlockingPOpen(char *const argv[]){
int inpipe;
pid_t pid;
/* open both ends of pipe nonblockingly */
pid = fork();
switch(pid){
case 0: /*child*/
sleep(1); /*child should open after parent has open for reading*/
/*redirect stdout to opened pipe*/
int outpipe = open("./fifo", O_WRONLY);
/*SHOULD BLOCK UNTIL MAIN PROCESS OPENS FOR WRITING*/
dup2(outpipe, 1);
fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK);
printf("HELLO WORLD I AM A CHILD PROCESS\n");
/*This seems to be written to the pipe immediately, blocking or not.*/
execvp(*argv, argv);
/*All output from this program, which outputs "one" sleeps for 1 second
*outputs "two" sleeps for a second, etc, is captured only after the
*exec'd program exits!
*/
break;
default: /*parent*/
inpipe = open("./fifo", O_RDONLY | O_NONBLOCK);
sleep(2);
/*no need to do anything special here*/
break;
}
return inpipe;
}
Почему дочерний процесс не запишет свой stdout в канал каждый раз, когда строка сгенерирована? Есть ли что-то, что я пропускаю в пути execvp или работе dup2? Я знаю, что мой подход ко всему этому является немного странным, но я не могу найти другой способ получить вывод двоичных файлов с закрытым исходным кодом программно.
Я предполагаю, что вы получите вывод программы exec'd только после ее выхода, потому что она не сбрасывает после каждого сообщения. Если так, то извне ничего не поделаешь.
Я не совсем уверен, как это должно соотноситься с выбором между блокирующим и неблокирующим вводом-выводом в вашем вопросе. Неблокирующая запись может завершиться неудачей полностью или частично: вместо того, чтобы блокировать программу до тех пор, пока в канале не появится место, вызов немедленно возвращается и сообщает, что он не смог записать все, что должно было иметь. Неблокирующий ввод-вывод не увеличивает размер буфера и не вызывает принудительную очистку вывода, и это может плохо поддерживаться некоторыми программами.
Вы не можете принудительно сбросить двоичную программу, которую вы выполняете. Если вы думали, что неблокирующий ввод-вывод был решением этой проблемы, извините, но я боюсь, что это довольно ортогонально.
РЕДАКТИРОВАТЬ: Что ж, если программа exec'd использует только буферизацию, предоставляемую libc (не реализует свою собственную) и динамически связана, вы можете принудительно сбросить ее, связав ее с модифицированной libc, которая сбрасывает каждую запись. Это была бы отчаянная мера. попробовать, только если все остальное не удалось.
Когда процесс запущен (через execvp() в вашем примере), поведение стандартного вывода зависит от того, является ли устройство вывода терминалом или нет. Если нет (а FIFO не является терминалом), то вывод будет полностью буферизованным, а не линейным. С этим ничего нельзя поделать; это делает библиотека (стандартная) C.
Если вы действительно хотите заставить программу работать со строчной буферизацией, то вам придется предоставить программе псевдотерминал в качестве стандартного вывода. Это влечет за собой интересные проблемы - с псевдотерминалами или ptys не все так просто. Для POSIX-функций см.:
grantpt()
- предоставить доступ к подчиненному псевдотерминальному устройствуposix_openpt()
- открыть псевдотерминальное устройствоptsname()
- получить имя подчиненного псевдотерминального устройстваunlockpt()
- разблокировать псевдотерминальную пару ведущий/ведомый Почему дочерний процесс не записывает свой стандартный вывод в конвейер каждый раз, когда генерируется строка?
Откуда вы это знаете? Вы даже не пытаетесь читать вывод с фифо.
Примечание. Судя по имени файла, я предполагаю, что вы используете fifo . Или это простой файл?
И небольшая ошибка в дочернем: после dup2 ()
вам нужно закрыть (outpipe)
.
fcntl (1, F_SETFL, fcntl (1, F_GETFL) | O_NONBLOCK);
В зависимости от того, какую программу вы используете exec (), вы можете либо потерять какой-то вывод, либо вызвать сбой программы, поскольку запись в стандартный вывод теперь может завершиться ошибкой с EWOULDBLOCK.
IIRC fifos имеет тот же размер буфера, что и каналы. Минимум для POSIX составляет 512 байт, обычно 4 КБ или 8 КБ.
Вы, наверное, хотите объяснить, зачем вам это вообще нужно. Неблокирующий ввод-вывод имеет другую семантику по сравнению с блокирующим вводом-выводом, и если ваш дочерний процесс не ожидает, что вы столкнетесь с различными проблемами.
printf («ПРИВЕТ, МИР, Я ДЕТСКИЙ ПРОЦЕСС \ n»);
stdout помещается в буфер, после этого у меня будет fflush (stdout)
. (Невозможно найти документацию, сможет ли exec () самостоятельно сбрасывать stdout или нет.)
Есть ли что-то, чего мне не хватает в работе execvp или dup2? Я знаю, что мой подход ко всему этому немного странный, но я не могу найти другого способа программно фиксировать вывод двоичных файлов с закрытым исходным кодом.
Я бы не стал играть с неблокирующим вводом-выводом - и оставил бы его как есть в режиме блокировки.
И я бы использовал pipe () вместо fifo. В Linux man pipe есть удобный пример с fork ().
В остальном это вполне нормальная практика.
Использование sleep()
не гарантирует не гарантирует, что родитель откроет трубу первым - как говорит Dummy00001, вы должны использовать pipe()
трубу, а не именованную трубу. Вам также следует проверить, что execvp()
и fork()
не работают, и вы не должны устанавливать дочернюю сторону на неблокирующую - это решение должен принимать дочерний процесс.
int nonBlockingPOpen(char *const argv[])
{
int childpipe[2];
pid_t pid;
pipe(childpipe);
pid = fork();
if (pid == 0)
{
/*child*/
/*redirect stdout to opened pipe*/
dup2(childpipe[1], 1);
/* close leftover pipe file descriptors */
close(childpipe[0]);
close(childpipe[1]);
execvp(*argv, argv);
/* Only reached if execvp fails */
perror("execvp");
exit(1);
}
/*parent*/
/* Close leftover pipe file descriptor */
close(childpipe[1]);
/* Check for fork() failing */
if (pid < 0)
{
close(childpipe[0]);
return -1;
}
/* Set file descriptor non-blocking */
fcntl(childpipe[0], F_SETFL, fcntl(childpipe[0], F_GETFL) | O_NONBLOCK);
return childpipe[0];
}