父进程和子进程同步

时间:2012-08-14 05:20:46

标签: c

我有一个项目要求我使用signal和fork来读取文件的内容并将其写回到stdout(低级I / O)。我应该在父进程和子进程之间拆分读写工作,我应该首先开始父进程来读取和写入文件的内容。在父完成之后,我应该将信号SIGUSR1发送到子进程,以便它可以开始读取和写入下一个数据块。 我的阅读和写作部分运行正常,但我对信号和处理部分有问题。当我运行我的程序时,它显示正确的输出但它永远不会终止。 有人可以检查我的代码有什么问题吗?

这是我到目前为止所得到的:

#define _XOPEN_SOURCE 600

#include <limits.h> /* For LONG_MAX */
#include <fcntl.h> /*For open()*/
#include <unistd.h> /*For close(), read(), write(), nice()*/
#include <stdlib.h> /*For exit()*/
#include <stdio.h> /*For fprintf(),perror()*/
#include <signal.h> /*For signal()*/
#include <sys/types.h> /* For pid_t */
#include <sys/wait.h> /* For wait() */

void my_handler(int);
void displayUsage(FILE *, const char *);

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

        char c[BUFSIZ]; /* To hold the string that has been read from the file */
        int fd; /* file descriptor */
        int n;
        long int nBytes; /* To hold the number of bytes to read */
        pid_t pid;
        int child_status;

        /* Display usage if user use this process wrong */
        if(argc != 3){
            displayUsage(stderr, argv[0]);
            exit(1);
        }

        /* Convert argv[1] to long - number of bytes to read */
        nBytes = strtol(argv[1], NULL, 10);
        if((nBytes <= 0) || (nBytes > INT_MAX)){
            fprintf(stderr, "Bufferize %s is not a positive integer\n",
             argv[1]);
            exit(2);
        }

        /* Open a file */
        if((fd = open(argv[2],O_RDONLY,0)) < 0){
           perror(argv[2]);
           exit(3);
        }

        /* Attempt to register a signal handler */
        if(signal(SIGUSR1, my_handler) == SIG_ERR){
           perror("Could not set a handler for SIGUSR1");
           exit(4);
        }

        /* lowering the process priority */
        if(nice(40) < 0){
           perror("Cannot lower the priority");
           exit(5);
        }

        /* Create a new process */
        pid = fork();
        if(pid < 0){
           exit(6);
        }
        /* Allocating memory space for string/data to be read from the file
        if(!(c = malloc(nBytes))){
           perror("ERROR");
           exit(4);
        }*/

        if(pid == 0){
           pause();
           /* I'm the child */
           printf("Child's turn!\n");

           /* Read The File */
           while((n = read(fd, c, nBytes)) != 0){
                  /* Write content of 'c' to the stdout */
                  if(write(STDOUT_FILENO, c, n) < 0){
                         perror("Write Error!\n");
                         exit(7);
                  }
           }
           kill(pid,SIGUSR1);

        }
        else{
           /* I'm the parent */
           printf("Parent's turn!\n");
           /* Read The File */
           while((n = read(fd, c, nBytes)) != 0){
                 /* Write content of 'c' to the stdout */
                 if(write(STDOUT_FILENO, c, n) < 0){
                          perror("Write Error!\n");
                          exit(7);
                 }
           }
           /* Reap the child */
           wait(&child_status);
           kill(pid, SIGUSR1);
         }
         pause();

         /* Close the file */
         if(close(fd) < 0){
            perror(argv[2]);
            exit(8);
         }

         exit(0);
}

void displayUsage(FILE *fp, const char *arg){

        fprintf(fp, "Usage: %s n filename\n", arg);
        fprintf(fp, "where\n");
        fprintf(fp, 
            "\tn\tNumber of bytes to read and write in each \"block\".\n");
        fprintf(fp,"\tfilename\tName of file to be catted.\n");
}

void my_handler(int sig){
        /* Re-registering the handler */
        if(signal(SIGUSR1, my_handler) == SIG_ERR){
             perror("Could not set a handler for SIGUSR1");
             exit(4);
        }

        /*if(isParent == 1){
             printf("Parent's turn!\n");
          }
          else{
             printf("Child's turn!\n");
          }*/
}

1 个答案:

答案 0 :(得分:4)

我认为你的问题是你等孩子死了,然后给它发信号去读取文件。

你有:

       /* Reap the child */
       wait(&child_status);
       kill(pid, SIGUSR1);

您可能需要:

       kill(pid, SIGUSR1);
       /* Reap the child */
       wait(&child_status);

问题的另一部分可能是孩子在退出之前到达第二个pause(),但没有一个过程在那时向它发送信号。由于父母仍在等待孩子死亡,但孩子无法退出,直到有人发出信号打破pause(),除了大家等着别人做某事之外,没有什么事情发生。 / p>


并且,经过一些实验(以及更多的诊断输出)后,我得到了父母打印的文件(这是源代码),然后是信息:

Parent sending signal to child
Parent waiting for child
Child (56243) about to pause()

