My little program includes two pipes to create a multidirectional communication between father and son.
The write on s2f [1] returns -1 but I did not understand why.
Can you help me? Is there anything else that does not work or that I could improve?
/*
Write a program in C language that in sequence:
1) create 2 pipes and a child (the 2 pipes will be used for two-way communication between the parent
and son);
2) the father, after the creation of the child, takes in input from the user a file name;
3) send the child the name of the file using the first pipe;
4) make the child look for the number of spaces in the file and communicate this number to the father through the use of the second pipe;
5) let the father print the number received from son;
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main (int argc, char *argv[])
{
int f2s[2];
int s2f[2];
int fd, n;
pid_t p;
char buf[20];
char c;
int countspace=0, valueofspace;
if (argc<2)
{
printf("ERROR!\n");
exit (-1);
}
if (pipe (f2s) == -1 && pipe (s2f) == -1)
exit(-1);
p=fork();
if (p>0)
{
printf("I'm inside the father process.\n");
close(f2s[0]);
close(s2f[1]);
write(f2s[1],argv[1],sizeof(argv[1]));
read(s2f[0],&valueofspace, sizeof(int));
printf("The spaces are %d", valueofspace);
printf("Father exit\n");
exit(0);
}
if (p==0)
{
printf("I'm inside the child process.\n");
close(f2s[1]);
close(s2f[0]);
read (f2s[0],buf,20);
if (fd = open(buf, O_RDONLY) == -1)
printf("Error when opening the file\n");
while (read(fd,&c,1) > 0)
{
if (c==' ')
countspace++;
}
close(fd);
printf("Count: %d\n",countspace);
n = write(s2f[1],&countspace, sizeof(countspace));
printf("WRITE of %d BYTES\n", n);
printf("Son exit \n");
exit(0);
}
}
You have closed the reading end of the pipe s2f before writing into it.
close(s2f[0]);
So there will be a EPIPE error when you write into the pipe. From the online reference on write (with added emphasis):
The EPIPE error occurs when fd is connected to a pipe or socket whose reading end is closed. When this happens the writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)
Since your program does not catch, block or ignore this signal, the write return value is not as expected and instead is -1.
Related
I have a homework question:
Q7: After executing the following code, a new file named myFile.txt is generated. Is the content in myFile.txt will be consistent? Why?
And here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
printf("hello world (pid:%d)\n", (int)getpid());
int fd = open("myFile.txt", O_CREAT|O_RDWR);
if(fd == -1 ) {
printf("Unable to open the file\n exiting....\n");
return 0;
}
int rc = fork();
if (rc < 0) {
fprintf(stderr, "fork failed\n");
exit(1);
}
else if (rc == 0) {
printf("hello, I am child (pid:%d)\n", (int)getpid());
char myChar='a';
write(fd, &myChar,1);
printf("writing a character to the file from child\n");
}
else {
printf("hello, I am parent of %d (pid:%d)\n",
rc, (int)getpid());
char myChar='b';
write(fd, &myChar,1);
printf("writing a character to the file from parent\n");
}
return 0;
}
The parent will write "a" into myFile.txt while the child writes "b" into that file. I executed this program several times, finding the txt file consistent being "ba", meaning that the parent always comes after the child. However, I noticed that there's no wait() function called by the parent process. Can someone explain why the parent comes after the child?
The order of concurrent tasks are implementation defined by the OS. You need to use synchronization primitives to ensure a well defined order of actions if required (file locks, pipes, mutex, condition variables, semaphores etc).
I wrote a simple script (taken from a tutorial) which writes data to one end of a pipe in a child process, and reads it from the other end of the pipe in the parent process:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid;
int mypipefd[2];
int ret;
char buf[20];
ret = pipe(mypipefd);
if (ret == -1) {
printf("Pipe failed.\n");
exit(1);
}
if ((pid = fork()) == -1) {
printf("Fork failed.\n");
exit(1);
} else if (pid == 0) {
printf("Child process.\n");
char msg[] = "Hello there!";
write(mypipefd[1], msg, strlen(msg) + 1);
} else {
printf("Parent process.\n");
read(mypipefd[0], buf, 15);
printf("Buf: %s\n", buf);
}
return 0;
}
This works fine and outputs the results I expect:
Parent process.
Child process.
Buf: Hello there!
[ project ] $
Then as I got more familiar with the code, I wondered why we need to use mypipefd[2] and pipe() to achieve this goal, or whether mypipefd[1] by itself would work. So I tried it out with the following code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid;
int my_array[1];
char buf[20];
if ((pid = fork()) == -1) {
printf("Fork failed.\n");
exit(1);
} else if (pid == 0) {
printf("Child process.\n");
char msg[] = "Hello there!\n";
write(my_array[0], msg, strlen(msg) + 1);
} else {
// wait(NULL);
printf("Parent process.\n");
read(my_array[0], buf, 15);
printf("Buf: %s\n", buf);
}
return 0;
}
This code outputs the same text, but it hangs after it finishes printing.
Parent process.
Child process.
Buf: Hello there!
No prompt, this time. I even tried un-commenting that call to wait(NULL), on the off-chance that the root cause was a conflict between parent and child processes. No such luck.
What's going on here? Why am I unable to read and write to a length-of-one array in this way without the program hanging? What exactly is the compiler stuck on?
A pipe, on computers as well as in real life, have two ends. And like pipes in real life, data flows from one end of the pipe (the write end) to the other (the read end).
The pipe function gives you those two ends by writing them to an array of two file-descriptors. The first element of the pair is read-only, and the second is write-only.
The pipe() function accepts an array of 2 integer as an input argument.
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
It then generates a new pipe object, and initializes the pipefd array with file descriptors for read and write operation.
What you try to do is call read() and write() using some arbitrary, uninitialized ints (or file descriptor). Meaning the OS did not allocate a pipe object and did not provide you with file descriptors (the pipe's API) to be use with read() and write().
This (calling read() or write() with uninitialized file descriptor) will result in "undefined behavior".
"I find that a good working definition of "undefined behaviur" is "works for me, works for you, works during development and QA, but blows up in your most important customer's face"" --- Scott Meyers
I am trying to communicate with forked child processes via pipe redirection of stdin and stdout in C. I already managed to get this to work for shell commands (like ls, for example) executed in child processes. However, I wasn't able to recursively execute the same program and redirect the output (printed by printf(), fprintf() to stdout, ...) via the pipes from the child process to the parent (in this test to stdout of the parent), although this works fine for ls or similar commands.
Here's how I tried to approach this:
I create a pipe, the reading end is for the parent, the child process should write to the writing end.
The Process forks, both processes close the unused end, respectively.
The writing end of the pipe is redirected to STDOUT_FILENO and closed
The child process executes the program recursively (it is called ./to2)
As mentioned, this does work if I execute ls in the child process, but not if I try to call the same program recursively. Here's my test program where I tried to get this to work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);
//I want this to work:
//execlp("./to2", "./to2", "-e");
//This works fine:
execlp("ls", "ls");
exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);
char reading_buf[1];
while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}
For testing purposes, the function is called recursively with additional arguments, while the parent program is called without additional arguments (hence the if(argc>1)).
In the final program, endless recursion is being avoided by other means.
Did I understand something wrongly? I am pretty confused by the fact that the only thing that doesn't seem to work is redirecting the output of my own
program...
Thank you very much in advance, any help or ideas are greatly appreciated.
The primary problem is precisely as outlined in the comments — you are not calling execlp() correctly (nor ls in the alternative). You must make the last argument on those function calls into an explicit null pointer, as shown in this code, which is a mostly mildly edited version of what's in the question:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);
char reading_buf[1];
while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}
Two important changes should be highlighted:
This code echoes the first line of data — the one written by dprintf() — whereas the original code just read it and discarded it.
The wait() call is after the input, not before. If the child had more data to write than a set of fixed messages, it could block waiting for the parent to read some of the data, while the parent is blocked waiting for the child to exit. This would be a deadlock.
The usage() function is not appropriately named — it doesn't report how to run the program. I also exit with a failure status, not success, if the child process fails the execlp().
Under peculiar circumstances, the wait() call might report on the exit status from some child other than the one that was forked. It is generally best to use a loop to reap such children. However, the circumstances required are extremely peculiar — the process which launched the parent with an exec*() function must have previously created some children for which it didn't wait, so that they are inherited by the parent process (because the PID doesn't change across an exec*() call).
I want to try Pipe communication with child and parent process. Parent process write to pipe and child process read this but my program get error "write: Broken pipe". How can I change this code?
Thnks.
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <errno.h>
#include <fcntl.h>
int main(void)
{
int i=0;
int child=5;
int fdp;
int fds[2];
int controlRead;
int controlWrite;
char pathName[30] = {"Trying Pipe Communication\n"};
if(pipe(fds) < 0)
{
perror("pipe");
exit(EXIT_FAILURE);
}
do{
if(child == 0)
{
close(fds[1]);
if( (controlRead = read(fds[0],pathName,sizeof(pathName)) ) <= 0)
{
perror("read");
exit(EXIT_FAILURE);
}
close(fds[0]);
printf("boru :%s\n",pathName);
wait();
}
else
{
printf("Parent process\n");
close(fds[0]);
if( (controlWrite = write(fds[1],&pathName,sizeof(pathName))) <= 0)
{
perror("write");
exit(EXIT_FAILURE);
}
close(fds[1]);
}
i++;
child = fork();
}while(i<3);
return 0;
}
error "write: Broken pipe". How can I change this code?
Don't break the pipe before you write to it. On the first pass through your do/while loop, the parent closes the read end and then writes to the remaining pipe fd. Kablam. EPIPE.
Your read loop shall count number of bytes read before closing the socket. Otherwise it is terminated too early.
Pipes are not packet transport, and single read/write is actually a series of operations. So when you are writing an array, it is wrong to assume it will come in one piece.
I want to make a simple program, that fork, and the child writes into the named pipe and the parent reads and displays from the named pipe.
The problem is that it enters the parent, does the first printf and then it gets weird, it doesn't do anything else, does not get to the second printf, it just ways for input in the console.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main()
{
char t[100];
mkfifo("myfifo",777);
pid_t pid;
pid = fork();
if (pid==0)
{
//execl("fifo2","fifo2",(char*)0);
char r[100];
printf("scrie2->");
scanf("%s",r);
int fp;
fp = open("myfifo",O_WRONLY);
write(fp,r,99);
close(fp);
printf("exit kid \n");
exit(0);
} else
{
wait(0);
printf("entered parent \n"); // <- this it prints
// whats below this line apparently its not being executed
int fz; printf("1");
fz = open("myfifo",O_RDONLY); printf("2");
printf("fd: %d",fz);
char p[100];
int size;
printf("------");
//struct stat *info;
//stat("myfifo",info); printf("%d",(*info).st_size);
read(fz,p,99);
close(fz);
printf("%s",p);
printf("exit"); exit(0);
}
}
You really should be checking the return value on function calls for errors, especially mkfifo() and open().
Your call to wait() is going to cause problems in its current location. Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa1. The parent is waiting for the child to terminate and the child is waiting for a reader process, i.e., the parent, to connect to the FIFO.
1 - see note on open() below for using O_NONBLOCK with a FIFO
Moving the wait() call to just before the parent process exits along with changing the mode in the call to mkfifo() to 0666 seems to resolve some of your immediate problems.
It is also good practice to remove the FIFO when you are finished with it.
unlink("myfifo");
From the open() function documentation in IEEE Std 1003.1-2004:
When opening a FIFO with O_RDONLY or O_WRONLY set:
If O_NONBLOCK is set, an open() for reading-only shall return without delay. An open() for writing-only shall return an error if no process currently has the file open for reading.
If O_NONBLOCK is clear, an open() for reading-only shall block the calling thread until a thread opens the file for writing. An open() for writing-only shall block the calling thread until a thread opens the file for reading.
The following example is a combination of the code in your original question and the FIFO page of Beej's Guide to Unix IPC:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define FIFO_NAME "myfifo"
int main(void)
{
char buf[256];
int num, fd;
pid_t pid;
if (mkfifo(FIFO_NAME, 0666) < 0)
perror("mkfifo");
pid = fork();
if (pid == 0)
{
printf("child - waiting for readers...\n");
if ((fd = open(FIFO_NAME, O_WRONLY)) < 0)
perror("child - open");
printf("child - got a reader -- type some stuff\n");
while (fgets(buf, sizeof(buf), stdin), !feof(stdin))
{
if ((num = write(fd, buf, strlen(buf))) < 0)
perror("child - write");
else
printf("child - wrote %d bytes\n", num);
}
close(fd);
exit(0);
}
else
{
printf("parent - waiting for writers...\n");
if ((fd = open(FIFO_NAME, O_RDONLY)) < 0)
perror("parent - open");
printf("parent - got a writer\n");
do
{
if ((num = read(fd, buf, sizeof(buf))) < 0)
perror("parent - read");
else
{
buf[num] = '\0';
printf("parent - read %d bytes: \"%s\"\n", num, buf);
}
} while (num > 0);
close(fd);
wait(0);
}
unlink(FIFO_NAME);
return 0;
}
This example was tested in Linux. Press Ctrl-D to terminate the program.
First of all, try fprintf to stderr instead of printf (to stdout)
The stderr is unbuffered.
Then you can tell what actually gets printed and what does not.
or at least add fflush before waiting for anything.