简体   繁体   English

检查管道是否为空,UNIX

[英]Check if pipe is empty, UNIX

I have 2 processes, the parent and the child. 我有两个过程,父母和孩子。 Parent process should wait for the user to insert some string and then it should send this string to the child process through a pipe. 父进程应等待用户插入一些字符串,然后应通过管道将此字符串发送给子进程。 I've successfully done this, but what I'm trying to achieve is to check if the pipe is empty, if it is empty for 60 seconds then the child process should send a signal to its parent. 我已成功完成此操作,但我要实现的目的是检查管道是否为空,如果管道为60秒为空,则子进程应向其父进程发送信号。

My Code so far: 到目前为止,我的代码:

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

Here's an example where the parent sleep() s for a random number of seconds and then write() s to the child through the pipe. 这是一个示例,其中父sleep()随机停留几秒钟,然后通过管道将write()孩子。 If the child does not receive the message within 5.5 seconds, it sends SIGUSR1 to the parent, and the parent does not write the message. 如果子级在5.5秒内未收到消息,则它将SIGUSR1发送给父级,而父级不会编写该消息。 This repeats for a specified number of times. 重复指定的次数。

Note that the child waits for 5.5 seconds, rather than 5 seconds, because we're using sleep() in the parent which takes whole seconds. 请注意,孩子等待的时间为5.5秒,而不是5秒,因为我们在父母中使用sleep()占用了整秒。 This (usually) will leave enough of a time gap to avoid the case where the parent sleep() s and the child waits for 5 seconds, and their wires cross in the middle. (通常)这将留出足够的时间间隔,以避免父母sleep()和孩子等待5秒,并且它们的电线在中间交叉的情况。 We could instead use nanosleep() in the parent, and have the child wait for a whole number of seconds. 相反,我们可以在父级中使用nanosleep() ,让子级等待整数秒。

Notes: 笔记:

  1. We can use select() with a timeout in the child process, to wait for the specified amount of time. 我们可以在子进程中将select()与超时一起使用,以等待指定的时间。 select() will return the number of ready file descriptors, so if we only check the pipe and it returns 0 , we know that nothing is ready for reading in the pipe. select()将返回就绪文件描述符的数量,因此,如果我们仅检查管道,并且它返回0 ,那么我们知道没有任何内容可供读取管道。

  2. We can use sleep() in the parent to test it out. 我们可以在父级中使用sleep()进行测试。 sleep() returns 0 if it slept for the requested time, and non-zero if it was interrupted by a signal, so we can check the return value to figure out which case it was. sleep()如果在请求的时间内睡眠,则返回0;如果它被信号中断,则返回非零,因此我们可以检查返回值以找出是哪种情况。 For your particular case, if you want the parent to get input from the user, read() will return -1 and set errno to EINTR if it is waiting for user input and receives a signal, so you can detect the timeout in that way. 对于您的特定情况,如果您希望父级从用户那里获取输入,则read()将返回-1并将errnoEINTR如果它正在等待用户输入并接收到信号),因此您可以通过这种方式检测超时。 Of course, in this particular use case, it's easier for the parent to just call select() itself on STDIN_FILENO with your 60 second timeout, rather than have the child wait on the pipe and send a signal. 当然,在此特定用例中,父级只需在60秒的超时时间内仅在STDIN_FILENO上调用select()本身,而不是让子级等待管道并发送信号就容易了。

  3. In the child, we have to FD_ZERO the fd_set and repopulate the struct timeval each time through the loop, because select() might modify them. 在子FD_ZERO ,我们必须对fd_set进行FD_ZERO并通过循环每次填充struct timeval ,因为select()可能会对其进行修改。

  4. We need to register a signal handler for SIGUSR1 (which does nothing) to avoid the process being terminated when it is received. 我们需要为SIGUSR1注册一个信号处理程序(不执行任何操作),以避免进程在接收到时终止。 We don't need the signal handler to do anything, because sleep() will tell us via the return value when it was interrupted by a signal, and that's all the information we need, here. 我们不需要信号处理程序执行任何操作,因为sleep()会在信号中断信号时通过返回值告诉我们,这就是我们需要的所有信息。

Code: 码:

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

and sample output session: 和示例输出会话:

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$ 

so we can see that each iteration the parent gets interrupted if the child reaches its 5.5 second timeout, and successfully sends the message in all other cases. 因此我们可以看到,如果子级达到其5.5秒超时,则父级的每次迭代都会被中断,并在所有其他情况下成功发送消息。

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

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