Recently I've been messing around with named pipes for a university project I'm working on. I need to make a program that acts as a "server" - It continuously reads from a named pipe and executes whatever command is given to it through said pipe. I've managed to do this but there's a problem: 100% CPU usage. Obviously this is a problem and it would contribute to a lower grade from my professors so I'd like to reduce this.
EDIT: I forgot to mention that after reading the message from the pipe, and executing it, the server must keep on running waiting for another message. At no point should the server program terminate (except for when SIGINT or similar signal is sent). The desired behavior is for it to never leave the loop, and just keep on reading and executing the message sent by the pipe.
#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;
}
I'm not handling errors on some functions just in this particular example, on the actual project I handle all of those. This code just prints out whatever the command passed to it through the pipe.
This is a small program that I made to send something through the pipe:
#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;
}
This works fine, apart from the CPU usage problem. One thing I've thought of was using pause()
in the beginning of the while, and stopping this pause once every second.
#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;
}
This "works", by setting CPU usage to 0%. However, it makes it so that my project wouldn't work properly. If I send two commands fast enough, it would register only as one command. This is due to the pause, while execution is stopped, these commands would be written on to the pipe without being read - Writing hello
and world
separately would register as a hello world
instead of two separate arguments.
Is there a different way I can reduce CPU usage?
@Cheatah's sugestion of using poll()
worked perfectly. This is the code now with the changes suggested:
#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;
}
This gets me 0% CPU usage and works really well