然后,在孩子暂停之前,信号被发送给孩子,因此信号也可能从未被发送过。由于代码是虔诚的,我不太清楚为什么我没有看到孩子信号处理程序的痕迹。


没有什么比吹嘘一个好的简单问题......

这里有一些修改后的代码可以达到预期的效果,尽管你可能会感到有些惊讶:

#define _XOPEN_SOURCE 600

#include <limits.h> /* For LONG_MAX */
#include <fcntl.h> /*For open()*/
#include <unistd.h> /*For close(), read(), write(), nice()*/
#include <stdlib.h> /*For exit()*/
#include <stdio.h> /*For fprintf(),perror()*/
#include <signal.h> /*For signal()*/
#include <sys/types.h> /* For pid_t */
#include <sys/wait.h> /* For wait() */

static volatile sig_atomic_t sigusr1 = 0;

void my_handler(int);
void displayUsage(FILE *, const char *);

int main(int argc, char **argv)
{
    char c[BUFSIZ]; /* To hold the string that has been read from the file */
    int fd; /* file descriptor */
    int n;
    long int nBytes; /* To hold the number of bytes to read */
    pid_t pid;
    int child_status;

    /* Display usage if user use this process wrong */
    if(argc != 3){
        displayUsage(stderr, argv[0]);
        exit(1);
    }

    /* Convert argv[1] to long - number of bytes to read */
    nBytes = strtol(argv[1], NULL, 10);
    if((nBytes <= 0) || (nBytes > INT_MAX)){
        fprintf(stderr, "Bufferize %s is not a positive integer\n",
                argv[1]);
        exit(2);
    }

    /* Open a file */
    if((fd = open(argv[2],O_RDONLY,0)) < 0){
        perror(argv[2]);
        exit(3);
    }

    /* Attempt to register a signal handler */
    if(signal(SIGUSR1, my_handler) == SIG_ERR){
        perror("Could not set a handler for SIGUSR1");
        exit(4);
    }

    /* lowering the process priority */
    if(nice(40) < 0){
        perror("Cannot lower the priority");
        exit(5);
    }

    /* Create a new process */
    pid = fork();
    if(pid < 0){
        perror("fork");
        exit(6);
    }
    /* Allocating memory space for string/data to be read from the file
       if(!(c = malloc(nBytes))){
       perror("ERROR");
       exit(4);
       }*/

    if(pid == 0){
        printf("Child (pid = %d) about to signal self (sigusr1 = %d)\n", (int)getpid(), (int)sigusr1);
        fflush(0);
        kill(pid, SIGUSR1);
        printf("Child (%d) about to pause() (sigusr1 = %d)\n", (int)getpid(), (int)sigusr1);
        fflush(0);
        pause();

        /* I'm the child */
        printf("Child's turn %d!\n", (int)getpid());

        /* Read The File */
        while((n = read(fd, c, nBytes)) != 0){
            /* Write content of 'c' to the stdout */
            if(write(STDOUT_FILENO, c, n) < 0){
                perror("Write Error!\n");
                exit(7);
            }
        }
        printf("Child signalling parent\n");
        kill(pid,SIGUSR1);
        printf("Child exiting (status = 37)\n");
        exit(37);
    }
    else{
        /* I'm the parent */
        printf("Parent's turn! (pid = %d, kid = %d)\n", (int)getpid(), (int)pid);
        /* Read The File */
        while((n = read(fd, c, nBytes)) != 0){
            /* Write content of 'c' to the stdout */
            if(write(STDOUT_FILENO, c, n) < 0){
                perror("Write Error!\n");
                exit(7);
            }
        }
        printf("Parent sleeping\n");
        sleep(1);
        printf("Parent sending signal to child\n");
        fflush(0);
        kill(pid, SIGUSR1);
        printf("Parent waiting for child\n");
        fflush(0);
        /* Reap the child */
        int corpse = wait(&child_status);
        printf("waiting over: pid = %d, status = 0x%.4X\n", corpse, child_status);
    }

    printf("%d skipping final pause\n", (int)getpid());
    /*pause();*/

    /* Close the file */
    if(close(fd) < 0){
        perror(argv[2]);
        exit(8);
    }

    printf("%d exiting\n", (int)getpid());
    return(0);
}

void displayUsage(FILE *fp, const char *arg)
{
        fprintf(fp, "Usage: %s n filename\n", arg);
        fprintf(fp, "where\n");
        fprintf(fp, 
            "\tn\tNumber of bytes to read and write in each \"block\".\n");
        fprintf(fp,"\tfilename\tName of file to be catted.\n");
}

void my_handler(int sig)
{
    /* Re-registering the handler */
    if(signal(sig, my_handler) == SIG_ERR){
        perror("Could not set a handler for SIGUSR1");
        exit(4);
    }
    sigusr1 = 1;
    printf("In signal handler (pid %d)\n", (int)getpid());
    fflush(0);
}

示例输出:

