2013-02-10 4 views
0

Я пишу код С, который включает в себя использование труб. Для того, чтобы процесс ребенка использовать трубку вместо STDOUT для вывода, я использовал следующие строки:C закрыть STDOUT работает навсегда

close(STDOUT); 
    dup2(leftup[1], STDOUT); 

Тем не менее, кажется, идти в какой-то бесконечный цикл или повесить на этих линиях. Когда я избавлюсь от close, он висит на dup2.

Любопытно, что та же самая идея работает непосредственно предшествующая линия для STDIN:

close(STDIN); 
dup2(leftdown[0], STDIN); 

Что может быть причина такого поведения?

Edit: Просто чтобы быть ясно ...

#define STDIN 0 
#define STDOUT 1 

Edit 2: Вот урезанный пример:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/wait.h> 

#define STDIN 0 
#define STDOUT 1 

main(){ 
    pid_t child1 = 0; 
    int leftdown[2]; 
    if (pipe(leftdown) != 0) 
     printf("ERROR"); 
    int leftup[2]; 
    if (pipe(leftup) != 0) 
     printf("ERROR"); 

    printf("MADE PIPES"); 

    child1 = fork(); 
    if (child1 == 0){ 
     close(STDOUT); 
     printf("TEST 1"); 
     dup2(leftup[1], STDOUT); 
     printf("TEST 2"); 
     exit(0); 
    } 
    return(0); 
} 

"TEST 1" линия никогда не достигается. Единственный выход - "MADE PIPES".

+1

Чтобы быть еще четче: есть уже «STDIN_FILENO» и др. Не изобретайте велосипед. – 2013-02-10 06:27:45

ответ

1

На минимум вы должны убедиться, что функция dup2 возвращает новый дескриптор файла, а не -1.

Всегда существует вероятность, что он даст вам ошибку (например, если вызов pipe() не удался ранее). Кроме того, будьте абсолютно уверены, что вы используете правильные индексы (0 и 1) - я был укушен этим раньше, и это зависит от того, находитесь ли вы в родительском или дочернем процессе.


На основании вашего редактирования, я нисколько не удивлен, что MADE PIPES это последнее, что напечатано.

При попытке распечатать TEST 1 вы уже закрыли дескриптор STDOUT, чтобы он никуда не исчез.

При попытке напечатать TEST 2 у вас есть dup ed STDOUT дескриптор, чтобы он перешел к родительскому, но ваш родитель не читает его.

Если вы измените Разветвляющиеся коды на:

child1 = fork(); 
if (child1 == 0){ 
    int count; 
    close(STDOUT); 
    count = printf("TEST 1\n"); 
    dup2(leftup[1], STDOUT); 
    printf("TEST 2 (%d)\n", count); 
    exit(0); 
} else { 
    char buff[80]; 
    read (leftup[0], buff, 80); 
    printf ("%s\n", buff); 
    sleep (2); 
} 

вы увидите, что TEST 2 (-1) строки выводится родителем, потому что он прочитал его через трубу. В коде -1 есть код возврата от printf, который вы пытались выполнить ребенку после того, как вы закрыли дескриптор STDOUT (но перед вами dup ed it), что означает, что он не удался.

От ISO C11 7.20.6.3 The printf function:

printf функция возвращает количество символов, переданное или отрицательное значение, если произошла ошибка вывода или кодирования.

+0

Ничего не возвращается из-за того, что я описал. Код обработки ошибок существует, но я урезал его, чтобы все было как можно проще. –

+0

@ Andrew Latham, извините, что он висит, когда вы вынимаете код обработки ошибок? –

+0

Он висит в обоих направлениях. –

1

Multiple вещь упомянуть,

При использовании вилки, это вызывает почти полную копию родительского процесса. Это также включает буфер, который настроен для стандартного потока вывода stdout. Поток stdout будет содержать данные до тех пор, пока буфер не будет заполнен или явно не запрошен для сброса данных из буфера/потока. Теперь из-за этого теперь у вас есть "MADE PIPES" сидит в буфере. Когда вы закрываете STDOUT fd и используете printf для записи данных на терминал, он ничего не делает, кроме как передает "TEST 1" и "TEST 2" в буфер stdout и не вызывает никаких ошибок или сбоев (из-за достаточного количества буфера). Таким образом, даже после дублирования pipe fd на STDOUT, из-за буферизированного выхода printf даже не коснулся конца записи трубы. Самое главное, используйте только один набор API-интерфейсов, то есть либо NIX, либо стандартные функции C lib. Убедитесь, что вы хорошо понимаете библиотеки, поскольку они часто играют в трюки для какой-то оптимизации.

Теперь, еще одно замечание, убедитесь, что вы закрываете соответствующие концы трубы в соответствующем процессе. Это означает, что если, скажем, pipe-1 используется для связи от родителя к потомку, тогда убедитесь, что вы закрываете конец чтения в родительском и записываете конец в дочернем. В противном случае ваша программа может зависать из-за подсчета ссылок, связанных с файловыми дескрипторами, вы можете думать, что закрытие конца чтения в дочернем режиме означает, что конец чтения канала закрыт. Но, когда вы не закрываете чтение в родительском, у вас есть дополнительный счетчик ссылок для чтения конца трубы, и в конечном итоге труба никогда не будет закрыта.

Есть много других вещей, связанных с вашим стилем кодирования, лучше вы должны удержать его :) Рано вы узнаете, что это лучше сэкономит ваше время. :)

Проверка ошибок абсолютно важна, используйте хотя бы утверждение, чтобы убедиться, что ваши предположения верны.

При использовании printf заявления зарегистрировать ошибку или как метод отладки и вы меняете (STDOUT/STDIN/STDERR) его лучше, когда вы открываете файл логтерминального FD с *NIX открытой и ошибкой записи/записи журнала к нему.

Наконец-то, используя утилиту strace, вы будете очень полезны. Эта утилита позволит вам отслеживать системные вызовы, выполняемые во время выполнения кода. Это очень прямолинейно и просто. Вы даже можете присоединить это к процессу выполнения, если у вас есть права.

Смежные вопросы