how does synchronization work in this example? - c

I am trying to implement pipe system call as part of my semester project.
I came across the following code here
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pfd[2];
pid_t cpid;
char buf;
assert(argc == 2);
if (pipe(pfd) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
cpid = fork();
if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); }
if (cpid == 0) { /* Child reads from pipe */
close(pfd[1]); /* Close unused write end */
while (read(pfd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pfd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pfd[0]); /* Close unused read end */
write(pfd[1], argv[1], strlen(argv[1]));
close(pfd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
Having studied Multiprocessor programming, I realize this is a producer consumer model. The main process is forking a child which is consuming the bytes written by the main process in the write end of the pipe.
I am not able to understand how the synchronization is working here. I mean how does the parent notify the child that it has written n number of bytes in the write end of the pipe?
Does closing unused fd's has anything to do with synchronization?
What if in this example, I want child to write something in the write end and parent to read it?
Any help would be great, thanks!

Related

why does my program get stuck in wait(NULL)?

I'm a c beginner and wrote a multiprocess program. I want to let my child process invoke strace and then pipe to the parent process so that parent process could print it.
But my parent progress seem to be getting stuck in wait(NULL); . I tried commenting code wait(NULL); and I got the output from my child process. I can't figure out why parent process keeping waiting. Hasn't the child process returned yet?
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t pid;
char *exec_argv[] = { "/bin/strace", "-T", "tree", "/bin", NULL};
char *exec_envp[] = { "PATH=/bin", NULL };
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) { // child
close(pipefd[0]); /* close unused read end */
close(STDOUT_FILENO);
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
perror("dup2");
exit(EXIT_FAILURE);
}
// invoke strace
execve(exec_argv[0], exec_argv, exec_envp);
perror(exec_argv[0]);
exit(EXIT_FAILURE);
} else { // parent
close(pipefd[1]); /* close unused write end */
if (dup2(pipefd[0], STDIN_FILENO) == -1) {
perror("dup2");
exit(EXIT_FAILURE);
}
printf("I'm parent!!!\n");
wait(NULL);
char *line = NULL;
size_t len;
while (getline(&line, &len, stdin) != -1) {
printf("%s", line);
}
free(line);
}
return 0;
}
You didn't close pipefd[0] in the parent.
You didn't close pipefd[1] in the child.
Another problem is that your code is susceptible to deadlocks. If the child writes enough to the pipe to fill it, it will block until it has space to accept more. And since the the pipe is not emptied until the child exits, the child will never unblock.
This is easy to fix: Read until EOF, then call wait to reap the child. In other words, move the wait so it's after the loop.

Create and write function for pipe for usage in main()

The 'create_andWrite' function should create a pipe and write a string to it. The child process which is created in the main should read from the pipe.
I only worked with pipes in one method(e.g. read and write are both in main) before and I'm really struggling with this one.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int creat_and_write(FILE *fd, char *string) {}
int main() {
int fd[2];
creat_and_write();
int p = fork;
}
These are my functions signatures so far
Pipes are one way of unidirectional IPC, used usually with fork() to pass data in a sort of vertical hierarchy.
Hope the example below is clear. and spot the usage of the 2 functions.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define READ_END 0
#define WRITE_END 1
// pipe uses file descriptors to communicate between processes not pointers to files.
int creat_and_write(int *fd, char *string) {
// first and formost we need a pipe.
if (pipe(fd) == -1) {
perror("pipe error");
exit(1);
}
// now the fd contains 2 file descriptors, one for reading and one for writing to the pipe.
// we fork a child process which will inherit most internals including the pipe's file descriptors.
pid_t pid = fork();
if (pid == -1)
{
perror("fork error");
exit(1);
}
else if (pid == 0)
{
// we're at the child process.
// now we can write to the write end of the pipe
write(fd[WRITE_END], string, strlen(string));
// good practice to close done end of the pipe. for a full cleanup.
close(fd[WRITE_END]);
}
else
{
// we're at the parent process.
// now we can read from the read end of the pipe
char buffer[strlen(string)];
read(fd[READ_END], buffer, 100);
close(fd[READ_END]);
printf("now at the parent process: %s\n", buffer);
}
return 0;
}
int main() {
int fd[2];
creat_and_write(fd, "msg from child process");
return 0;
}

