检查管道是否为空,UNIX

时间:2015-12-19 18:29:40

标签: c unix ipc

我有2个进程,父进程和子进程。父进程应该等待用户插入一些字符串然后它应该通过管道将此字符串发送到子进程。 我已成功完成此操作,但我想要实现的是检查管道是否为空,如果它是空的60秒,那么子进程应该向其父进程发送信号。

我的代码到目前为止:

int main(void)
{
        int     fd[2], nbytes;
        pid_t   childpid;
        char    string[100];
        char    readbuffer[80];
        pipe(fd);

        if((childpid = fork()) == -1)
        {
                perror("fork");
                exit(1);
        }
        else if(childpid > 0) //parent process
        {
                close(fd[0]);
                printf ("Insert a string: ");
                scanf("%s",string);
                /* Send "string" through the output side of pipe */
                write(fd[1], string, (strlen(string)+1));
                exit(0);
        }
        else // child process
        {
                close(fd[1]);
                // check if pipe is empty for 60 seconds 
                nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
                printf("Received string: %s", readbuffer);
        }
        return(0);
} 

1 个答案:

答案 0 :(得分:1)

这是一个示例,其中父sleep()为随机秒数,然后write()通过管道传递给孩子。如果孩子在5.5秒内没有收到消息,它会向父节点发送SIGUSR1,而父节点不会写消息。这会重复指定的次数。

请注意,孩子等待5.5秒而不是5秒,因为我们在父母中使用sleep()需要整整秒。这(通常)将留下足够的时间间隔,以避免父sleep()和孩子等待5秒,并且他们的电线在中间交叉的情况。我们可以在父节点中使用nanosleep(),让孩子等待整整几秒钟。

