如何在没有 100% CPU 使用率的情况下继续从命名管道读取数据?

How can I keep reading from a named pipe without 100% CPU usage?

背景和问题

最近我一直在为我正在进行的大学项目弄乱命名管道。我需要制作一个充当“服务器”的程序 - 它不断地从命名管道读取并执行通过所述管道提供给它的任何命令。我设法做到了这一点,但存在一个问题:100% CPU 使用率。显然这是一个问题,它会导致我的教授给我打分,所以我想减少这个问题。

编辑:我忘了提到从管道读取消息并执行后,服务器必须继续运行 等待另一条消息。服务器程序在任何时候都不应终止(发送 SIGINT 或类似信号时除外)。 期望的行为是它永远不会离开循环,只是继续读取和执行管道发送的消息。

“服务器”代码的最小可重现示例:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main () {
    if (mkfifo("fifo", 0600) == -1) {
        perror("Error on fifo");
        return -1;
    }
    char buffer[1024];
    int bytesWritten = 0;
    int fifo = open("fifo", O_RDONLY);
    while(1) {
        bytesWritten = read(fifo, buffer, 1024);
        if (bytesWritten > 0) 
            write(1, buffer, bytesWritten);
    }
    return 0;
}

我不只是在这个特定示例中处理某些函数的错误,在实际项目中我会处理所有这些错误。这段代码只是打印出通过管道传递给它的任何命令。
这是我制作的一个通过管道发送东西的小程序:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

int main () {
    int fifo = open("fifo", O_WRONLY);
    write(fifo, "This is a test\n", strlen("This is a test\n"));
    close(fifo);
    return 0;
}

除 CPU 使用问题外,这工作正常。我想到的一件事是在 while 的开始使用 pause(),并且每秒停止一次暂停。

我尝试了什么:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

void alarm_handler(int signum) {
    alarm(1);
}

int main () {
    if (mkfifo("fifo", 0600) == -1) {
        perror("Error on fifo");
        return -1;
    }
    char buffer[1024];
    int bytesWritten = 0;
    int fifo = open("fifo", O_RDONLY);
    signal(SIGALRM, alarm_handler);
    alarm(1);
    while(1) {
        pause();
        bytesWritten = read(fifo, buffer, 1024);
        if (bytesWritten > 0) 
            write(1, buffer, bytesWritten);
    }
    return 0;
}

通过将 CPU 使用率设置为 0%,这“有效”。但是,它使我的项目无法正常工作。如果我足够快地发送两个命令,它只会注册为一个命令。这是由于暂停,当执行停止时,这些命令将被写入管道而不被读取 - 分别写入 helloworld 将注册为 hello world 而不是两个单独的参数。
有没有其他方法可以减少 CPU 使用量?

@Cheatah 关于使用 poll() 的建议非常有效。这是现在的代码,建议进行更改:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <poll.h>

int main () {
    if (mkfifo("fifo", 0600) == -1) {
        perror("Error on fifo");
        return -1;
    }

    char buffer[1024];
    int bytesWritten = 0;
    int fifo = open("fifo", O_RDONLY);
    struct pollfd *pfd = calloc(1, sizeof(struct pollfd));
    pfd->fd = fifo;
    pfd->events = POLLIN;
    pfd->revents = POLLOUT;

    while(1) {
        poll(pfd, 1, -1);
        bytesWritten = read(fifo, buffer, 1024);
        if (bytesWritten == -1) {
            perror("Error on read()");
            exit(-1);
        }
        if (bytesWritten > 0) 
            write(1, buffer, bytesWritten);
    }
    return 0;
}

这使我的使用率达到 0% CPU,而且效果非常好

为防止这种忙等待情况而对代码进行的最小更改是简单地将服务器中的 fifo 打开为 read/write 而不是只读:

int fifo = open("fifo", O_RDWR);

通过这种方式,您将始终打开 fifo 以供程序(服务器本身)写入,因此当第一个客户端关闭 fifo 时,读取操作不会提前终止。

另一方面,如果您希望 read() 超时阻塞,使用 poll() 特别好。

见: