2014-11-21 2 views
0

Сегодня я столкнулся с небольшой проблемой на C, где я не могу понять, почему мои процессы не «выстраиваются» в том, как они используют канал.Передача данных через каналы в C между несколькими дочерними процессами (среда UNIX)

У меня есть 2 дочерних процесса, разветвленных из родительского процесса, которые записывают некоторые данные, и они должны записывать эти данные в трубу. Затем труба периодически считывается родительским процессом и «освобождается» от данных в трубе и печатается в терминальной линии. Затем все это повторяется.

//Creates Three Processes A,B,C which communicate data through a pipe. 
int main(void) { 
    int  fd[2], nbytes; 
    pid_t childpidB, childpidC; 
    char readbuffer[100]; 
    readbuffer[0] = '\0'; 
    pipe(fd); 

    if((childpidB = fork()) < 0) 
    { 
     perror("fork B error."); 
     exit(1); 
    } 
    if(childpidB == 0) 
    { 
     close(fd[0]); 
     char AAA[3] = {'A','A','A'}; 
     int j = 0; 
     int k = 1; 
     while (j < 500) { 
     if(j%100==0 && j!=0) { 
      usleep(100); 
     } 
     char numbers[6]; 
     numbers[0] = '\0'; 
     sprintf(numbers,"%03dAAA",k); 
     k++; 
     j++; 
     write(fd[1], numbers, (strlen(numbers)+1)); 
     } 
     close(fd[1]); 
     exit(0); 
    } 
    if((childpidC = fork()) < 0) 
    { 
     perror("fork C error."); 
     exit(1); 
    } 
    if(childpidC == 0) 
    { 
     close(fd[0]); 
     int i = 0; 
     char c = 'A'; 
     int numberafter = 0; 
     while (i < 260) { 
     if(i%60==0 && i!=0) { 
      usleep(300); 
     } 
     char dump[3]; 
     dump[0] = '\0'; 
     if(c=='[') { c='A'; numberafter++; } 
     sprintf(dump,"%cx%d\n",c,numberafter); 
     c++; 
     write(fd[1], dump, (strlen(dump)+1));  
     i++; 
     } 
     close(fd[1]); 
     exit(0); 
    } 

    //Process A 
    if(childpidC > 0 && childpidB > 0) { 
    int x = 0; 
    close(fd[1]); 
    while (nbytes = read(fd[0], readbuffer, sizeof(readbuffer)) > 0) { 
     if(x%50==0 && x!=0) { 
    usleep(200); 
     } 
     printf("%s\n", readbuffer); 
     x++; 
    } 
    close(fd[0]); 
    exit(0); 
    } 
} 

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

001AAA 
Ax0 

Gx0 

Ox0 
...(keeps going for a while)... 

Я задаюсь вопросом, почему/как это только чтение небольшое количество данных из трубы каждый раз. Данные, которые он читает, действительно выглядят правильно, но это просто не чтение 100 символов в строке (как я думал, это логически должно быть, в моем коде). Затем он как-то ничего не читает из трубы и записывает только пустую строку (потому что он не читает никаких символов, но все равно печатает пустую строку). Это должно означать, что дочерние процессы только записывают несколько символов в канал на каждой итерации, но я не понимаю, как это может быть. Выход также различается при каждом запуске программы.

Похоже, что ребенок обрабатывает и родители не синхронизированы, но я думал, что для этого потребуется закрыть соответствующий интервал fd [0] или fd [1].

EDIT: Я обновил код, чтобы приблизиться к тому, что я хочу (или, возможно, именно то, что хочу, нужно больше тестировать). Это связано с пропуском слишком много символов NULL.

