简体   繁体   English

消除潜在的赛车状况

[英]Eliminating a potential racing condition

I am supposed to measure the latency and bandwidth between two processes.我应该测量两个进程之间的延迟和带宽。 To do this, I wrote a simple program in C using pipe and fork.为此,我使用 pipe 和 fork 在 C 中编写了一个简单的程序。 To see that everything works, I wanted the parent process and the child process to work alternately.为确保一切正常,我希望父进程和子进程交替工作。 So I wanted the parent process to stop after writing and continue after the child process read and printed the string with 'a'.所以我希望父进程在写入后停止并在子进程读取并打印带有'a'的字符串后继续。

I used kill() and pause() along with sleep.我使用 kill() 和 pause() 以及睡眠。 My problem is that from time to time the program terminates itself and in other cases it works fine because it may not hit the race condition, I suppose.我的问题是程序有时会自行终止,而在其他情况下它运行良好,因为我想它可能没有达到竞争条件。

I would be very grateful for another way to deal with this.如果有另一种方法来处理这个问题,我将不胜感激。 If there is any information missing, please tell me and don't just dwonvote.如果有任何信息遗漏,请告诉我,不要只是 dwonvote。

Up to now I have the following code:到目前为止,我有以下代码:


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

#define DATASIZE 5

int count = 0;

double diff = 0;

void wakeup_handler(int sig)
{
    // Do nothing
}

int main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t pid;
    int i, n;

    double total_time = 0;
    struct timeval t1, t2;

    if (pipe(pipefd) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid = fork();

    if (pid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    // Child Process
    if (pid == 0)
    {
        
        close(pipefd[0]);

        char tmp1[DATASIZE];

        // Set tmp to 'a'

        for (i = 0; i < DATASIZE; i++)
        {
            tmp1[i] = 'a';
        }

        gettimeofday(&t1, NULL);

        signal(SIGUSR1, wakeup_handler);

        for (int i = 0; i < 1000; i++) {
            write(pipefd[1], tmp1, sizeof(tmp1));
            pause();
            count++;
            printf("CountC: %d\n", count);
        }

        gettimeofday(&t2, NULL);
        diff = (t2.tv_sec - t1.tv_sec);          // sec to ms
        diff += (t2.tv_usec - t1.tv_usec) / 1e6; // us to sec
        
        //Correction of 10 seconds because of the 10 seconds sleep in the parent process
        
        // diff+= diff - 10;

        double band = (DATASIZE * 1000) / diff;
        printf("Average latency: %lf seconds\n", diff / 1000);
        printf("Average bandwidth: %lf Mbps\n", band / 1e6);
        close(pipefd[1]);

    }
    // Parent Process
    else
    {
        close(pipefd[1]);

        char tmp2[DATASIZE];

        for (int i = 0; i < 1000; i++) {
            // printf("Parent: Iteration %d...\n", i+1);
            read(pipefd[0], tmp2, sizeof(tmp2));
            printf("%s\n", tmp2);
            sleep(0.01);
            kill(pid, SIGUSR1);
        }

        close(pipefd[0]);
    }

    return 0;
}

To avoid the race condition where a signal may be delivered before pause has executed, instead of "buying more time" by calling sleep in the parent process, sigprocmask may be used to block the delivery of signals in the child process, delaying their arrival until they are later unblocked .为了避免在pause执行之前可能传递信号的竞争条件,而不是通过在父进程中调用sleep“购买更多时间” ,可以使用sigprocmask阻止子进程中的信号传递,延迟它们的到达直到他们后来畅通无阻 A delayed signal is said to be pending .延迟信号被称为未决

sigsuspend can be used to temporarily unblock signals (by changing the signal mask) while it waits for a signal to arrive. sigsuspend可用于在等待信号到达时临时解锁信号(通过更改信号掩码)。

To avoid the race condition where the parent process sends the data-request signal before the child process has finished establishing its signal handling, the pipe can be used to transmit some initial data.为了避免父进程在子进程完成建立其信号处理之前发送数据请求信号的竞争条件,pipe 可用于传输一些初始数据。 By executing a blocking read in the parent process, the child process can indicate it is ready with an initial write that unblocks the parent.通过在父进程中执行阻塞read ,子进程可以指示它已准备好进行解除阻塞父进程的初始write

When the work is complete, the parent process should wait for the child process to terminate.当工作完成后,父进程应该wait子进程终止。

For significantly improved portability, prefer sigaction to signal .为了显着提高可移植性,更喜欢sigaction而不是signal

Here is a cursory refactoring of your example.这是您示例的粗略重构。 However, for the sake of brevity, it has no error handling.但是,为了简洁起见,它没有错误处理。

Do note the blocking nature of write and read help create a lockstep here, ensuring the parity of iterations between processes.请注意writeread的阻塞性质有助于在此处创建一个锁步,确保进程之间的迭代对等。 Otherwise, calling kill N times does not guarantee N writes to the pipe, as a to-be-delivered signal would be dropped if it is already pending .否则,调用kill N次并不能保证对 pipe 进行N次写入,因为如果待交付信号已经处于pending状态,它将被丢弃。 An important part of what makes this synchronization work is that the parent process immediately waits for the data it requests.使这种同步工作的一个重要部分是父进程立即等待它请求的数据。

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

struct packet {
    char buffer[32];
};

void wakeup_handler(int sig)
{
    /* Do nothing */
    (void) sig;
}

int main(void)
{
    /* each process receives a copy of the number of tests to run */
    unsigned cycles = 1000;
    int pipefd[2];

    pipe(pipefd);
    pid_t pid = fork();

    if (pid == 0) {
        /* Child process */
        close(pipefd[0]);

        struct sigaction sa = { .sa_handler = wakeup_handler };
        sigaction(SIGUSR1, &sa, NULL);

        sigset_t mask, original;

        sigemptyset(&mask);
        sigaddset(&mask, SIGUSR1);
        /* block (delay) delivery of the SIGUSR1 signal */
        sigprocmask(SIG_BLOCK, &mask, &original);

        /* write some initial data
         * letting the parent know the child has finished
         * setting up its signal handling
         */
        int ready = 1;
        write(pipefd[1], &ready, sizeof ready);

        struct timeval t1, t2;
        int count = 0;
        double diff = 0;

        gettimeofday(&t1, NULL);

        while (cycles--) {
            struct packet output = { "aaaaa" };

            /* temporary restore the original signal mask
             * and wait for a signal to arrive
             * (including previously delayed signals)
             */
            sigsuspend(&original);

            write(pipefd[1], &output, sizeof output);
            printf("CountC: %d\n", ++count);
        }

        gettimeofday(&t2, NULL);

        diff = (t2.tv_sec - t1.tv_sec);          // sec to ms
        diff += (t2.tv_usec - t1.tv_usec) / 1e6; // us to sec
        double band = (sizeof (struct packet) * 1000) / diff;
        printf("Average latency: %lf seconds\n", diff / 1000);
        printf("Average bandwidth: %lf Mbps\n", band / 1e6);
        close(pipefd[1]);
        /* ensure child does not run off */
        exit(0);
    }

    /* Parent process */
    close(pipefd[1]);

    /* block execution in the parent process until
     * some initial data arrives indicating the child process
     * is ready to receive signals
     */
    int initialized;
    read(pipefd[0], &initialized, sizeof initialized);

    while (cycles--) {
        struct packet input;

        kill(pid, SIGUSR1);
        read(pipefd[0], &input, sizeof input);
        printf("%.*s\n", (int) sizeof input.buffer, input.buffer);
    }

    close(pipefd[0]);

    /* wait for the child process to terminate */
    wait(NULL);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM