使用轮询功能的缓冲流 [英] Using poll function with buffered streams

查看:113
本文介绍了使用轮询功能的缓冲流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用的民调在C函数流程如下:

I am trying to implement a client-server type of communication system using the poll function in C. The flow is as follows:


  1. 主程序叉一个子进程

  2. 子进程调用 EXEC 函数来执行 some_binary

  3. 家长和孩子将消息发送到相互交替,发送每条消息取决于收到的最后一条消息。

  1. Main program forks a sub-process
  2. Child process calls the exec function to execute some_binary
  3. Parent and child send messages to each other alternately, each message that is sent depends on the last message received.

我试图实现这一点使用调查,但遇到了问题,因为子进程缓冲输出,导致我的调查调用超时。这里是我的code:

I tried to implement this using poll, but ran into problems because the child process buffers its output, causing my poll calls to timeout. Here's my code:

int main() {
char *buffer = (char *) malloc(1000);
int n;

pid_t pid; /* pid of child process */

int rpipe[2]; /* pipe used to read from child process */
int wpipe[2]; /* pipe used to write to child process */
pipe(rpipe);
pipe(wpipe);

pid = fork();
if (pid == (pid_t) 0)
{
    /* child */

    dup2(wpipe[0], STDIN_FILENO);
    dup2(rpipe[1], STDOUT_FILENO);
    close(wpipe[0]); close(rpipe[0]);
    close(wpipe[1]); close(rpipe[1]);
    if (execl("./server", "./server", (char *) NULL) == -1)
    {
        fprintf(stderr, "exec failed\n");
        return EXIT_FAILURE;
    }       
    return EXIT_SUCCESS;
}
else
{
    /* parent */

    /* close the other ends */
    close(wpipe[0]);
    close(rpipe[1]);

    /* 
      poll to check if write is good to go 
                This poll succeeds, write goes through
        */
    struct pollfd pfds[1];
    pfds[0].fd = wpipe[1];
    pfds[0].events = POLLIN | POLLOUT;
    int pres = poll(pfds, (nfds_t) 1, 1000);
    if (pres > 0)
    {
        if (pfds[0].revents & POLLOUT)
        {
            printf("Writing data...\n");
            write(wpipe[1], "hello\n", 6);
        }
    }

    /* 
        poll to check if there's something to read.
        This poll times out because the child buffers its stdout stream.
    */
    pfds[0].fd = rpipe[0];
    pfds[0].events = POLLIN | POLLOUT;
    pres = poll(pfds, (nfds_t) 1, 1000);
    if (pres > 0)
    {
        if (pfds[0].revents & POLLIN)
        {
            printf("Reading data...\n");
            int n = read(rpipe[0], buffer, 1000);
            buffer[n] = '\0';
            printf("child says:\n%s\n", buffer);
        }
    }

    kill(pid, SIGTERM);
    return EXIT_SUCCESS;
}
}

服务器code是简单的:

The server code is simply:

int main() {
    char *buffer = (char *) malloc(1000);

    while (scanf("%s", buffer) != EOF)
    {
        printf("I received %s\n", buffer);
    }   
    return 0;
}

我如何prevent 调查,因为缓冲时间从召唤出?

How do I prevent poll calls from timing out because of buffering?

编辑:

我想程序工作,即使在 EXEC 编辑二进制是外部的,也就是说,我有超过code无控制 - 就像一个unix命令,例如, LS

I would like the program to work even when the execed binary is external, i.e., I have no control over the code - like a unix command, e.g., cat or ls.

推荐答案

您所需要的,因为我在相关答案由您previous问题,实现一个事件循环;因为它顾名思义,它是的循环的,所以你应该code在父进程:

You need, as I answered in a related answer to a previous question by you, to implement an event loop; as it name implies, it is looping, so you should code in the parent process:

while (1) { // simplistic event loop!
   int status=0;
   if (waitpid(pid, &status, WNOHANG) == pid)
      { // clean up, child process has ended
        handle_process_end(status);
        break;
      };
   struct pollpfd pfd[2];
   memset (&pfd, 0, sizeof(pfd)); // probably useless but dont harm
   pfd[0].fd = rpipe[0];
   pfd[0].events = POLL_IN;
   pfd[1].fd = wpipe[1];
   pfd[0].event = POLL_OUT;
   #define DELAY 5000 /* 5 seconds */
   if (poll(pfd, 2, DELAY)>0) {
      if (pfd[0].revents & POLL_IN) {
         /* read something from rpipe[0]; detect end of file; 
            you probably need to do some buffering, because you may 
            e.g. read some partial line chunk written by the child, and 
            you could only handle full lines. */
      };
      if (pfd[1].revents & POLL_OUT) {
         /* write something on wpipe[1] */
      };
   }
   fflush(NULL);
} /* end while(1) */

您不能predict其中责令管道读写,这样可以发生很多次。当然,有很多缓冲(在父进程)参与,我离开的细节,你....你必须在子进程缓冲无影响(某些程序会检测到它们的输出或与终端 isatty )。

you cannot predict in which order the pipes are readable or writable, and this can happen many times. Of course, a lot of buffering (in the parent process) is involved, I leave the details to you.... You have no influence on the buffering in the child process (some programs detect that their output is or not a terminal with isatty).

这是多么的事件轮询循环的像上面给你是为了避免当子进程被阻止,因为它的标准输出管道中充满了僵局的情况下,而父被阻塞写作(孩子的标准输入管道),因为管道已满:有一个事件循环,你只要某些数据进行轮询输入管道读(读即子进程的标准输出),您只要输出管道进行轮询写入写的一些数据(即未满)。你不能predict提前在订购这些事件小孩的输出是由家长可读和小孩的输入是由家长写发生了。

What an event polling loop like above gives you is to avoid the deadlock situation where the child process is blocked because its stdout pipe is full, while the parent is blocked writing (to the child's stdin pipe) because the pipe is full: with an event loop, you read as soon as some data is polled readable on the input pipe (i.e. the stdout of the child process), and you write some data as soon as the output pipe is polled writable (i.e. is not full). You cannot predict in advance in which order these events "output of child is readable by parent" and "input of child is writable by parent" happen.

我建议你阅读高级Linux编程

BTW我简单的事件循环是有点不对:如果子进程终止,一些数据仍然是其标准输出管道,其读数没有这样做。你可以移动调查后 waitpid函数测试

BTW my simplistic event loop is a bit wrong: if the child process terminated and some data remains in its stdout pipe, its reading is not done. You could move the waitpid test after the poll

另外,不要指望一个(从子进程)到管道将触发一个单一的中的父进程。换句话说,没有消息长度的概念。然而,POSIX知道 PIPE_MAX ....看到它的文档。也许您的缓冲区传递给 PIPE_MAX的尺寸

Also, don't expect that a single write (from the child process) into a pipe would trigger one single read in the parent process. In other words, there is no notion of message length. However, POSIX knows about PIPE_MAX .... See its write documentation. Probably your buffer passed to read and write should be of PIPE_MAX size.

我再说一遍:你需要为呼叫调查您事件中的循环并很有可能调查将被调用的几次的(因为你的循环会被重复很多次!),并将报告读写管在未predictable结束(和非重复性的)命令!你的程序可以报告的第一跑 rpipe [0] 可读,您从它324字节,重复事件循环,调查说你 wpipe [1] 可写,可以 10个字节它,你重复事件循环,调查告诉 rpipe [0] 可读,您从它110个字节,你重复事件循环,调查再次告诉 rpipe [0] 可读,您从它4096个字节,等等等......的第二次运行在同一环境同一程序会给出不同的事件,如:调查表示, wpipe [1] 可写,您 1000字节,你重复循环,调查表示, rpipe [ 0] 可读,等等...

I repeat: you need to call poll inside your event loop and very probably poll will be called several times (because your loop will be repeated many times!), and will report readable or writable pipe ends in an unpredictable (and non-reproducible) order! A first run of your program could report "rpipe[0] readable", you read 324 bytes from it, you repeat the event loop, poll says you "wpipe[1] writable", you can write 10 bytes to it, you repeat the event loop, poll tells that "rpipe[0] readable", you read 110 bytes from it, you repeat the event loop, poll tells again "rpipe[0] readable", you read 4096 bytes from it, etc etc etc... A second run of the same program in the same environment would give different events, like: poll says that "wpipe[1] writable", you write 1000 bytes to it, you repeat the loop, poll says that "rpipe[0] readable, etc....

注:您的问题不在孩子(客户)计划,我们假设你不能改变的缓冲。所以,重要的不是它的缓冲数据,但是的真正的输入和输出(也就是你的父进程可以观察到的唯一的事;内孩子的缓冲无关父),即数据你的孩子的程序已经能够的真正读取( 2)写(2)。而如果要直通一个管(7)时,这样的数据会调查(2)在父进程-able(和你的父进程可以后,它的一些 POLL_IN POLL_OUT 在以后的更新的revents 字段调查) 。顺便说一句,如果你没有code中的孩子,不要忘记调用 fflush 在适当的地方在里面。

NB: your issue is not the buffering in the child ("client") program, which we assume you cannot change. So what matters is not the buffered data in it, but the genuine input and output (that is the only thing your parent process can observe; internal child buffering is irrelevant for the parent), i.e. the data that your child program has been able to really read(2) and write(2). And if going thru a pipe(7), such data will become poll(2)-able in the parent process (and your parent process can read or write some of it after POLL_IN or POLL_OUT in the updated revents field after poll). BTW, if you did code the child, don't forget to call fflush at appropriate places inside it.

这篇关于使用轮询功能的缓冲流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