close pipe from parent and child process

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main() {
int p[2];
pipe(p);
if (fork() == 0) {
// child
/*#0*/ close(p[1]);
int received = -1;
while (read(p[0], &received, 4) != 0) {
printf("receive integer: %d\n", received);
received = -1;
}
printf("child exit\n");
exit(0);
} else {
// parent
/*#1*/ close(p[0]);
int sent = 42;
write(p[1], &sent, 4);
/*#2*/ close(p[1]);
printf("wait for child\n");
wait(0);
}
printf("finished\n");
}
I'm trying to understand fork and pipe in C. This program fork a child process, which receive an integer from parent process then exit when pipe closed. When executing, it prints
wait for child
receive integer: 42
child exit
finished
Yet the while loop got stuck after close(p[1]); at position #0 removed: that read would infinitely wait for an incoming variable from the pipe and never detect the pipe closed.
Can someone explain to me why p[1] has to be closed by both parent (position #2) and child (position #0) process?
Here is the code (from Linux manual page) with comments at the bottom of the code.
https://man7.org/linux/man-pages/man2/pipe.2.html
At /#2/ close(pipefd[1]), the comment states that "Reader will see EOF". It means there is nothing to read into child process anymore and then the statement "read(p[0], &received, 4)" will return 0. In the Linux manaul page https://man7.org/linux/man-pages/man2/read.2.html
states that "On success, the number of bytes read is returned (zero indicates end of file)"
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else {/* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
/*#2*/ close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}

C Programming: Segmentation Fault (Core Dumped)

The program i am trying to write is trying to demonstrate how IPC works on Linux, but i keep getting a core dump error. It compiles fine and will run up until the last output statement in the parent process.
My code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <err.h>
#include <sysexits.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#define SHM_SIZE 15
int main (int argc, char ** argv[]) {
pid_t pid; //pid variable of type pid
int shmid; //shared memory id
int key = 1776; //randomly chosen key
char *shm; //shared memory name
int pipefd[2];
char buff;
pid = fork(); //creating child process
pipe(pipefd); //creating pipe
if (pid < 0) {
fprintf(stderr, "Fork Failed");
return -1;
} else if (pid == 0) {
shmid = shmget(key, SHM_SIZE, 0);
shm = shmat(shmid, 0, 0);
char *n = (char *) shm;
printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
scanf("%s", n);
printf("\n");
///////////////////////////////////////////////////////////////////////////////////////
close(pipefd[1]);
printf("pipe opened on child end");
printf("\n");
while(read(pipefd[0], &buff, 1) > 0) {
write(1, &buff, 1);
}
write(1, "\n", 1);
close(pipefd[0]);
printf("pipe successfully closed");
printf("\n");
exit(EXIT_SUCCESS);
} else {
shmid = shmget(key, SHM_SIZE, 0777 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
wait(NULL);
printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
printf("\n");
//////////////////////////////////////////////////////////////////////////////////////
close(pipefd[0]);
printf("pipe open on parent end");
printf("\n");
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]);
printf("pipe successfully closed");
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
Does it have something to do with my args[]? Such as could i be accessing memory out of reach? Or am trying to access some invalid pointer?
Many Thanks!
You have several problems in your code
Create the pipe before the fork. You create the pipe twice, once for
the parent process and one for the child process. That makes no sense, the pipe
that the child created cannot be used by the parent. The pipe must already
exists so that the child inherits the file descriptors when the child is
created.
Usually the parent creates the shared memory and the child gets the shmid
from the parent when it does the fork. Otherwise you will have to synchronize
the child and parent. So I would put the creation of the shared memory before
the fork, so that the child inherits the shmid from the parent.
In the line char *n = (char *) shm; the cast is not needed, shm is
already a char*.
In the parent block after the fork, you do wait(NULL); and then proceed to
write into the pipe. That makes no sense and you block both parent and child.
The child blocks on read because the parent hasn't send anything through the
pipe, yet. And the parent blocks on wait, because the child never exits and thus
cannot send anything through the pipe. The parent must first send data
through the pipe, then wait for the child to exit.
In the child block you do scanf("%s", n);, you are not protecting you
against buffer overflows. scanf("%14s", n) would be better. Also you are not
checking if scanf read anything at all. If the user presses
CtrlD then stdin is closed, scanf fails. In that case
n might not be '\0'-terminated and this would lead to undefined behaviour
when the parent tries to print it. So it would be better:
if(scanf("%14s", n) != 1) // avoid buffer overflow
{
fprintf(stderr, "Child: cannot read from stdin\n");
n[0] = 0; // 0-terminating
}
In the parent block after the fork, you do wait twice, why?
Your main is wrong, it should be
int main(int argc, char **argv);
The parent sends the contents of argv[1] to the child through the pipe, but
you fail to check if argv[1] is not NULL. Use this at the start of the
program:
if(argc != 2)
{
fprintf(stderr, "usage: %s string\n", argv[0]);
return 1;
}
So the correct version would be:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <err.h>
#include <sysexits.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#define SHM_SIZE 15
int main (int argc, char **argv) {
pid_t pid; //pid variable of type pid
int shmid; //shared memory id
char *shm; //shared memory name
if(argc != 2)
{
fprintf(stderr, "usage: %s string\n", argv[0]);
return 1;
}
int pipefd[2];
char buff;
// create shared memory before the fork,
// otherwise you will need to syncronize parent
// and child
pipe(pipefd); //creating pipe before the fork
// parent creates shared memory, child inherits shmid
// after fork
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
pid = fork(); //creating child process
if (pid < 0) {
fprintf(stderr, "Fork Failed");
return 1; // return -1 would be the same as return 255
} else if (pid == 0) {
shm = shmat(shmid, 0, 0);
char *n = shm; // shm is already a char*
printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
if(scanf("%14s", n) != 1) // avoid buffer overflow
{
fprintf(stderr, "Child: cannot read from stdin\n");
n[0] = 0; // 0-terminating
}
printf("\n");
///////////////////////////////////////////////////////////////////////////////////////
close(pipefd[1]);
printf("pipe opened on child end");
printf("\n");
printf("Parent sends: ");
fflush(stdout);
while(read(pipefd[0], &buff, 1) > 0) {
write(1, &buff, 1);
}
write(1, "\n", 1);
close(pipefd[0]);
printf("pipe successfully closed");
printf("\n");
exit(EXIT_SUCCESS);
} else {
shm = shmat(shmid, 0, 0);
close(pipefd[0]);
printf("pipe open on parent end");
printf("\n");
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]);
printf("pipe successfully closed");
// not we wait for child to exit
wait(NULL);
printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
printf("\n");
//////////////////////////////////////////////////////////////////////////////////////
exit(EXIT_SUCCESS);
}
return 0;
}
And the output is:
$ ./b "message to child: stop playing video games!"
pipe open on parent end
hello i am the child process. my pid is 10969. what is your name?: Pablo
pipe opened on child end
Parent sends: message to child: stop playing video games!
pipe successfully closed
pipe successfully closed
This is Child's Parent. My pid is 10968. Nice to me you Pablo.
You are reading and writing from the same end of the pipe you create. Common practice is to read from end [1] and write to end [0]. Tell me if that helps. Additionally, it is also common practice to not have too much going on between the child and parent processes. Attempting to execute code in between segments (parent and child) usually ends up with a segmentation fault, even if your code compiles.

How to Create IPC (Interprocess Communication) C programme to create with two child process

I want to create a IPC c program to create one parent and two child's processes. My code is:
#include <stdio.h>
void main()
{
int pid, status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}
I want to show you an other code snippet to create one parent and two child process. This is what I am looking for. Now I want to write shell script for IPC, first take look of this code.
Note: there is an other code with same logic but different process names UP, uc1, uc2 e.g in this way we have two parent VP and UC and there childs vp1 vp2 and uc1 uc2.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(){
int mypipe_c1[2];
int ret_c1;
char buf_c1[6];
ret_c1 =pipe(mypipe_c1);
int mypipe_c2[2];
int ret_c2;
char buf_c2[6];
ret_c2 =pipe(mypipe_c2);
if(ret_c1 == -1)
{
perror("pipe");
exit(1);
}
pid_t vc1;
pid_t vc2;
vc1 = fork ();
if (vc1 == 0)
{
read(mypipe_c1[0], buf_c1 , 37);
printf("PIPE1 :%s\n", buf_c1);
printf (" vc1 : I'm the child! My pid is (%d)\n", getpid ());
close(ret_c1);
int fd;
char * fifo1 = "/tmp/fifo1";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(fifo1, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("FIFO1: %s\n", buf);
close(fd);
exit(0);
}
if(vc1 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
vc2 = fork ();
if (vc2 == 0)
{
printf ("vc2 : I'm the child! My pid is (%d)\n", getpid ());
read(mypipe_c2[0], buf_c2 , 37);
printf("PIPE2 %s\n", buf_c2);
int fd;
char * fifo2 = "/tmp/fifo2";
/* create the FIFO (named pipe) */
mkfifo(fifo2, 0666);
/* write "Hi" to the FIFO */
fd = open(fifo2, O_WRONLY);
write(fd, " assignment VU 2 ", sizeof(" assignment VU 2 "));
close(fd);
/* remove the FIFO */
unlink(fifo2);
exit(0);
}
else if (vc2 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
printf ("I'm the parent! My pid is (%d)!\n",getpid());
write(mypipe_c1[1], "I am going to close you carry on UC1 \n", 37);
write(mypipe_c2[1], "I am going to close you carry on UC2 \n", 37);
exit(0);
}
Now I want shell script such that VP and UP should be started when users types … script.sh start VP or UP. vc1, vc2, uc1,uc2 should be stoppable only using script.sh stop vc1 or vc2 or uc1 or uc2
script.sh connect command should create two fifo and connect processes as shown in figure.
So you are asking for methods for IPC, with the sample code you provided, I think the best one is the use of pipes.
From the pipe() man page:
A pipe is a unidirectional data channel that can be used for interprocess communication
Basically, it is handled like a pair of file descriptors. First, you must init the pipe, and then create the childs using the fork() call, so both parents and childs share the resource. Then, using write and read methods, you can send data between them.
In this example I create a child which reads some data from the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pid;
char buffer[255];
int fd[2]; // channel 0 for reading and 1 for writing
pipe(fd);
pid = fork();
if(pid == 0) {
close(fd[1]); // close fd[1] since child will only read
read(fd[0], &buffer, sizeof(buffer));
close(fd[0]);
exit(0);
} else { // parent
close(fd[0]) // close fd[0] since parent will only write
// init buffer contents
write(fd[1], &buffer, sizeof(buffer));
close(fd[1]);
}
return 0;
}
As you can see pipe creates a pair of file descriptors, one for writing (number 1) and one for reading (number 0).
In my sample code, the child process closes the writing one, since it will only read, and the parent closes the reading one, since it will only write data.
Note that pipes are unidirectional, so if you want that both the childs and the parent write and read data from it, you should create two pipes (so 4 file descriptors) for each of the childs. An example of how to handle that situation:
int pipeA[2], pipeB[2];
pid = fork();
if (pid == 0) { // child will write to pipeB and read from pipeA
close(pipeA[1]); // closing pipeA writing fd
close(pipeB[0]); // closing pipeB reading fd
write(pipeB[1],&buffer, sizeof(buffer));
read(pipeA[0], &buffer2, sizeof(buffer2));
close(pipeA[0]);
close(pipeB[1]);
exit(1);
} else { // parent will write to pipeA and read from pipeB
close(pipeA[0]); // closing pipeA reading fd
close(pipeB[1]); // closing pipeB writing fd
read(pipeB[0], &buffer, sizeof(buffer));
write(pipeA[1], &buffer2, sizeof(buffer2));
close(pipeA[1]);
close(pipeB[0]);
}
If you want more info about pipes you can check the man page here.
Also, other simple ways of IPC would be the use of Unix Sockets, although I think that for the example you presented pipes will be enough.
You'r code create one parent and one child, not two child, so you need to add another fork into child block :
#include <stdio.h>
void main()
{
int pid,status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
fork();// another child
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}

Resources