@si1n3rd

Как синхронизировать дочерние процессы?

Доброго времени суток!

Лазил я по форумам и наткнулся на задачку. Необходимо при помощи двух дочерних процессов читать и выводить в терминал содержимое файла. Сначала первый процесс считывает 10 байт, потом второй - следующие 10 байт. И так до конца. В программирование не особо силен, но при помощи System V семафоров, вроде как написал что-то работающее. Но возникает проблема, файл иногда печатается не совсем корректно, некоторые фрагменты документа теряются. Если считывать файл побайтово, то все корректно. При изменении размера буфера, в который считывается фрагмент файла, иногда возникают выше описанные проблемы. Хотелось бы понять в чем причина. Код под спойлером.
Код
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/sem.h>
 
#define SIZE 10
 
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
#if defined(__linux__)
    struct seminfo *__buf;
#endif
};
 
int main(void)
{
    int fd;
    int ch;
    int pid[2];
    int status[2];
    int semaphore;
    int semval;
    int len;
    union semun arg;
    char buff[SIZE + 1];
 
    setbuf(stdout, NULL);
    memset(buff, 0, SIZE + 1);
 
    fd = open("data", O_RDONLY);
    
    semaphore = semget(IPC_PRIVATE, 1, 0);
    if(semaphore == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }
 
    arg.val = 1;
    if(semctl(semaphore, 0, SETVAL, arg) == -1) {
        perror("semctl");
        exit(EXIT_FAILURE);
    }
    
    pid[0] = fork();
    if(!pid[0]) {
        while(1) {
            semval = semctl(semaphore, 0, GETVAL);
            if(semval != 1)
                continue;
 
            memset(buff, 0, SIZE + 1);
            len = read(fd, buff, SIZE);
            if(!len) {
                arg.val = 2;
                semctl(semaphore, 0, SETVAL, arg);
                break;
            }
            else if(len > 0) {
                printf("%s", buff);
                arg.val = 1;
                semctl(semaphore, 0, SETVAL, arg);
            }
            else if(len == -1) {
                perror("read");
                break;
            }
        }
 
        exit(EXIT_SUCCESS);
    }
    else if(pid[0] == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
 
    pid[1] = fork();
    if(!pid[1]) {
        while(1) {
            semval = semctl(semaphore, 0, GETVAL);
            if(semval != 2)
                continue;
 
            memset(buff, 0, SIZE + 1);
            len = read(fd, buff, SIZE);
            if(!len) {
                arg.val = 2;
                semctl(semaphore, 0, SETVAL, arg);
                break;
            }
            else if(len > 0) {
                printf("%s", buff);
                arg.val = 2;
                semctl(semaphore, 0, SETVAL, arg);
            }
            else if(len == -1) {
                perror("read");
                break;
            }
        }
 
        exit(EXIT_SUCCESS);
    }
    else if(pid[1] == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
 
    while(1) {
        waitpid(pid[0], &status[0], 0);
        waitpid(pid[1], &status[1], 0);
 
        if(WIFEXITED(status[0]) && WIFEXITED(status[1]))
                break;
    }
 
    exit(EXIT_SUCCESS);
}


Буду благодарен за любую помощь и критику :)

UPD. Проблема была в мусоре, который находился в буфере. Код обновил.
  • Вопрос задан
  • 94 просмотра
Решения вопроса 1
  • leahch
    @leahch
    Я мастер на все руки, я козлик Элек Мэк :-)
    Вам нужно проверять количество реально прочитанных байт, так как read может прерываться сигналами.
    len = read(fd, buff, SIZE);

    man read

    On success, the number of bytes read is returned (zero indicates end of
    file), and the file position is advanced by this number. It is not an
    error if this number is smaller than the number of bytes requested;
    this may happen for example because fewer bytes are actually available
    right now (maybe because we were close to end-of-file, or because we
    are reading from a pipe, or from a terminal), or because read() was
    interrupted by a signal. See also NOTES.

    Да и что-тот работа с семафорами мне не очень нравится... Я привык через
    1) shmget - получить общую память для семафора
    2) sem_init - проинициализировать семафор
    Для каждого процесса
    3) sem_wait/sem_timedwait - ожидаем семафора
    4) sem_post - отпускаем семафор
    Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы