C 使用 select() 从两个命名管道 (FIFO) 读取

问题描述 投票:0回答:1

我目前正在尝试用 C 编写一个程序,该程序将从两个命名管道读取数据,并在可用时将任何数据打印到标准输出。

例如:如果我打开两个终端,并在其中一个终端中打开

./execute pipe1 pipe2
(其中pipe1和pipe2是有效的命名管道),然后键入
echo "Data here." > pipe1
,然后输入管道的名称(此处为
pipe1
),则大小,数据应该打印到标准输出 - 这里看起来像
pipe1 [25]: Data here.

我知道我需要使用

O_RDONLY
O_NONBLOCK
标志打开管道。我看过很多使用
select()
的例子(这个论坛上有很多),但我仍然不明白传递给
select()
的不同参数在做什么。如果有人可以在这里提供指导,那将会非常有帮助。下面是我到目前为止的代码。

 int pipeRouter(char[] fifo1, char[] fifo2){
    fileDescriptor1 = open(fifo1, O_RDONLY, O_NONBLOCK);
    fileDescriptor2 = open(fifo2, O_RDONLY, O_NONBLOCK);

    if(fileDescriptor1 < 0){
        printf("%s does not exist", fifo1);
    }
    if(fileDescriptor2 < 0){
        printf("%s does not exist", fifo2);
    }
}
c fifo posix-select
1个回答
12
投票

select
让您可以等待 I/O 事件,而不是在
read
上浪费 CPU 周期。

因此,在您的示例中,主循环可能如下所示:

for (;;)
{
  int res;
  char buf[256];

  res = read(fileDescriptor1, buf, sizeof(buf));
  if (res > 0)
  {
      printf("Read %d bytes from channel1\n", res);
  }
  res = read(fileDescriptor2, buf, sizeof(buf));
  if (res > 0)
  {
      printf("Read %d bytes from channel2\n", res);
  }
}

如果您添加代码并运行它,您会注意到:

  • 该程序实际上执行了您想要的操作 - 它从两个管道读取。
  • 一个核心的CPU利用率为100%,即即使没有数据可读取,程序也会浪费CPU。

为了解决问题,引入了

select
poll
API。对于
select
,我们需要知道描述符(我们确实知道),以及其中的最大值。

所以我们稍微修改一下代码:

 for (;;)
 {
    fd_set fds;
    int maxfd;

    FD_ZERO(&fds); // Clear FD set for select
    FD_SET(fileDescriptor1, &fds);
    FD_SET(fileDescriptor2, &fds);

    maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2;

    select(maxfd + 1, &fds, NULL, NULL, NULL);
    // The minimum information for select: we are asking only about
    // read operations, ignoring write and error ones; and not
    // defining any time restrictions on wait.


   // do reads as in previous example here
 }

运行改进后的代码时,CPU 不会浪费太多,但您会注意到,即使特定管道没有数据,但另一个管道有数据,也会执行

read
操作。

要检查哪个管道实际上有数据,请在

FD_ISSET
调用后使用
select

if (FD_ISSET(fileDescriptor1, &fds))
{
   // We can read from fileDescriptor1
}
if (FD_ISSET(fileDescriptor2, &fds))
{
   // We can read from fileDescriptor2
}

因此,加入上述内容后,代码将如下所示:

for (;;)
{
  fd_set fds;
  int maxfd;
  int res;
  char buf[256];

  FD_ZERO(&fds); // Clear FD set for select
  FD_SET(fileDescriptor1, &fds);
  FD_SET(fileDescriptor2, &fds);

  maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2;

  select(maxfd + 1, &fds, NULL, NULL, NULL);


  if (FD_ISSET(fileDescriptor1, &fds))
  {
     // We can read from fileDescriptor1
     res = read(fileDescriptor1, buf, sizeof(buf));
     if (res > 0)
     {
        printf("Read %d bytes from channel1\n", res);
     }
  }
  if (FD_ISSET(fileDescriptor2, &fds))
  {
     // We can read from fileDescriptor2
    res = read(fileDescriptor2, buf, sizeof(buf));
    if (res > 0)
    {
        printf("Read %d bytes from channel2\n", res);
    }
  }
}

所以,添加错误处理,就可以了。

© www.soinside.com 2019 - 2024. All rights reserved.