Unidirectional pipes in C - c

I'm having trouble trying to work out how to make individual pipes for a parent process and a child process to act in a unidirectional fashion. I.e.: a descriptor for the parent and a different descriptor for its child.
Here's what I have:
#include <sys/types.h>
int main(int argc, char *argv[]) {
int parent[2];
int child[2];
pid_t pid;
int num = 0;
pipe(parent);
pipe(child);
pid =fork();
if(pid > 0){ // do parent stuff
num = 5;
write(parent[1], &num, sizeof(num));
printf("Parent with pid %d sent value: %d\n", getpid(), num);
close(parent[1]);
}else{ // do child stuff
read(child[0], &num, sizeof(num));
printf("Child with pid %d received value: %d\n", getpid(), num);
close(child[0]);
exit(0);
}
return 0;
}
The output:
Parent with pid 31702 sent value: 5
I know I'm supposed to close some of the descriptors at some point in there before the read() and write() commands, but it seems no matter what I close either the child response prints before the parent can write() or I end up with a broken pipe. Where should I close the descriptors to successfully use these pipes unidirectionally?

In a nutshell, that's not how you should work with pipes.
A pipe has a read 0 and write 1 end.
In your case, the child is reading from child[0], but nobody is going to write to the child through child[1]. The parent is going to write to the parent[1].
Try using a single pipe (change child[0] to parent[0]). And make sure you delete the ends you wont be using in the respective processes

You're making two pipes and then only using one end of each of them; the communication doesn't work because nobody is paying any attention to the other end of either. parent and child are totally independent pipes, what you send on one doesn't appear on the other. You only need one pipe:
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t pid;
int num = 0;
pipe(pipefd);
pid = fork();
if(pid > 0){ // do parent stuff
close(pipefd[0]); // close the reading end
num = 5;
write(pipefd[1], &num, sizeof(num)); // write on the writing end
printf("Parent with pid %d sent value: %d\n", getpid(), num);
}else{ // do child stuff
close(pipefd[1]); // close the writing end
read(pipefd[0], &num, sizeof(num)); // read on the reading end
printf("Child with pid %d received value: %d\n", getpid(), num);
exit(0);
}
return 0;
}
A pipe is already unidirectional, and it gives you two ends back: a writing end and a reading end. You just have to use each one appropriately.

Related

Creating n childs process with fork() and treat them after

I want to create n child processes by fork () inside a for loop, and treat the child processes later once they have all been created.The child processes must be treated once the
execution of the parent process has been carried out.
int main(int argc, char *argv[])
{
char cadena[STRLONG];
pid_t pid;
for(int i =0; i<5; i++){
pid = fork();
if(pid == -1){
perror("Error\n");
exit(-1);
}
else if(pid == 0){
break;
}
}
if (pid > 0){
printf("I'm the parent, --> %d\n", getpid());
}
else if(pid == 0){
printf("I'm the child --> %d \n", getpid());
exit(0);
}
for(int i = 0; i<5; i++){
wait(NULL);
}
}
This is what I have done, but the child processes are executed before they are all created and I don't know how to solve it ...
When you fork(), the parent and child process will run in parallel immediately from the place where you fork().
time parent child
| |
| |
| fork()--------+
| | |
V | | ​
There is no way of telling which one of them that does something before the other - unless you synchronize their actions in some way.
To do proper synchronization between processes you can use semaphores or some other interprocess communication technique. For this simple case, you could use the old self-pipe trick.
Create a pipe
When a child is created, close the writing end of the pipe in the child - and try reading a byte from the pipe. This will hang until there is a byte or the pipe is closed.
When all children have been created, close the reading end in the parent.
The state at this point should be:
The parent only has the write end of the pipe open.
All the children only have the read end of the pipe open, eagerly waiting for something to happen in the pipe.
When the parent want all the children to start working, close the write end of the pipe in the parent. This will cause the read operation in all the children to unblock.
There's no error checking in this below, but it'll show the idea:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
enum { P_RD, P_WR }; // pipe indices, 0 and 1
int main() {
pid_t pid;
int pip[2];
pipe(pip); // create a pipe from pip[P_WR] to pip[P_RD]
for(int i = 0; i < 5; i++) {
pid = fork();
if(pid == -1) {
perror("Error\n");
exit(1);
} else if(pid == 0) {
close(pip[P_WR]); // close write end of pipe
char ch; // dummy buffer
read(pip[P_RD], &ch, 1); // hang here until closed
close(pip[P_RD]); // close read end of pipe
printf("I'm the child --> %d \n", getpid());
exit(0);
}
}
close(pip[P_RD]); // close read end of pipe
// The state at this point:
// * The parent only has the write end of the pipe open.
// * All the children only have the read end of the pipe open.
printf("I'm the parent --> %d\n", getpid());
close(pip[P_WR]); // close write end of pipe to start children
int wstatus;
while((pid = wait(&wstatus)) != -1) {
printf("%d died\n", pid);
}
}

