I'm implementing a pipe in C, where multiples producer programs (9 in my case) write data to one single consumer program.
The problem is that some producers (some times one or two) exit the program abruptly when calling the write() function.
The code is simple, here is the producer code:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#define MSG_SIZE_BYTES 4
void send(unsigned int * msg){
int fd, msg_size;
int r;
char buffer [5];
char myfifo[50] = "/tmp/myfifo";
fd = open(myfifo, O_WRONLY);
if(fd == -1){
perror("error open SEND to fifo");
}
r = write(fd, msg, MSG_SIZE_BYTES);
if(r == -1){
perror("error writing to fifo");
}
close(fd);
printf("Message send\n");
}
int main(int argc, char *argv[]){
int cluster_id = atoi(argv[1]);
unsigned int msg[1];
msg[0] = cluster_id;
while(1){
printf("Press a key to continue...\n");
getchar();
send(msg);
}
}
And here is the consumer code
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#define MSG_SIZE_BYTES 4
int receive(unsigned int * received_msg){
int fd, msg_size;
int ret_code;
char buffer [5];
char myfifo[50] = "/tmp/myfifo";
fd = open(myfifo, O_RDONLY);
if(fd == -1)
perror("error open RECV to fifo");
ret_code = read(fd, received_msg, MSG_SIZE_BYTES);
close(fd);
if (ret_code == -1){
printf("\nERROR\n");
return 0;
}
return 1;
}
void main(){
mkfifo("/tmp/myfifo", 0666);
unsigned int msg[1];
while(1){
receive(msg);
printf("receive msg from id %d\n", msg[0]);
}
}
I'm compiling the producers and consumer with the following command: gcc -o my_progam my_program.c
To reproduce the problem, you need to open 9 terminals to run each producer and 1 terminal to run the consumer.
Execute the consumer: ./consumer
Execute the producer in all terminals simultaneously, passing to each execution an associated ID passed by command line. Ex: ./producer 0, ./producer 1.
After the producer send messages some times (10 in average), one arbitrary producer will abruptly stop its execution, showing the problem.
The following image depicts the execution:
Terminals ready to execute
The following image depicts the error on producer ID 3
Error on producer 3
Thanks in advance
It looks like the consumer program closes the reading end of the pipe after reading data:
fd = open(myfifo, O_RDONLY);
if(fd == -1){
perror("error open RECV to fifo");
}
ret_code = read(fd, received_msg, MSG_SIZE_BYTES);
close(fd);
All other writers, which are currently trying to write() data (i.e. are blocked in the write()-syscall) now receive a SIGPIPE, which leads to program termination (if no other signal handling is specified).
Your consumer program may not close the filedescriptor while producers are writing. Just read the next datum without closing.
Problem SOLVED:
The problem is that I was opening and closing the FIFO at each message, generating a Broken pipe in some write attempts. Removing the close() and inserting the open() function for BOTH producer and consumer at the begging of the code instead inside the loop solved the problem.
Here is the code of producer with the bug fixed:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#define MSG_SIZE_BYTES 4
int my_fd;
void send(unsigned int * msg){
int fd, msg_size;
int r;
char buffer [5];
char myfifo[50] = "/tmp/myfifo"
if(fd == -1){
perror("error open SEND to fifo");
}
r = write(my_fd, msg, MSG_SIZE_BYTES);
if(r == -1){
perror("error writing to fifo");
}
//close(fd);
printf("Message send\n");
}
int main(int argc, char *argv[]){
int cluster_id = atoi(argv[1]);
unsigned int msg[1];
msg[0] = cluster_id;
my_fd = open("/tmp/myfifo", O_WRONLY);
while(1){
printf("Press a key to continue...\n");
getchar();
send(msg);
}
}
And here is the consumer code:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#define MSG_SIZE_BYTES 4
int my_fd;
int receive(unsigned int * received_msg){
int fd, msg_size;
int ret_code;
char buffer [5];
char myfifo[50] = "/tmp/myfifo";
if(fd == -1)
perror("error open RECV to fifo");
ret_code = read(my_fd, received_msg, MSG_SIZE_BYTES);
//close(fd);
if (ret_code == -1){
printf("\nERROR\n");
return 0;
}
return 1;
}
void main(){
mkfifo("/tmp/myfifo", 0666);
my_fd = open("/tmp/myfifo", O_RDONLY);
unsigned int msg[1];
while(1){
receive(msg);
printf("receive msg from id %d\n", msg[0]);
}
}
Thank you all!!
Related
i have this simple program that passes a value through a named pipe from child to parent process:
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
char * myfifo = "/home/tmp/myfifo";
mkfifo(myfifo, 0666);
int fd,rec;
pid_t c=fork();
if(c==0){
fd = open(myfifo, O_WRONLY);
rec=100;
write(fd, rec, sizeof(rec));
}
if(c>0){
sleep(1);
fd = open(myfifo, O_RDONLY);
read(fd, rec, sizeof(rec));
printf("%d\n",fd);
printf("%d\n",rec);
}
}
This program prints fd=-1 and instead of rec being 100 it prints rec's address.I also tried putting &rec in read and write but it did not solve anything.What am i doing wrong?
There's an issue with this line:
write(fd, rec, sizeof(rec));
This is the prototype of write():
ssize_t write(int fd, const void *buf, size_t count);
That means that you're reading from the memory location stored in rec, not the content of rec.
The same thing applies for read(). You need to pass a pointer to rec instead of rec itself.
Also, always make sure to close files after you open and perform I/O on them.
Here's a correct copy of your code:
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
const char *myfifo = "/home/tmp/myfifo";
mkfifo(myfifo, 0666);
int fd, rec;
pid_t c = fork();
if(c == 0) {
fd = open(myfifo, O_WRONLY);
rec = 100;
write(fd, &rec, sizeof(rec));
close(fd);
}
if(c > 0) {
sleep(1);
fd = open(myfifo, O_RDONLY);
read(fd, &rec, sizeof(rec));
printf("%d\n", fd);
printf("%d\n", rec);
close(fd);
}
}
Of course, always make sure you have the proper permissions to create, read, and write files in that directory. Also, make sure the directory /home/tmp exists.
In my understanding, according to the https://linux.die.net/man/3/mkfifo,
I got an implication that I must have reader and writer file, in order to
utilize the pipe file. The source below is the writer file,
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int fd;
char *myfifo = "./myfifo";
mkfifo(myfifo, 0777);
fd = open(myfifo, O_WRONLY);
int PID = fork();
if(PID == 0){
execl("./reader.o", "reader", (char*)NULL);
}
write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
close(fd);
unlink(myfifo);
return 0;
}
and the source being provided below is for the reader file.
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUF 1024
int main(){
int fd;
char* myfifo = "./myfifo";
char buf[MAX_BUF];
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
write(STDOUT_FILENO, buf, MAX_BUF);
close(fd);
exit(EXIT_SUCCESS);
return 0;
}
When run the executable for the writer file, the command prompt goes into
halt, after printing a newline. My assumption for this problem is because the
open() in the writer file is not being able to detect the pipe file,
is that the case?
Thank you.
I suggest that you should create the FIFO before the fork, but only open the FIFO after the fork. This avoids an assortment of problems. For the most part, I've used write() to report errors to standard error; it isn't as convenient as using fprintf(stderr, …) though.
Note that the writer writes a null byte at the end of the message. The reader gets the null byte, but overwrites it with a newline before writing the resulting character array (it is no longer a string; strings have a terminal null byte at the end) to standard output. If the code used <stdio.h> to write the data (e.g. printf("%s\n", buf)), it wouldn't need to replace the null byte with a newline.
writer.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef READER
#define READER "./reader"
#endif
int main(void)
{
char *myfifo = "./myfifo";
if (mkfifo(myfifo, 0777) != 0)
{
write(STDERR_FILENO, "Failed to create FIFO\n",
sizeof("Failed to create FIFO\n") - 1);
}
int PID = fork();
if (PID == 0)
{
execl(READER, "reader", (char *)NULL);
write(STDERR_FILENO, "Failed to execute reader\n",
sizeof("Failed to execute reader\n") - 1);
exit(EXIT_FAILURE);
}
if (PID < 0)
{
write(STDERR_FILENO, "Failed to fork\n",
sizeof("Failed to fork\n") - 1);
exit(EXIT_FAILURE);
}
int fd = open(myfifo, O_WRONLY);
if (fd < 0)
{
write(STDERR_FILENO, "Failed to open FIFO for writing\n",
sizeof("Failed to open FIFO for writing\n") - 1);
unlink(myfifo);
exit(EXIT_FAILURE);
}
write(fd, "Rock and roll baby", sizeof("Rock and roll baby"));
close(fd);
unlink(myfifo);
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
reader.c
#include <fcntl.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(void)
{
char* myfifo = "./myfifo";
int fd = open(myfifo, O_RDONLY);
if (fd < 0)
write(STDERR_FILENO, "Failed to open FIFO for reading\n",
sizeof("Failed to open FIFO for reading\n")-1);
else
{
char buf[MAX_BUF];
int nbytes = read(fd, buf, MAX_BUF);
if (nbytes > 0)
{
buf[nbytes-1] = '\n';
write(STDOUT_FILENO, buf, nbytes);
}
close(fd);
}
return 0;
}
Example output
Rock and roll baby
Child 43734 exited with status 0x0000
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int fd;
char *myfifo = "./myfifo";
int PID = fork();
if(PID == 0){
execl("./reader.o", "reader", (char*)NULL);
}
mkfifo(myfifo, 0777);
fd = open(myfifo, O_WRONLY);
write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
close(fd);
unlink(myfifo);
return 0;
}
After having the body of the code, where the execl is, moved above the
mkfifo(),
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_BUF 1024
int main(){
sleep(3);
int fd;
char* myfifo = "./myfifo";
char buf[MAX_BUF];
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
write(STDOUT_FILENO, buf, MAX_BUF);
close(fd);
exit(EXIT_SUCCESS);
return 0;
}
and having the reader have sleep() for 3 seconds, the programs started to
work; however, does anyone know if the two programs can open() the pipe file
exactly at the same time?
Thank you.
I'm writing a program that should run indefinitely maintaining the value of a variable. Two other programs could change the value of the variable. I use named pipes to receive and send the variable value to external programs.
Here is my code for the manager of the variable.
manager.c:
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
char a = 'a';
void *editTask(void *dummy)
{
int fd;
char* editor = "editor";
mkfifo(editor, 0666);
while(1)
{
fd = open(editor, O_RDONLY);
read(fd, &a, 1);
close(fd);
}
}
void *readTask(void *dummy)
{
int fd;
char* reader = "reader";
mkfifo(reader, 0666);
while(1)
{
fd = open(reader, O_WRONLY);
write(fd,&a,1);
close(fd);
}
}
int main()
{
pthread_t editor_thread, reader_thread;
pthread_create(&editor_thread, NULL, editTask, NULL);
pthread_create(&reader_thread, NULL, readTask, NULL);
pthread_join (editor_thread, NULL);
pthread_join (reader_thread, NULL);
return 0;
}
This program uses pthreads to separately get external values for the variable and to communicate the current value of the variable to external programs.
The program that is able to write values to the variable is:
writer.c:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char** argv)
{
if(argc != 2)
{
printf("Need an argument!\n");
return 0;
}
int fd;
char * myfifo = "editor";
fd = open(myfifo, O_WRONLY);
write(fd, argv[0], 1);
close(fd);
return 0;
}
The program that could read the current value is:
reader.c:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd;
char * myfifo = "reader";
fd = open(myfifo, O_RDONLY);
char value = 'z';
read(fd, &value, 1);
printf("The current value of the variable is:%c\n",value);
close(fd);
return 0;
}
I ran these programs in my Ubuntu system as follows:
$ ./manager &
[1] 5226
$ ./writer k
$ ./reader
bash: ./reader: Text file busy
Why doesn't my system allow me to run this program?
Thank you.
You are trying to call both the FIFO and the reader program "reader".
Also, you have no error checking. You have no idea whether those calls to mkfifo and open succeeded or not. Adding this is critical before you attempt to do any troubleshooting.
This is the producer.
// speak.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_NAME "american_maid"
int main(void)
{
char s[300];
int num, fd;
mknod(FIFO_NAME, S_IFIFO | 0666, 0);
printf("waiting for readers...\n");
fd = open(FIFO_NAME, O_WRONLY);
printf("got a reader--type some stuff\n");
while (gets(s), !feof(stdin)) {
if ((num = write(fd, s, strlen(s))) == -1)
perror("write");
else
printf("speak: wrote %d bytes\n", num);
}
return 0;
}
And this is the consumer.
//tick.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_NAME "american_maid"
int main(void)
{
char s[300];
int num, fd;
mknod(FIFO_NAME, S_IFIFO | 0666, 0);
printf("waiting for writers...\n");
fd = open(FIFO_NAME, O_RDONLY);
printf("got a writer\n");
do {
if ((num = read(fd, s, 300)) == -1)
perror("read");
else {
s[num] = '\0';
printf("tick: read %d bytes: \"%s\"\n", num, s);
}
} while (num > 0);
return 0;
}
When I run them, Producer outputs,
waiting for readers...
And consumer outputs,
waiting for writers...
speak doesn't find the reader, tick. As from the theory here I got that, open() (speak.c) will be keep blocked until open() (tick.c) is opened. And the vice versa. So I guess there a deadlock or something happening. I need a solution of this.
It looks like you have a race condition between the reader and the writer.
To fix this, you need a method of not launching the reader until the writer is "active". For this, I'd suggest making a pipe and writing to it when the writer is ready. Then, when reading from the read end of the fork succeeds, the fifo is prepared and the reader should work.
You need to use forks here because coordinating mutexes between a parent and a child process is non-trivial and properly done pipes is easier.
Also, you called mknod() twice. Granted, it'll return -1 with errno == EEXIST, but be more careful. To avoid this, make the reader and writer a function that takes a path as an argument.
Rewrite your writer as int speak(const char *fifo, int pipefd) and your reader as int tick(const char *fifo).
Then make a wrapper like this:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
const char fifo_name[] /* = ... */;
int speak(const char *fifo, int pipefd);
int tick(const char *fifo);
int main() {
int pipefd[2];
pipe(pipefd);
mknod(fifo_name, S_IFIFO | 0666, 0);
if (fork() == 0) {
close(pipefd[0]);
return speak(fifo_name, pipefd[1]);
} else {
close(pipefd[1]);
char foo;
read(pipefd[0], &foo, 1);
return tick(fifo_name);
}
}
Modify your writer to print a byte (of anything) to the passed fd after the fifo is created (i.e. right after the call to open(..., O_WRONLY)).
Don't use my code verbatim, as I've omitted error checking for the sake of brevity.
it runs ok in my env. and if reader and writer is ready, open will return. because open is blocked, so in my opinion, mknod function is success. May be you excute these two process at different path.
I'm trying to write simple client and server C programs, communicating with each other in separate terminals.
The server has to create a public fifo and wait for the client. Meanwhile the client is creating his own fifo through which the server's response will come. The task of the client is sending the server a name created by the queue and get in return the result of the ls command.
I did search for an answer, for example: fifo-server-program, example-of-using-named-pipes-in-linux-bash, how-to-send-a-simple-string-between-two-programs-using-pipes. I started with the code from the third link and slowly modified it.
What I've got now, is a client taking input from the user, sending it to the server and receiving it back. But it only works once. I have no idea why. The body of main function is below. I will be grateful for any help.
EDIT:
I got it working! :D The codes are below, maybe it will help someone.
The server.c code:
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char* argv[])
{
int fds[2];
char tab[BUFSIZ];
int fd, n;
char *myfifo = "/tmp/serwer";
char *myfifo2 = "/tmp/client";
pipe(fds);
mkfifo(myfifo,0666);
while(1)
{
fds[0]=open(myfifo2,O_RDONLY);
fds[1]=open(myfifo,O_WRONLY);
read(fds[0],tab,BUFSIZ);
if (strcmp("klient",tab)==0) {
printf("Od klienta: %s\n",tab);
fd=open(tab,O_WRONLY);
if(fork()==0)
{
dup2(fds[1],1);
close(fds[1]);
execlp("ls","ls","-l",NULL);
close(fds[0]);
close(fds[1]);
}
else
{
dup2(fds[0],0);
n = read(fds[0],tab,BUFSIZ);
write(fd,tab,n);
close(fds[0]);
close(fds[1]);
}
}
memset(tab, 0, sizeof(tab));
close(fd);
close(fds[0]);
close(fds[1]);
}
unlink(myfifo);
return 0;
}
The client.c code:
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char* argv[])
{
int fds[2];
char *myfifo = "/tmp/serwer";
char *myfifo2 = "/tmp/client";
mkfifo(myfifo2,0666);
fds[0]=open(myfifo,O_RDONLY);
fds[1]=open(myfifo2,O_WRONLY);
char tab[BUFSIZ];
memset(tab, 0, sizeof(tab));
write(fds[1],"klient",6);
perror("Write:"); //Very crude error check
read(fds[0],tab,sizeof(tab));
perror("Read:"); // Very crude error check
printf("Odebrano od serwera: %s\n",tab);
close(fds[0]);
close(fds[1]);
unlink(myfifo2);
return 0;
}
Why don't you just manage both fifo's in the server? Simply changing your code to do this makes it work correctly.
If you actually want to have a client-server relationship, with a server serving many different clients, sockets would probably be a better choice.
client.cpp
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
printf("Input message to serwer: ");
scanf("%s", str);
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
server_to_client = open(myfifo2, O_RDONLY);
write(client_to_server, str, sizeof(str));
perror("Write:"); //Very crude error check
read(server_to_client,str,sizeof(str));
perror("Read:"); // Very crude error check
printf("...received from the server: %s\n",str);
close(client_to_server);
close(server_to_client);
/* remove the FIFO */
return 0;
}
server.cpp
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
mkfifo(myfifo2, 0666);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
server_to_client = open(myfifo2, O_WRONLY);
printf("Server ON.\n");
while (1)
{
read(client_to_server, buf, BUFSIZ);
if (strcmp("exit",buf)==0)
{
printf("Server OFF.\n");
break;
}
else if (strcmp("",buf)!=0)
{
printf("Received: %s\n", buf);
printf("Sending back...\n");
write(server_to_client,buf,BUFSIZ);
}
/* clean buf from any data */
memset(buf, 0, sizeof(buf));
}
close(client_to_server);
close(server_to_client);
unlink(myfifo);
unlink(myfifo2);
return 0;
}
It only works once because of how named pipes work. Each time you open a named pipe for read you block until another process opens it for write. Then you are paired up and the file descriptor connects your processes. Once either end closes that connection that's the end of that pipe. In order for your server to "accept another connection" it needs to move the open and close of the pipes into its main loop so it can be paired up over and over.