与 C 信号的简单同步 [英] Simple synchronization with C signals

查看:46
本文介绍了与 C 信号的简单同步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试解决一个要求:启动过程必须分叉两次.父亲和孩子必须同步写入,一个接一个地,在读取写入字符的临时文件的第一个位置三个不同的文件(每个进程一个).程序必须使用信号来实现同步机制."

I'm trying to solve an exercise which requires that : "the starting process must fork two times. The father and the children must synchronize to write, one after another, in the first position of a temporary file reading the characters written on three different files (one for each process). The program must use signals to implement the synchronization mechanism."

到目前为止,我已经尝试通过这样做来解决这个问题:

So far i've tried to solve this by doing so :

  • P1(父亲)首先开始读/写.在停止自己之前(通过调用 raise 函数),他发送一个 SIGCONT 信号来唤醒 F2(第二个孩子)
  • F2 从他的文件中读取并写入临时文件.然后他自己也停下来,并发送一个 SIGCONT 信号来唤醒 F1(第一个孩子)
  • F1 的作用与 F2 相同,但会唤醒 P1 等等......

但是,我无法使代码工作(在某些情况下,在更改读取和写入的顺序后,我在输出中获得了大部分后者,但程序行为始终不稳定且从未终止).

However, i can't get the code working (in some cases, after changing the order of the readings and writings, i got most of the latter in output but the program behavior was always erratic and never terminated).

这是我的代码:

#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);
}

推荐答案

更好的方法是使用 sigwait() 函数,并将信号掩码设置为您想要的信号等待.首先,在使用 sigwait() 之前,您应该确保您正在等待的信号首先被阻塞在进程或线程的信号掩码中.然后,执行以下操作:

A better way to-do this would be to utilize the sigwait() function with a signal mask set to the signal you want to wait on. First, before you can use sigwait(), you should make sure that the signal you are waiting on is first blocked in the signal mask of the process or thread. Then, do the following:

  1. 在父级中,打开您要从中读取和写入的文件的所有适当的文件描述符
  2. 设置父进程的信号掩码,以便阻止将用于父进程和子进程之间同步的信号.
  3. fork 子进程.设置处理后,在 while 循环中,使用仅包含同步信号的信号掩码调用 sigwait().当该子进程接收到同步信号时,它将继续通过 while 循环.在完成循环并重复对 sigwait() 的阻塞调用之前,向下一个进程发送信号.
  4. 在父进程中,使用while循环执行第一个读/写序列,然后向下一个子进程发送信号.最后,在循环结束时调用 sigwait().
  1. In the parent, open all the appropriate file descriptors for the files you're going to be reading and writing from
  2. Set the signal mask of the parent process so that you're blocking the signal you're going to be using for synchronization between the parent and child processes.
  3. Fork the child processes. After setting up the processing, in a while-loop, call sigwait() with a signal mask that only includes the synchronization signal. When the synchronization signal is received by that child process, it will continue through the while-loop. Before completing the loop and repeating the blocking call to sigwait(), send a signal to the next process.
  4. In the parent process, using a while-loop do the first read/write sequence, and then send a signal to the next child process. Finally, call sigwait() at the end of the loop.

所以最终你的子进程看起来像:

So in the end your child processes would look something like:

//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;
    }
}   

这篇关于与 C 信号的简单同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