Простая синхронизация с сигналами C - PullRequest
1 голос
/ 02 января 2012

Я пытаюсь выполнить упражнение, которое требует, чтобы: «процесс запуска должен быть разветвлен два раза. Отец и дети должны синхронизироваться, чтобы записывать одну за другой в первую позицию временного файла, читая написанные символы в трех разных файлах (по одному для каждого процесса). Программа должна использовать сигналы для реализации механизма синхронизации. "

До сих пор я пытался решить эту проблему следующим образом:

  • P1 (отец) начинает читать / писать первым. Перед тем как остановиться (посредством вызова функции рейза), он посылает сигнал SIGCONT, чтобы разбудить F2 (второго ребенка)
  • F2 читает из своего файла и пишет во временный файл. Затем он тоже останавливается и посылает сигнал SIGCONT, чтобы разбудить F1 (первого ребенка)
  • F1 делает то же самое, что и F2, но активирует P1 и т. Д. *

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

Вот мой код:

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

#define TEMP_PATH "/tmp/mytempfile"

int main(int argc, char *argv[]){

FILE *writeFp;
FILE *rfpF1;
FILE *rfpF2;
FILE *rfpP1;
pid_t pid1, pid2;

char car;   
char sizeOfChar = sizeof(char);

if (argc != 4 || !strcmp(argv[1], "--help")){
    fprintf(stderr, "Usage : %s filePath1 filePath2 filePath3\n", argv[0]);
    exit(EXIT_FAILURE);
}

if (access(argv[1], F_OK)==-1){
    perror("access 1 error");
    exit(EXIT_FAILURE);
}

if (access(argv[2], F_OK)==-1){
    perror("access 2 error");
    exit(EXIT_FAILURE);
}

if (access(argv[3], F_OK)==-1){
    perror("access 3 error");
    exit(EXIT_FAILURE);
}

if((writeFp = fopen(TEMP_PATH, "w")) == NULL){
    fprintf(stderr, "Can't open temp file on writing.\n");
    exit(EXIT_FAILURE);
}

if ((rfpP1 = fopen(argv[3], "r")) == NULL){
    fprintf(stderr, "Can't open %s on reading.\n", argv[3]);
    exit(EXIT_FAILURE);
}   

switch(pid1 = fork()){
    case -1:
            perror("fork error");
            exit(EXIT_FAILURE);

    case 0:
            /* F1 : first child */

            if ((rfpF1 = fopen(argv[1], "r")) == NULL){
                fprintf(stderr, "Can't open %s on reading.\n", argv[1]);
                exit(EXIT_FAILURE);
            }

            raise(SIGSTOP);
            while(fscanf(rfpF1, "%c", &car) != EOF){

                if(fseek(writeFp, 0L, SEEK_SET) == -1){
                    perror("fseek error");
                    exit(EXIT_FAILURE);
                }
                if(fprintf(writeFp, "%c", car) != 1){
                    fprintf(stderr, "fprintf error. Terminating...\n");
                    exit(EXIT_FAILURE);
                }                   

                if(kill(getppid(), SIGCONT) == -1){
                    perror("F1 kill error");
                    exit(EXIT_FAILURE);
                }

                printf("F1 : i've written '%c'\n", car); fflush(stdout);

                // If with the next read EOF is reached, the process doesn't have to stop...
                if(fscanf(rfpF1, "%c", &car) == EOF)
                    break;
                else{
                    if(fseek(rfpF1, -sizeOfChar, SEEK_CUR)){
                        perror("fseek error");
                        exit(EXIT_FAILURE);
                    }
                    raise(SIGSTOP);
                }
            }

            fclose(rfpF1);              
            exit(EXIT_SUCCESS);


    default :
            break;  

}


switch(pid2 = fork()){
    case -1:
            perror("fork 2 error");
            exit(EXIT_FAILURE);

    case 0:
            /* F2 : second child */

            if ((rfpF2 = fopen(argv[2], "r")) == NULL){
                fprintf(stderr, "Can't open %s on reading.\n", argv[2]);
                exit(EXIT_FAILURE);
            }

            raise(SIGSTOP);
            while(fscanf(rfpF2, "%c", &car) != EOF){

                if(fseek(writeFp, 0L, SEEK_SET) == -1){
                    perror("fseek error");
                    exit(EXIT_FAILURE);
                }
                if(fprintf(writeFp, "%c", car) != 1){
                    fprintf(stderr, "fprintf error. Terminating...\n");
                    exit(EXIT_FAILURE);
                }               

                if(kill(pid1, SIGCONT) == -1){
                    perror("F2 kill error");
                    exit(EXIT_FAILURE);
                }

                printf("F2 : i've written '%c'\n", car); fflush(stdout);

                if(fscanf(rfpF2, "%c", &car) == EOF)
                    break;
                else{
                    if(fseek(rfpF2, -sizeOfChar, SEEK_CUR)){
                        perror("fseek error");
                        exit(EXIT_FAILURE);
                    }
                    raise(SIGSTOP);
                }
            }

            fclose(rfpF2);  
            exit(EXIT_SUCCESS);
    default:    
            /* P1 : Father */

            // Wait for the children to be interrupted by SIGSTOP (which changes their states)
            if(wait(NULL) == -1){
                perror("wait 1 error");
                exit(EXIT_FAILURE);
            }

            if(wait(NULL) == -1){
                perror("wait 2 error");
                exit(EXIT_FAILURE);
            }

            // P1 is the first to be reading and writing...
            while(fscanf(rfpP1, "%c", &car) != EOF){

                if(fseek(writeFp, 0L, SEEK_SET) == -1){
                    perror("fseek error");
                    exit(EXIT_FAILURE);
                }
                if(fprintf(writeFp, "%c", car) != 1){
                    fprintf(stderr, "fprintf error. Terminating...\n");
                    exit(EXIT_FAILURE);
                }

                if(kill(pid2, SIGCONT) == -1){
                    perror("P kill error");
                    exit(EXIT_FAILURE);
                }

                printf("P1 : i've written '%c'\n", car); fflush(stdout);

                if(fscanf(rfpP1, "%c", &car) == EOF)
                    break;
                else{
                    if(fseek(rfpP1, -sizeOfChar, SEEK_CUR)){
                        perror("fseek error");
                        exit(EXIT_FAILURE);
                    }
                    raise(SIGSTOP);
                }

            }

            fclose(rfpP1);  
            break;
}

// Wait for the children...
if(wait(NULL) == -1){
    perror("wait 1 error");
    exit(EXIT_FAILURE);
}

if(wait(NULL) == -1){
    perror("wait 2 error");
    exit(EXIT_FAILURE);
}

fclose(writeFp);
exit(EXIT_SUCCESS);
}

