IPC FIFO Producer-Consumer Deadlock - c

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.

Related

Using named pipes in c

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.

use dup2 to redirect printf failed

The code is following.
Q1:
If dup2(fd3, STDOUT_FILENO), string2 will be in log.txt.
If dup2(g_ctl[0], STDOUT_FILENO), string2 won't be received by g_ctl[1].
string1 and ls -al output will be received, Why ?
Q2:
The third library have some stdout/stderr log, if using dup2(socket_fd, STDOUT_FILENO), all logs will be collected by socket. But I also want to print all logs to screen at the same time, how to do it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
static pthread_t g_pid;
static int g_ctl[2] = {-1, -1};
void *_run_loop(void *args) {
char buf[1024];
int n;
while (1) {
n = recv(g_ctl[1], buf, 1024, 0);
if (n > 0) {
fprintf(stderr, "%.*s\n", n, buf);
}
}
}
int main(int argc, char const *argv[])
{
int fd3 = open("./log.txt", O_CREAT | O_RDWR | O_APPEND, 0666);
int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, g_ctl);
assert(ret == 0);
ret = dup2(g_ctl[0], STDOUT_FILENO);
assert(ret > 0);
pthread_create(&g_pid, NULL, _run_loop, NULL);
send(STDOUT_FILENO, "string1", 5, 0);
system("ls -al");
printf("string2\n");
sleep(5);
return 0;
}
Q1: You need to fflush(stdout); after your printf. Otherwise printf may buffer your output. It will be written when your program exits if it hasn't already, but by that time your reading thread has already been canceled, so you don't get to read it.
Q2: As far as I know, the only way to get your output written to two files is to actually write it to both file descriptors. There is no way in Unix to "double-dup" a file descriptor. Even a command like tee is really just calling write() twice for each chunk of data read. You can do it manually, or inside a function, or in a thread, but you have to do it.

fifo linux - write() function terminates the program abruptly

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!!

Unnamed pipe without fork in C

I need create unnamed pipe in C without fork();
I have code with fork, but I can't find any information about unnamed pipe without fork. I read that this is an old solution, but it just needs it. Can anyone help me?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define KOM "Message to parent\n"
int main()
{
int potok_fd[2], count, status;
char bufor[BUFSIZ];
pipe(potok_fd);
if (fork() == 0) {
write(potok_fd[1], KOM, strlen(KOM));
exit(0);
}
close(potok_fd[1]);
while ((count = read(potok_fd[0], bufor, BUFSIZ)) > 0)
write(1, bufor, count);
wait(&status);
return (status);
}
You should be more precise, what do you need it to do? Just sending a message to yourself within the same process doesn't make much sense.
Anyway you can literally just not fork and do everything inside one process, it's just not really useful.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define KOM "Message to parent\n"
int main()
{
int potok_fd[2], count;
char bufor[BUFSIZ];
pipe(potok_fd);
write(potok_fd[1], KOM, strlen(KOM));
fcntl(potok_fd[0], F_SETFL, O_NONBLOCK);
while ((count = read(potok_fd[0], bufor, BUFSIZ)) > 0)
write(1, bufor, count);
return 0;
}

Reading and writng with named pipes C

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.

Resources