0

我有一个项目需要我使用信号和叉来读取文件的内容并将其写回标准输出(低级 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");
          }*/
}
4

1 回答 1

4

我认为您的问题是您要等待孩子死去,然后再给它发出去读取文件的信号。

你有:

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

你可能需要:

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

您问题的另一部分可能是孩子pause()在退出之前到达第二个,但此时没有进程向它发送信号。而且由于父母仍在等待孩子死去,但孩子无法退出,直到有人向它发送信号以破坏pause(),所以除了每个人都在等待其他人做某事外,没有什么事情发生。


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

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()

于 2012-08-14T05:27:04.943 回答