Using two pipes to communicate between parent process and child process

Problem
I only get this in the terminal output. I believe the program is getting stuck at the fork() call but I don't know exactly why.
The name of the program is q9:
prompt>$ ./q9 inputString
Parent: writing to pipe 'inputString'
Task
read input from terminal into the parent-to-child pipe.
fork() to create a child process.
read input from parent-to-child pipe.
concatenate some other string to that string read in from the pipe.
write the newly concatenated string to the child-to-parent pipe.
in the parent, read from the child-to-parent pipe and print the output read from the pipe to the terminal.
Attempts
I have tried fixing this by:
attempting to close pipes in different places. I thought I may have missed something or left something open, but I don't think so.
placing a wait() in the parent because perhaps it wasn't letting the child run completely
attempted to print the output of the concatenated string just in case it was that messing up the prints.
Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main (int argc, char *argv[]) {
// parent RUN
if(argc == 1) {
printf("usage: q9 <string>\n");
return 0;
}
// create two way pipes
int parent_fds[2], child_fds[2];
// create strings to save too
char fromParent[100];
char fromChild[100];
// read:[0] - write:[1]
if (pipe(parent_fds) != 0 && pipe(child_fds) != 0) {
fprintf(stderr, "pipes failed!\n");
}
// close unused pipe end by parent
close(parent_fds[0]);
close(child_fds[1]);
close(child_fds[0]);
// write from terminal to parent pipe FOR child to read
printf("Parent: writing to pipe '%s'\n", argv[1]);
write(parent_fds[1], argv[1], strlen(argv[1]));
close(parent_fds[1]);
// fork() child process
int child = fork();
// NEVER GETS PASSED HERE :(
if (child < 0) {
fprintf(stderr, "fork failed!");
exit(1);
} else if (child == 0) {
printf("I reached the child :)");
// close unwanted pipe ends by child
close(child_fds[0]);
close(parent_fds[1]);
// read from parent pipe
int n = read(parent_fds[0], fromParent, 100);
fromParent[n] = 0;
printf("Child: reading from parent pipe '%s'\n", fromParent);
close(parent_fds[0]);
// Concatinate to what was read in
const char myText[14] = " (added this.)";
strcat(fromParent, myText);
write(child_fds[1], fromParent, strlen(fromParent));
close(child_fds[1]);
printf("Child: writing to pipe - '%s'\n", fromParent);
} else {
// read from child pipe
int n = read(child_fds[0], fromChild, 100);
fromChild[n] = 0;
printf("Parent: reading from pipe - '%s'\n", fromChild);
}
return 0;
}
What is going wrong?
There were several problems, and your diagnostic messages were not guaranteed to appear. Make sure you end your messages with newlines.
You only created one pipe because you used && instead of ||.
You closed the pipes 'for the parent' before you'd created the child (also noted by kaylum in a comment).
There are multiple other cleanups in the code below. The code (still) does not ensure that the write-to-pipe operations succeed (they were failing before). It does ensure that the strings read from the pipes are not longer than the buffers in which they are placed; it doesn't ensure there's enough space to append the extra information in the child. The code shown waits for any child processes to complete before exiting. The child executes the wait() call but it immediately fails (and the child doesn't print anything) and it exits. The parent waits for the child to complete and reports on it doing so before exiting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc == 1)
{
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
return EXIT_FAILURE;
}
// create two pipes:
// - parent_fds used by parent to write to child
// - child_fds used by child to write to parent
int parent_fds[2], child_fds[2];
// read:[0] - write:[1]
if (pipe(parent_fds) != 0 || pipe(child_fds) != 0) /* || not && */
{
fprintf(stderr, "pipes failed!\n");
return EXIT_FAILURE;
}
// fork() child process
int child = fork();
if (child < 0)
{
fprintf(stderr, "fork failed!");
return EXIT_FAILURE;
}
else if (child == 0)
{
printf("%d: I reached the child :)\n", (int)getpid());
// close unwanted pipe ends by child
close(child_fds[0]);
close(parent_fds[1]);
// read from parent pipe
char fromParent[100];
int n = read(parent_fds[0], fromParent, sizeof(fromParent) - 1);
fromParent[n] = '\0';
printf("%d: Child: read from parent pipe '%s'\n", (int)getpid(), fromParent);
close(parent_fds[0]);
// Append to what was read in
strcat(fromParent, " (added this.)");
write(child_fds[1], fromParent, strlen(fromParent));
close(child_fds[1]);
printf("%d: Child: writing to pipe - '%s'\n", (int)getpid(), fromParent);
}
else
{
// close unwanted pipe ends by parent
close(parent_fds[0]);
close(child_fds[1]);
// write from terminal to parent pipe FOR child to read
printf("%d: Parent: writing to pipe '%s'\n", (int)getpid(), argv[1]);
write(parent_fds[1], argv[1], strlen(argv[1]));
close(parent_fds[1]);
// read from child pipe
char fromChild[100];
int n = read(child_fds[0], fromChild, sizeof(fromChild) - 1);
fromChild[n] = '\0';
close(child_fds[0]);
printf("%d: Parent: read from pipe - '%s'\n", (int)getpid(), fromChild);
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("%d: child PID %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
return EXIT_SUCCESS;
}
Sample output (source pipe43.c, program pipe43):
$ pipe43 'Nobody expects the Spanish Inquisition!'
84543: Parent: writing to pipe 'Nobody expects the Spanish Inquisition!'
84544: I reached the child :)
84544: Child: read from parent pipe 'Nobody expects the Spanish Inquisition!'
84544: Child: writing to pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: Parent: read from pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: child PID 84544 exited with status 0x0000
$

Does anyone see any mistakes here? I am trying to get a message via a pipe, from my parent to child

The problem here is that the child process does not wait for the message to arrive from the function startWorking(), and because of that I am getting a random char as output or sometimes nothing.
I am sending a char array from startWorking() to the pipe and I am making sure only the parent does this job.
One solution would be, sending a signal from startWorking() to the child processor, after writing into the pipe.
But the read() function behavior is waiting for the pipe to receive the message and only then read the message, but somehow it's not doing that, or maybe there is a problem in writing the message.
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
int mypipefd[2];
if (pid > 0)
{
if (pipe(mypipefd) == -1)
{
perror("Pipe failed\n");
exit(EXIT_FAILURE);
}
storeEngine(mypipefd);
}
else if(pid < 0)
{
perror("fork call failed \n");
exit(EXIT_FAILURE);
}
else
{
printf("I am the child \n");
printf("child: %d \n", getpid());
char message[6];
close(mypipefd[1]);
read(mypipefd[0], &message, 6);
close(mypipefd[0]);
printf("child read value:\n ");
printf("%s \n", message);
}
return 0;
}
void startWorking(int *mypipefd)
{
printf("%d \n" ,getpid());
//close(*mypipefd);
write(*(mypipefd+1), "hello", 6);
close(*(mypipefd+1));
}
Notice that if I remove the two slash behind close(*mypipefd) the program will never finish, and it will get stuck there.
Without examining the rest of your code, you need to call pipe() before you call fork() so the pipe can be used by both the parent and the child process. If you call pipe() after you call fork(), the pipe is only usable by that one process.
More like this:
int main(int argc, char const *argv[])
{
int mypipefd[2];
if ( pipe( mypipefd ) == -1 )
{
perror("Pipe failed\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid > 0)
{
storeEngine(mypipefd);
}
.
.
.
No, pipes used to communicate between processes should be created before the fork() (otherwise, you have no easy way to send thru them, since the reading and the writing ends should be used by different processes).

creating pipe between father and child process

I'm trying to create a pipe between father and child process.
in this pipe, the child process will write data and the father will read and print it.
I don't know why but if I enter a big string the data got wrong, for strings with +- 7 words it still do fine.
I guess it is about the size of the buffer but can't fix it.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* in this code i will make a child process with fork command
then i will create pipe using pipe commands.
i will transfer data from the child process to the father process
omriziner code
*/
void main(int argc,char *argv[])
{
if(argc < 2){
printf("prototype error \n<Enter any data you wana write> \n");
return;
}
int fd[2]; // creating array with 2 places for 2 fd'stdio
// fd[0] is set to read file in the pipe
//fd[1] is set to write file in the pipe
int piperes;
pid_t childpid;
char buff[5];
char * data = "learning to the exam";
printf("father pid %d:\n",getpid());
printf ("size of data is %d \n",(int)sizeof(argv[1]));
printf ("size of buff is %d \n",(int)sizeof(buff));
piperes = pipe(fd);
if(piperes < 0){
perror("PIPE ERR");
exit(1);
}
printf("Pipe succeed \n");
if((childpid = fork()) == -1){ // fork will create a child process
perror("FORK ERR");
exit(1);
}
// when fork suceed - the pid of the child will return in the parent and 0 will return in the child
// when fork fail - the pid will be -1
printf("Fork succeed, fork return is %d and process pid is %d :\n",childpid,getpid());
if(childpid == 0){ // if pid zero , wer in the child prcs
close(fd[0]);
write(fd[1],argv[1],sizeof(argv[1])); // send data to the write fd of the pipe
printf("data was written to fd[1] by pid : %d \n",getpid());
exit(0);
}
else{ // in this case, we're in the father process
close(fd[1]);
read(fd[0],buff,sizeof(argv[1])+1);
printf("Recived data is ''%s''", buff);
printf("By pid : %d \n",getpid());
exit(1);
}
}
sizeof(argv[1])
This does not do what you think it does.
sizeof is evaluated at compile-time1, and in this case will return 8 (assuming you're on a 64-bit machine), because argv[1] is a pointer.
Because you want the length of the string (which can only be known at run-time), you should instead use:
strlen(argv[1])
1 - There are cases where sizeof is evaluated at run-time. This is not one of them.

Parent process does not reap child return variable

This is for a class so I am trying to understand why the variable nChars is not being set when the child process returns. I have read that waitpid() reaps the child process but when I try to print nChars it still shows zero when the childs' nChars is the number of the commandline characters
int main(int argc, char **argv)
{
// set up pipe
int fd[2], status;
pid_t childpid;
pipe(fd);
// call fork()
if((childpid = fork()) == -1){
perror("pipe");
return -1;
}
if (childpid == 0) {
// -- running in child process --
int nChars = 0;
char ch;
close(fd[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while(read(fd[0], &ch, 1) == 1)nChars++;
// Return number of characters counted to parent process.
printf("child returns %d\n", nChars);
close(fd[0]);
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
close(fd[0]);
printf("CS201 - Assignment 3 - \n");
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for(int i=1; i < argc; i++)
write(fd[1], argv[i], strlen(argv[i]));
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
waitpid(childpid, &status, WNOHANG);
printf("child counted %d characters\n", nChars);
close(fd[1]);
return 0;
}
Parent and child don't share memory, so they have different variables nChars. Child is a COPY of parent, so when you change some variables in copy they doesn't get changed in original.
If you need to have one variable visible from two execution flows use threads.
You're returning nChars from child as process exit code, so it'll be in status variable.
Try:
waitpid(childpid, &status, 0);
// removed WNOHANG because with it parent won't wait for child to exit
printf("child counted %d characters\n", status);
But it would better to use come IPC mechanism like pipes or sockets to transfer data between child and parent, because exit codes are for program exit status, exit code 0 means all is okay, and other exit codes mean that something gone wrong, exit code is not for transferring arbitrary data

Resources