1 Ответ

2 голосов
/ 02 января 2012

Лучший способ сделать это - использовать функцию sigwait() с маской сигнала, установленной на сигнал, который вы хотите ждать.Во-первых, прежде чем вы сможете использовать sigwait(), вы должны убедиться, что ожидаемый сигнал сначала заблокирован в маске сигналов процесса или потока.Затем выполните следующее:

  1. В родительском файле откройте все соответствующие файловые дескрипторы для файлов, с которых вы собираетесь читать и записывать
  2. Установите маску сигнала дляродительский процесс, поэтому вы блокируете сигнал, который собираетесь использовать для синхронизации между родительским и дочерним процессами.
  3. Разветвите дочерние процессы.После настройки обработки в цикле while вызовите sigwait() с маской сигнала, которая включает только сигнал синхронизации.Когда этот дочерний процесс получит сигнал синхронизации, он продолжит цикл while.Перед завершением цикла и повторением вызова блокировки на sigwait() отправьте сигнал следующему процессу.
  4. В родительском процессе с помощью цикла while выполните первую последовательность чтения / записи, а затем отправьтесигнал для следующего дочернего процесса.Наконец, вызовите sigwait() в конце цикла.

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

//child process

//...setup the child process

while (/* some condition for stopping */)
{
    int signal
    sigwait(&signal_mask, &signal)

    //check to make sure we're getting the right signal
    if (signal != synchronization_signal)
        continue;

    //...more code for reading/writing to files

    //send a signal to next process in-line
    //i.e., F1 will send a signal to F2, and F2 will signal P1    
}

и ваш родительский процесс будет выглядеть так:

//...block the synchronization signal and fork the children

while (/* some condition for stopping */)
{
    //...perform the reading and writing to the files

    //send signal to F1

    //block waiting for the signal to arrive from F2
    while (true)
    {
        int signal;
        sigwait(&signal_mask, &signal);

        //check to make sure we're getting the right signal
        if (signal == synchronization_signal)
            break;
    }
}   
...