Linux-C: чтение из канала возвращает первый записанный в него буфер

Эта программа моделирует вариант проблемы производителя/потребителя Дейкстры. Сначала создается конвейер, за которым следует дочерний процесс с помощью fork(). Затем ребенок запишет в канал грубо сделанную случайно сгенерированную часть "информации о биржевом тикере". Подождав, пока процесс-потомок/производитель запишет эту информацию, процесс-родитель/потребитель прочитает ее.

Первый вывод правильный:

Produced: FPOO 57.83 +0.43
Consumed: FPOO 57.83 +0.43

Однако в любом последующем выводе всегда будет указано «Использовано: информация из первого чтения»:

Produced: RJII 71.30 -2.71
Consumed: FPOO 57.83 +0.43

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

Я скомпилировал код в g++. Он принимает в качестве аргумента количество секунд, в течение которых вы хотите, чтобы программа работала.

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

// generate info for each stock entry
void generateTickerInfo(char * info) {
    int i;
    int randNum = 0;
    srand(time(NULL)); // generate seed for random

    // generate 4 characters for STOCK sym
    for(i = 0; i < 4; i++) {
        randNum = rand() % 26 + 65;
        info[i] = (char)(randNum);
    }
    info[4] = ' ';

    // generate price traded
    for(i = 5; i < 7; i++) {
        randNum = rand() % 8 + 1;
        info[i] = '0' + randNum;
    }
    info[7] = '.';
    for(i = 8; i < 10; i++) {
        randNum = rand() % 9;
        info[i] = '0' + randNum;
    }
    info[10] = ' ';

    // determine if + or - for change amount
    randNum = rand();
    if(randNum % 2 == 1) {
        info[11] = '+';
    }
    else {
        info[11] = '-';
    }

    // generate change amount
    randNum = rand() % 9;
    info[12] = '0' + randNum;
    info[13] = '.';
    for(i = 14; i < 16; i++) {
        randNum = rand() % 9;
        info[i] = '0' + randNum;
    }
}

// ** constant and global variables **
const int BUFFER_SIZE = 25;

// ** main code **
int main(int argc, char *argv[]) {
    pid_t cpid; // child process id
    int myPipe[2]; // [0] read, [1] write
    char * tickerInfo; // hold current tickerInfo
    tickerInfo = new char[BUFFER_SIZE]; // info passed through pipe
    char buf[BUFFER_SIZE];
    time_t currentTime, stopTime;

    // initialize time variables
    if(argc < 2) {
        printf("Invalid arg. Type as 'foo.out (# seconds)'");
        exit(0);
    }
    else {
        currentTime = time(NULL);
        stopTime = time(NULL) + (time_t)atoi(argv[1]);
    }

    int pipeReturn = pipe(myPipe);

    if(pipeReturn == -1) { // handle pipe creation error
        perror("pipe error...");
        exit(0);
    }   

// main loop; continue until desired time has elapsed
    while(currentTime < stopTime) {             
        cpid = fork();

        if(cpid < 0) { // handle process creation error
            perror("forking error...\n");
            exit(0);
        }
        else if(cpid == 0) { // child process
            close(myPipe[0]); // child does not need to read
            generateTickerInfo(tickerInfo);     
            write(myPipe[1], tickerInfo, BUFFER_SIZE); 
            printf("Produced: %s\n", tickerInfo);
            exit(0);
        }
        else if(cpid > 0) { // parent process
            wait(0);
            close(myPipe[1]); // parent does not need to write
            read(myPipe[0], buf, BUFFER_SIZE);
            printf("Consumed: %s\n", buf);
        }
        sleep(1);
        currentTime = time(NULL);
    }
    return 0;
}

person mccoyrjm    schedule 09.04.2015    source источник
comment
Проверьте возвращаемое значение read(). Возможно, ему не удалось ничего прочитать, и поэтому он печатает текущее значение, хранящееся в buf. Хорошей практикой программирования всегда является инициализация буфера пустой строкой. Кроме того, родительский процесс никогда не будет повторно открывать канал, поэтому успешное завершение закрытия (myPipe[1]) ничего не делает.   -  person alvits    schedule 09.04.2015


Ответы (1)


После того, как первый дочерний процесс завершен и вы закрываете конец канала записи в родительском процессе, вы возвращаетесь к началу цикла и создаете еще один дочерний процесс. Этот ребенок не наследует запись fd. Запись fd исчезла. write во втором дочернем элементе терпит неудачу, но вы не проверили возвращаемое значение на наличие ошибки, поэтому не замечаете.

Затем родитель считывает EOF из канала, так как не осталось писателей. Поскольку вы также не проверили возвращаемое значение read (у вас плохая привычка!), вы этого не замечаете и просто печатаете буфер, который все еще содержит свое предыдущее содержимое.

person Community    schedule 09.04.2015
comment
Конечно, не хочу развивать какие-либо вредные привычки, спасибо, что указали на них! Это для моего класса по операционным системам, поэтому я все еще пытаюсь понять вилки и пайпы. Я переместил начало цикла while выше, где я впервые создал канал, и теперь он работает так, как предполагалось. Итак, когда вы говорите проверить возвращаемое значение, вы имеете в виду, что функции read() и write() возвращают значение, которое я могу проверить на наличие ошибки? - person mccoyrjm; 09.04.2015