注意:

  1. 我们可以在子进程中使用select()超时,等待指定的时间。 select()将返回就绪文件描述符的数量,因此如果我们只检查管道并返回0,我们就知道没有任何东西可以在管道中读取。

  2. 我们可以在父级中使用sleep()来测试它。 sleep()如果在请求的时间内睡眠则返回0,如果被信号中断则返回非零,因此我们可以检查返回值以确定它是哪种情况。对于您的特定情况,如果您希望父级获得用户的输入,read()将返回-1并将errno设置为EINTR,如果它正在等待用户输入并且接收信号,因此您可以通过这种方式检测超时。当然,在这个特定的用例中,父母更容易在select()上用你的60秒超时来调用STDIN_FILENO本身,而不是让孩子在管道上等待并发送一个信号。

  3. 在孩子中,我们必须FD_ZERO fd_set并在每次循环时重新填充struct timeval,因为select()可能会修改它们。

  4. 我们需要为SIGUSR1注册一个信号处理程序(什么都不做),以避免在收到进程时终止进程。我们不需要信号处理程序来执行任何操作,因为sleep()会在信号中断时通过返回值告诉我们,以及我们需要的所有信息。 / p>

  5. 代码:

    #define _XOPEN_SOURCE 500
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/select.h>
    
    #define READ_END 0
    #define WRITE_END 1
    #define SEC_THRESHOLD 5
    #define LOOP_TIMES 5
    #define BUFFER_SIZE 512
    
    void handler(int signum)
    {
        (void) signum;  //  Ignore unused argument
    }
    
    int main(void)
    {
        int p[2];
        if ( pipe(p) == -1 ) {
            perror("pipe() failed");
            exit(EXIT_FAILURE);
        }
    
        pid_t pid = fork();
        if ( pid == -1 ) {
            perror("fork() failed");
            exit(EXIT_FAILURE);
        }
        else if ( pid == 0 ) {
            if ( close(p[WRITE_END]) == -1 ) {
                perror("failed to close pipe end in child");
                exit(EXIT_FAILURE);
            }
    
            for ( int i = 0; i < LOOP_TIMES; ++i ) {
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(p[READ_END], &fds);
    
                struct timeval timeout;
                timeout.tv_sec = SEC_THRESHOLD;
                timeout.tv_usec = 500000;
    
                printf("Loop %d: child waiting for %d.5 seconds.\n",
                       i, SEC_THRESHOLD);
                int status = select(p[READ_END] + 1, &fds, NULL, NULL, &timeout);
                if ( status == -1 ) {
                    perror("select() failed in child");
                    exit(EXIT_FAILURE);
                }
                else if ( status == 0 ) {
                    printf("Loop %d: timed out in child, sending signal.\n", i);
                    kill(getppid(), SIGUSR1);
                }
                else {
                    char buffer[BUFFER_SIZE] = {0};
                    if ( read(p[READ_END], buffer, BUFFER_SIZE - 1) == -1 ) {
                        perror("read() failed in child");
                        exit(EXIT_FAILURE);
                    }
                    printf("Loop %d: child read: [%s]\n", i, buffer);
                }
            }
    
            if ( close(p[READ_END]) == -1 ) {
                perror("failed to close read end of pipe in child");
                exit(EXIT_FAILURE);
            }
    
            exit(EXIT_SUCCESS);
        }
        else {
            if ( close(p[READ_END]) == -1 ) {
                perror("failed to close pipe end in parent");
                exit(EXIT_FAILURE);
            }
    
            struct sigaction sa;
            sa.sa_handler = handler;
            sigemptyset(&sa.sa_mask);
            sa.sa_flags = 0;
            sigaction(SIGUSR1, &sa, NULL);
    
            srand((unsigned) time(NULL));
    
            for ( int i = 0; i < LOOP_TIMES; ++i ) {
                const char * msg = "Message to child";
                const int wait_time = rand() % 6 + 3;
    
                printf("Loop %d: parent waiting for %d seconds...\n", i, wait_time);
                int status = sleep(wait_time);
                if ( status == 0 ) {
                    printf("Loop %d: parent sending message to child.\n", i);
                    if ( write(p[WRITE_END], msg, strlen(msg)) == -1 ) {
                        perror("write() error in parent");
                        exit(EXIT_FAILURE);
                    }
                }
                else {
                    printf("Loop %d: parent interrupted by signal.\n", i);
                }
            }
    
            if ( close(p[WRITE_END]) == -1 ) {
                perror("failed to close write end of pipe in parent");
                exit(EXIT_FAILURE);
            }
    
        }
    
        if ( waitpid(pid, NULL, 0) == -1 ) {
            perror("waitpid() failed");
            exit(EXIT_FAILURE);
        }
    
        return EXIT_SUCCESS;
    }
    

    和示例输出会话:

    paul@horus:~/src/sandbox$ ./sigpipe
    Loop 0: parent waiting for 7 seconds...
    Loop 0: child waiting for 5.5 seconds.
    Loop 0: timed out in child, sending signal.
    Loop 1: child waiting for 5.5 seconds.
    Loop 0: parent interrupted by signal.
    Loop 1: parent waiting for 7 seconds...
    Loop 1: timed out in child, sending signal.
    Loop 2: child waiting for 5.5 seconds.
    Loop 1: parent interrupted by signal.
    Loop 2: parent waiting for 6 seconds...
    Loop 2: timed out in child, sending signal.
    Loop 3: child waiting for 5.5 seconds.
    Loop 2: parent interrupted by signal.
    Loop 3: parent waiting for 5 seconds...
    Loop 3: parent sending message to child.
    Loop 4: parent waiting for 3 seconds...
    Loop 3: child read: [Message to child]
    Loop 4: child waiting for 5.5 seconds.
    Loop 4: parent sending message to child.
    Loop 4: child read: [Message to child]
    paul@horus:~/src/sandbox$ 
    

    所以我们可以看到,如果子进程达到5.5秒超时,父进程被中断的每次迭代,并在所有其他情况下成功发送消息。