2015-11-08 2 views
0

Я столкнулся с странной ошибкой при работе с Unix-каналами. Я написал короткую программу для демонстрации проблемы.C - труба Unix, close() влияет на выход?

Вот код:

#include <stdio.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <ctype.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
#include <string.h> 
#include <fcntl.h> 


int main(int argc, char*args[]){ 
    int fd[2]; 
    pipe(fd); 
    int pid = fork(); 
    if(pid<0){ 
     perror("fork()"); 
     exit(1); 
    }else if(pid == 0){ 
     close(fd[0]); 
     const char* msg = "I'm here\n"; 
     size_t len = strlen(msg) + 1; // +1 for null char 
     write(fd[1], msg, len); 
     while(1){/*does other work*/} 
    }else{ 
     close(fd[1]); 
    } 
    pid = fork(); 
    if(pid<0){ 
     perror("fork()"); 
     exit(1); 
    }else if(pid == 0){ 
     close(fd[0]); 
     const char* msg = "I'm here\n"; 
     size_t len = strlen(msg) + 1; // +1 for null char 
     write(fd[1], msg, len); 
     while(1){/*does other work*/} 
    }else{ 
     close(fd[1]); 
    } 
    //close(fd[1]); 
    sleep(5); 

    const char* msg = "I'm here\n"; 
    size_t len = strlen(msg) + 1; // +1 for null char 
    char str[len]; 
    fcntl(fd[0],F_SETFL, O_NONBLOCK); 
    if(read(fd[0], str, len)<=0){ 
     printf("Nothing from child\n"); 
    } 
    /*does other work*/ 
    fcntl(fd[0],F_SETFL, O_NONBLOCK); 
    if(read(fd[0], str, len)<=0){ 
     printf("Nothing from child\n"); 
    } 
    printf("finished read\n"); 
    /*does other work*/ 
    wait(NULL); 
    return 0; 
} 

родительского процесса вилы двух детей и создает канал. Два дочерних процесса записываются в канал, родительский считывает из канала.

Родительский процесс должен восстанавливать всю информацию, написанную ребенком.

Однако, когда я бегу выше программы, родительские распечатывает

Nothing from child 
finished read 

Почему?

Интересно, если я пишу один близко (FD [1]), как это:

#include <stdio.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <ctype.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
#include <string.h> 
#include <fcntl.h> 


int main(int argc, char*args[]){ 
    int fd[2]; 
    pipe(fd); 
    int pid = fork(); 
    if(pid<0){ 
     perror("fork()"); 
     exit(1); 
    }else if(pid == 0){ 
     close(fd[0]); 
     const char* msg = "I'm here\n"; 
     size_t len = strlen(msg) + 1; // +1 for null char 
     write(fd[1], msg, len); 
     while(1){/*does other work*/} 
    }else{ 

    } 
    pid = fork(); 
    if(pid<0){ 
     perror("fork()"); 
     exit(1); 
    }else if(pid == 0){ 
     close(fd[0]); 
     const char* msg = "I'm here\n"; 
     size_t len = strlen(msg) + 1; // +1 for null char 
     write(fd[1], msg, len); 
     while(1){/*does other work*/} 
    }else{ 

    } 
    close(fd[1]);//HERE IS THE DIFFERENCE 
    sleep(5); 

    const char* msg = "I'm here\n"; 
    size_t len = strlen(msg) + 1; // +1 for null char 
    char str[len]; 
    fcntl(fd[0],F_SETFL, O_NONBLOCK); 
    if(read(fd[0], str, len)<=0){ 
     printf("Nothing from child\n"); 
    } 
    /*does other work*/ 
    fcntl(fd[0],F_SETFL, O_NONBLOCK); 
    if(read(fd[0], str, len)<=0){ 
     printf("Nothing from child\n"); 
    } 
    printf("finished read\n"); 
    /*does other work*/ 
    wait(NULL); 
    return 0; 
} 

Он работает, он также работает, если я не близко (FD [1]) на всех. Я теряюсь, почему позиция close() влияет на чтение труб?

+1

Почему, по вашему мнению, это нормально закрыть fd [1] дважды? –

+1

В первом примере вы закрываете сторону записи трубы перед тем, как развить второй дочерний элемент, чтобы ребенок не мог записывать в трубку. Вы исправляете это, перемещая закрытие. Что за вопрос? –

+0

А, я понимаю. Спасибо, я забыл, что у второго ребенка конец записи будет закрыт! – Noodles

ответ

1

Я не уверен, если это ваша проблема, но это утверждение явно не так:

write(fd[1], "I'm here\n", 256); 

Вы пишете 256 байт из исходного буфера, который является очень короткой статической строкой. Я думаю, вы хотите это сказать:

const char* msg = "I'm here\n"; 
size_t len = strlen(msg); // could be strlen(msg)+1 - see discussion in comments. 
write(fd[1], "I'm here\n", len); 
+0

Eh ... когда вы последний раз вы выводили ** нуль-завершающий байт ** с помощью' write() 'или любой другой функции ввода-вывода ? На терминалах это нормально, но при работе с трубами это может вызвать серьезные проблемы. – 3442

+0

Хорошая точка. Реальный протокол будет включать в себя некоторый формальный способ между читателем/автором, чтобы договориться о том, как определить конец строки. Но так как он просто вызывает 'read' с большим, чем необходимо, буфером для получения сообщения, как он узнает, сколько байтов в сообщении? Вот почему я изначально включал null - так, чтобы его 'read' не оставил буфер без нулевого символа. – selbie

+0

Вы правы. Но если он не пишет две или более отдельных строк, то он может просто «читать» до EOF, и все. – 3442

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