$ ./doze 20 file.1
Parent's turn! (pid = 56459, kid = 56460)
0.0
Parent sleeping
Child (pid = 56460) about to signal self (sigusr1 = 0)
In signal handler (pid 56460)
Child (56460) about to pause() (sigusr1 = 1)
In signal handler (pid 56459)
Parent sending signal to child
Parent waiting for child
In signal handler (pid 56460)
Child's turn 56460!
Child signalling parent
In signal handler (pid 56460)
Child exiting (status = 37)
In signal handler (pid 56459)
waiting over: pid = 56460, status = 0x2500
56459 skipping final pause
56459 exiting
$

数据文件file.1包含一行包含0.0(非常不引人注目)。如您所见,父级正确复制文件。现在,感谢sleep(1),孩子有机会做事,并从信号处理程序中醒来。它将文件的其余部分复制到标准输出...

那是什么 - 你看不出孩子复制了什么?啊,好吧......

这两个进程共享一个打开的文件描述,文件中的当前位置由打开的文件描述中的位置控制。因此,当父级读完文件时,文件处于EOF,因此子级的第一个read()为零字节。您需要执行lseek(fd, 0L, SEEK_SET);才能让它再次读取文件。

因此,孩子复制数据,然后发信号通知父母,退出状态37.您可以看到父响应,依此类推,直到退出。所以,正如编辑,它的工作原理。你有太多的停顿。我不确定sleep()是否必要,但我怀疑它是。


详细信息:消息Child signalling parent是不准确的信息;那是孩子发信号的本身。我认为没有必要。实际上,这里是“相同”的代码,其中包含40行:

#define _XOPEN_SOURCE 600

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

void my_handler(int);
void displayUsage(FILE *, const char *);

int main(int argc, char **argv)
{
    char c[BUFSIZ];
    int fd;
    int n;
    long int nBytes;
    pid_t pid;
    int child_status;

    if(argc != 3){
        displayUsage(stderr, argv[0]);
        exit(1);
    }

    nBytes = strtol(argv[1], NULL, 10);
    if((nBytes <= 0) || (nBytes > BUFSIZ)){
        fprintf(stderr, "Buffer size %s is not a positive integer between 1 and %d\n",
                argv[1], BUFSIZ);
        exit(2);
    }

    if((fd = open(argv[2], O_RDONLY, 0)) < 0){
        perror(argv[2]);
        exit(3);
    }

    if(signal(SIGUSR1, my_handler) == SIG_ERR){
        perror("Could not set a handler for SIGUSR1");
        exit(4);
    }

    pid = fork();
    if(pid < 0){
        perror("fork");
        exit(6);
    }

    if(pid == 0){
        printf("Child (%d) about to pause()\n", (int)getpid());
        pause();
        printf("Child's turn %d!\n", (int)getpid());
        lseek(fd, 0L, SEEK_SET);
        while((n = read(fd, c, nBytes)) != 0){
            if(write(STDOUT_FILENO, c, n) < 0){
                perror("Write Error!\n");
                exit(7);
            }
        }
        int rc = 37;
        printf("Child exiting (status = %d (0x%.2X))\n", rc, rc);
        exit(rc);
    }
    else{
        printf("Parent's turn! (pid = %d, kid = %d)\n", (int)getpid(), (int)pid);
        while((n = read(fd, c, nBytes)) != 0){
            if(write(STDOUT_FILENO, c, n) < 0){
                perror("Write Error!\n");
                exit(7);
            }
        }
        printf("Parent sleeping\n");
        sleep(1);
        printf("Parent sending signal to child\n");
        kill(pid, SIGUSR1);
        printf("Parent waiting for child\n");
        int corpse = wait(&child_status);
        printf("waiting over: pid = %d, status = 0x%.4X\n", corpse, child_status);
    }

    if(close(fd) < 0){
        perror(argv[2]);
        exit(8);
    }

    printf("%d exiting\n", (int)getpid());
    return(0);
}

void displayUsage(FILE *fp, const char *arg)
{
        fprintf(fp, "Usage: %s n filename\n", arg);
        fprintf(fp, "where\n");
        fprintf(fp, "\tn\tNumber of bytes to read and write in each \"block\".\n");
        fprintf(fp, "\tfilename\tName of file to be catted.\n");
}

void my_handler(int sig)
{
    if(signal(sig, my_handler) == SIG_ERR){
        perror("Could not set a handler for SIGUSR1");
        exit(4);
    }
    printf("In signal handler (pid %d)\n", (int)getpid());
}

示例输出:

$ ./doze 20 file.1
Parent's turn! (pid = 56651, kid = 56652)
0.0
Parent sleeping
Child (56652) about to pause()
Parent sending signal to child
Parent waiting for child
In signal handler (pid 56652)
Child's turn 56652!
0.0
Child exiting (status = 37 (0x25))
waiting over: pid = 56652, status = 0x2500
56651 exiting
$

当我删除sleep()时,该程序再次挂起;在孩子进入pause()之前,父母已将信号发送给孩子。