int main(void) { 
    int  fd[2], nbytes; 
    pid_t childpidB, childpidC; 
    char readbuffer[100]; 
    readbuffer[0] = '\0'; 
    pipe(fd); 

    if((childpidB = fork()) < 0) 
    { 
     perror("fork A error."); 
     exit(1); 
    } 
    if(childpidB == 0) 
    { 
     close(fd[0]); 
     char AAA[3] = {'A','A','A'}; 
     int j = 0; 
     int k = 1; 
     while (j < 500) { 
    if(j%100==0 && j!=0) { 
     usleep(100); 
    } 
    char numbers[6]; 
    numbers[0] = '\0'; 
    sprintf(numbers,"%03dAAA",k); 
    k++; 
    j++; 
    write(fd[1], numbers, (strlen(numbers))); 
     } 
     close(fd[1]); 
     exit(0); 
    } 
    if((childpidC = fork()) < 0) 
    { 
     perror("fork B error."); 
     exit(1); 
    } 
    if(childpidC == 0) 
    { 
     close(fd[0]); 
     int i = 0; 
     char c = 'A'; 
     int numberafter = 0; 
     while (i < 260) { 
    if(i%60==0 && i!=0) { 
     usleep(300); 
    } 
    char dump[3]; 
    dump[0] = '\0'; 
    if(c=='[') { c='A'; numberafter++; } 
    sprintf(dump,"%cx%d",c,numberafter); 
    c++; 
    write(fd[1], dump, (strlen(dump)));  
    i++; 
     } 
     close(fd[1]); 
     exit(0); 
    } 

    //Process A 
    int x = 0; 
    close(fd[1]); 
    if(childpidB > 0 && childpidC > 0) { 
    while ((nbytes = read(fd[0], readbuffer, sizeof(readbuffer))) > 0) { 
     if(x%50==0 && x!=0) { 
    usleep(200); 
     } 
     printf("%s\n", readbuffer); 
     x++; 
    } 
    } 
    close(fd[0]); 
    exit(0); 
} 
+0

пытаются напечатать количество записанных байтов и читать ... –

ответ

2

Проблема заключается в нулей, что вы посылаете к родителю:

write(fd[1], numbers, (strlen(numbers)+1)); 
... 
write(fd[1], dump, (strlen(dump)+1)); 

Обе записи будет включать нуль характера из-за +1 в выражении после strlen(), следовательно

printf("%s\n", readbuffer); 

остановит вывод данных из readbuffer после сначала null байт.

Родитель фактически получает 100 байт (или меньше, когда оба ребенка отсутствуют).

Таким образом, не посылают аннулирует, удалив +1;)

Edit:

Похоже childpidC будет спать дольше (4 * 300 = 1200 us против 4 * 100 = 400 us). Поэтому, когда childpidB выходит первым, OS отправляет SIGCHLD родительскому лицу, чтобы уведомить его об одном из выходов childs.В тот же момент read() будет прерван и может вернуться менее sizeof(readbuffer) или даже -1 (с ошибкой EINTR).

Вот пример функции для не прерываемого чтения из трубы:

ssize_t read_no_intr(int pipe_fd, void * buffer, size_t size) 
{ 
    if (!size) 
     return 0; 

    ssize_t bytes_read = 0; 
    ssize_t batch; 
    char * p_buffer = static_cast<char*>(buffer); 

    while ((size_t)bytes_read < size) { 
     batch = ::read(pipe_fd, p_buffer, size - bytes_read, 0); 
     if (batch == -1) { 
      if(errno == EINTR) { 
       // Interrupted by a signal, retry. 
       continue; 
      } else if (!bytes_read) { 
       // An error, and no bytes were read so far. 
       return -1; 
      } else { 
       // An error, but something was read. 
       return bytes_read; 
      } 
     } else if(!batch) { 
      // 0 means end of a stream. 
      break; 
     } 
     bytes_read += batch; 
     p_buffer += batch; 
    } 
    return bytes_read; 
} 
+0

Хм да, я что думал сам, и я попытался он без + 1s, и он сделал выход CLOSER, но не совсем то, что я хочу, но спасибо за подтверждение одной из моих мыслей ... Мне нужно провести более обширное тестирование без +1. – Musicode

+0

@Musicode, вы видите, есть несколько случаев, когда read() может возвращать меньше запрошенных, но '> = 0': ** (1) ** неблокирующий файловый дескриптор и некоторые доступные данные; ** (2) ** прерывается «синглом», и некоторые данные были прочитаны до прерывания; ** (3) ** другая сторона трубы была закрыта (в вашем случае: оба дескриптора детей 'fd [1]'); – GreenScape

+0

Я не уверен, что понимаю, что вы имеете в виду. – Musicode

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