C 使用 select() 从两个命名管道(FIFO)中读取
C using select() to read from two named pipes (FIFO)
我目前正在尝试用 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);
}
}
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
操作对于特定的管道,但对于另一个管道。
要检查哪个管道实际有数据,请在 select
调用后使用 FD_ISSET
:
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);
}
}
}
所以,添加错误处理,你就会被设置。
我目前正在尝试用 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);
}
}
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
操作对于特定的管道,但对于另一个管道。
要检查哪个管道实际有数据,请在 select
调用后使用 FD_ISSET
:
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);
}
}
}
所以,添加错误处理,你就会被设置。