Race Condition with fork() and pipe() - c

I am writing a program for an assignment where a parent plays connect four against N child processes. The program uses pipes to communicate game moves between processes.
I am having a problem fixing a race condition that is present in my program, however. There is a condition where the child process hangs one its read() call after the game has finished. This only seems to happen when there is more than one child process.
I've tried several things, such as named semaphores, but I'm still pretty new to forks, pipes, and IPC. I've posted a gist with relevant code (I tried to clean it up as best as I can for readability) here:
Gist with relevant code
Any help would be greatly appreciated
EDIT
Here's the relevant source from the gist with the declarations added.
int main (int argc, char const *argv[])
{
int dimension = 8, children = 2, i;
int child_play_to_win = 0;
int fd[children][4];
pid_t childpid[children];
Board** boards = (Board**) malloc(sizeof(Board*) * children);
GameMove* lastMove, *tmpMove;
char buf[80];
for(i = 0; i < children; i++) {
generate_board(&(boards[i]), dimension);
int tmp[2];
pipe(tmp);
// child read
fd[i][0] = dup(tmp[0]);
// parent write
fd[i][1] = dup(tmp[1]);
pipe(tmp);
// parent read
fd[i][2] = dup(tmp[0]);
// child write
fd[i][3] = dup(tmp[1]);
childpid[i] = fork();
if(childpid[i] == -1) {
perror("fork");
exit(1);
}
if(childpid[i] == 0) {
srand(getpid());
close(fd[i][1]);
close(fd[i][2]);
while(!boards[i]->finished) {
// Read in move from parent
printf("child[%d] about to read\n", getpid());
read(fd[i][0], &buf, sizeof(GameMove));
// repeat parent move on this board
if(gameNotFinished) {
// make child move
// write move back to parent
write(fd[i][3], lastMove, sizeof(GameMove));
// If the board is finished (there was a win),
if (!gameNotFinihsed) {
// Child wins
close(fd[i][0]);
close(fd[i][3]);
printf("child[%d] ending\n", getpid());
break;
}
}
else {
// Parent won
close(fd[i][0]);
close(fd[i][3]);
break;
}
}
dealloc(boards[i]);
exit(0);
}
}
// When this hits children amount, all games are done
int games_complete = 0;
// Make first move to all children
for (i = 0; i < children; i++) {
close(fd[i][0]);
close(fd[i][3]);
lastMove = placePieceAtBestPosition(boards[i], 1);
printf("parent writing to child[%d]\n", childpid[i]);
write(fd[i][1], lastMove, sizeof(GameMove));
}
while (games_complete != children) {
for (i = 0; i < children; i++) {
// Read move from child
read(fd[i][2], &buf, sizeof(GameMove));
// repeat child move
// Check for a child win...
if (!checkForWin(boards[i], 2)) {
// No win yet, place piece at best position
lastMove = placePieceAtBestPosition(boards[i], 1);
// check for win again
boards[i]->finished = checkForWin(boards[i], 1);
// Write move back to child
write(fd[i][1], lastMove, sizeof(GameMove));
// If we won, close everything up and increment
// the games_complete counter.
if(boards[i]->finished) {
close(fd[i][1]);
close(fd[i][2]);
games_complete++;
}
} else {
// write back child move if there was a win
write(fd[i][1], lastMove, sizeof(GameMove));
close(fd[i][1]);
close(fd[i][2]);
printf("Parent lost! ):\n");
games_complete++;
}
}
}

I think I know what your problem is. When you fork each child, you close the parent side of its pipes. however, each child still has open the parent side of the pipes for all of the previous children. Because of this, only the last-created child will have its parent-sides of its pipes closed.
Suggest you change:
close(fd[i][1]);
close(fd[i][2]);
to something like:
for (j = 0; j <=i; j++) {
close(fd[j][1]);
close(fd[j][2]);
}

Related

Pipe's related arguments to pass to a function?

I'm a beginner in C programming and I started learning about pipes today.
I need them because my program has to run up to 4 processes at the time, so to avoid creating more processes than those required, I have to use a shared variable between all of them to keep track how may can still be created.
I tried to simplify my program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void forking(int p, int pid);
int main(int argc, char *argv[])
{
int fd[2];
int p = 4; // Max number of processes that can run at the same time
int pid;
if(pipe(fd) == -1)
{
perror("pipe: ");
return 0;
}
//It will try the function forking 10 times to execute SOME CODE that
// changes everytime something operates on it
for(int i = 0; i < 10; i++)
{
forking(p, pid);
}
return 0;
}
void forking(int p, int pid)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
My 2 doubts is whether I should pass something else to the function "forking" (which can be recursive), like "fd", or if it is okay to just leave the code like this, and whether this will have the desired result.
Hopefully I made myself clear enough.
EDIT 1:
void forking(int p, int pid, int *fd)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid, fd);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
Passing fd resulted as a success, now I'm wondering whether I should add pipe(fd) at the start of the forking program like so . . .
void forking(int p, int pid, int *fd)
{
if(pipe(fd) == -1)
{
perror("pipe: ");
return;
}
//Rest of the code
}

Get pid from brother process

I want to have a parent process and three child processes. I want these child processes to know the pids of the other child processes.
The problem is that when I do fork and then I do it again, the second fork is also executed in the child process creating an extra process (or so I think).
How could I solve it?
Thanks.
The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.
After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.
Example code (long, but that's the nature of C):
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM_CHILDREN 3
/* Entry point for the child processes */
int child_main(int pipe_read_end) {
pid_t my_pid = getpid();
/* Read child pids from pipe */
int child_pids[NUM_CHILDREN];
unsigned int bytes_read = 0;
while (bytes_read < sizeof(child_pids)) {
ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
if (result < 0) {
perror("error reading from pipe");
return 1;
} else if (result == 0) {
fprintf(stderr, "unexpected end of file\n");
return 1;
} else {
bytes_read += result;
}
}
close(pipe_read_end);
/* Do something useful with these child pids */
for (int i = 0; i < NUM_CHILDREN; i++) {
printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
}
return 0;
}
/* Entry point for the parent process. */
int main() {
int child_pids[NUM_CHILDREN];
int pipe_write_ends[NUM_CHILDREN];
for (int i = 0; i < NUM_CHILDREN; i++) {
/* Create the pipe for child i */
int pipefd[2];
if (pipe(pipefd)) {
perror("error creating pipe");
return 1;
}
int pipe_read_end = pipefd[0];
int pipe_write_end = pipefd[1];
/* Fork child i */
pid_t child_pid = fork();
if (child_pid < 0) {
perror("error forking");
return 1;
} else if (child_pid == 0) {
printf("Child %d was forked\n", getpid());
close(pipe_write_end);
return child_main(pipe_read_end);
} else {
printf("Parent forked child %d\n", child_pid);
close(pipe_read_end);
pipe_write_ends[i] = pipe_write_end;
child_pids[i] = child_pid;
}
}
/* Send pids down the pipes for each child */
for (int i = 0; i < NUM_CHILDREN; i++) {
unsigned int bytes_written = 0;
while (bytes_written < sizeof(child_pids)) {
ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
if (result < 0) {
perror("error writing to pipe");
return 1;
} else {
bytes_written += result;
}
}
close(pipe_write_ends[i]);
}
/* Wait for children to exit */
for (int i = 0; i < NUM_CHILDREN; i++) {
if (waitpid(child_pids[i], 0, 0) < 0) {
perror("error waiting for child");
return 1;
}
}
}
As #PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.
However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.
You can use shared memory or some other kind of IPC to communicate the PIDs, but you probably shouldn't even try.
PIDs are subject to recycling and you can only ever know for sure if a PID refers to the process you think it refers to if that PID belongs to a child process of yours (because then you can know if you've waited on it or not).
Otherwise, PIDs (of non-children) are racy references which are basically only usable for hacky debugging.

C Pipe between a parent and 2 children

I can not figure out why only 1 child sends data to parent (only the 1st child)..
When I do sleep(5) after the child1 sends data through pipe to parent then the 2nd child sends the same prime number to the parent.
Can someone help me?
//--------------------------Consts---------------------------------
#define NUM_OF_CHILDS 2
#define N 20
#define WIN 5
struct msg{
pid_t _pid;
int _prime;
};
//--------------------------Prototypes-----------------------------
bool is_prime(int num);
void terminate(pid_t child_pid[],int fd[2]);
void do_child(int fd[2]);
void print_pair(const int f_arr[],const int s_arr[]);
//--------------------------Main-------------------------------------
int main()
{
int f_arr[N] = {0},
s_arr[N] = {0},
ind, //running on children fork
count1 = 0,
count2 = 0,
victory1 = 0,
victory2 = 0,
min = 0;
int fd[2];
bool read1 = false,
read2 = false;
srand((unsigned)time(NULL));
pid_t child_pid [NUM_OF_CHILDS];//children pid status array
struct msg msg1;
if (pipe(fd) == -1)//pipe fd
{
perror("cannot open pipe");
exit(EXIT_FAILURE);
}
for(ind = 0; ind < NUM_OF_CHILDS; ind++)
{
child_pid[ind] = fork();// duplicate the current process
if (child_pid[ind] < 0)//fork failed
{
perror("Cannot fork()");
exit(EXIT_FAILURE);
}
if(child_pid[ind] == 0)/* child : sends message to parent*/
do_child(fd);
}
/* parent : receives message from child */
close(fd[1]); // close the write-end of the pipe
//read data from pipe
while(read(fd[0],&msg1,sizeof(struct msg)) > 0)
{
if(child_pid[0] == msg1._pid)
{
f_arr[count1++] = msg1._prime;
read1 = true;
}
else
{
s_arr[count2++] = msg1._prime;
read2 = true;
}
if(read1 && read2)
{
if(f_arr[min] > s_arr[min])
victory1++;
else if(f_arr[min] < s_arr[min])
victory2++;
read1 = false;
read2 = false;
min++;
}
if(victory1 == WIN || victory2 == WIN)
terminate(child_pid,fd);
}
close(fd[0]);// close the read-end of the pipe
print_pair(f_arr,s_arr);
return EXIT_SUCCESS ;
}
//---------------------------------------------------------------------
//checking if number is a prime number or not
//and return true or false
bool is_prime(int num)
{
int i;
if(num==0 || num==1 || num==2)
return false;
for(i=2;i<=num/2;i++)
{
//the number is not prime
if(num%i == 0)
return false;
}
//the number is prime
return true;
}
//----------------------------------------------------------------
void do_child(int fd[2])
{
struct msg message;
int num;
close(fd[0]);
while (1)
{
num = rand() % 1000;
if(is_prime(num))
{
message._prime = num;
message._pid = getpid();
write(fd[1], &message, sizeof(struct msg));
}
}
}
//----------------------------------------------------------------
void terminate(pid_t child_pid[],int fd[2])
{
int ind,
loop;
for(ind = 0; ind < NUM_OF_CHILDS; ind++)
{
close(fd[1]);
//first to give the process an opportunity to die gratefully before
//using SIGKILL
kill(child_pid[ind], SIGTERM);
bool died = false;
//It will give the process 5 seconds to die gracefully
for (loop = 0; loop < 5 && !died; ++loop)
{
int pid;
//the time the child process takes to close down gracefully.
sleep(1);
//to get the return status of that process and prevent zombie processes.
if (waitpid(child_pid[ind], &pid, WNOHANG) == child_pid[ind])
died = true;
}
//if SIGTERM did not killed the child do SIGKILL
if (!died)
{
int pid;
kill(child_pid[ind], SIGKILL);
waitpid(child_pid[ind], &pid, 0);// harvest the zombie
}
}
}
//------------------------------------------------------------------
void print_pair(const int f_arr[],const int s_arr[])
{
int ind;
for(ind = 0; ind < N; ind++)
{
if(f_arr[ind] == 0 && s_arr[ind] == 0)
break;
printf("(%d,%d)\n",f_arr[ind],s_arr[ind]);
}
}
First, the two child processes are generating the same pseudorandom sequence because they're starting with the same seed. To have any chance of different numbers, you need to seed them after the fork, and probably use something that changes more than once per second (the chance of the two of them having different values of time() is very small even if you moved the srand(time(NULL)) after the fork).
Second, you are receiving all the numbers from the first process because it has a head start. There's plenty of time to write to the pipe while the second process is being created. The parent doesn't start reading until after both children are created, so the first child fills the pipe buffer and then blocks. Pipe buffers are at least a few kilobytes.
Even when I slowed down the child process by making it print the numbers to stderr, the first one still generated hundreds of numbers before the second one got going.
So what happens in your main loop when there are hundreds of messages arriving from child 1 and none from child 2? Your f_arr array overflows because it only has room for 20. After that, anything can happen.
The simplest way to prevent that would be to check whether count1 == N before attempting to store a number into f_arr[count1++], and if so, just continue; to the next message. You should do the same for messages from the second child, even though it's not likely to happen.
This way you'll be accepting at most N messages from each child, and ignoring the rest. You'll need to add another end condition to the main loop: if both children have sent N messages, you need to stop.
Another way to go would be to use a separate pipe for each child and alternate reading from both pipes to keep them synchronized, but I have a feeling you were deliberately avoiding that.

Closing Nonduplex Unnamed Pipes in C

I have 8 children, and am trying to use 8 pairs of nonduplex unnamed pipes to communicate with them. Thus, I have 2 pipes for each child and 16 pipes in total (one for childRead_ParentWrite and the other for parentRead_ChildWrite).
Anyway, my main question is when to close the pipes. I was taught to initially close the sides that are not being used by the process, and then when the process is finished with its side of the pipe, to close it off. However, I am brand new to the subject and am having some trouble. Here is my code:
// The 16 pipes
int fd_childReads_ParentWrites[8][2]; // Parent closes 0, Child closes 1
int fd_parentReads_ChildWrites[8][2]; // Child closes 0, Parent closes 1
// The 16 buffers
char buf_ChildReads_ParentWrites[8][80];
char buf_ParentReads_ChildWrites[8][80];
// CREATE THE PIPES
// FORK THE CHILDREN
for(playerNumber = 0; playerNumber < NUM_PLAYERS; playerNumber++)
{
pid = fork();
if (pid < 0) // Error occurred
{
printf("Fork Failed\n");
exit(1);
}
else if (pid == 0) // Child
{
break;
}
}
// MANAGE PROCESSES
if (pid == 0) // CHILD
{
printf("I am the child: %d\n", getpid());
// Close the appropriate pipe ends
close(fd_childReads_ParentWrites[playerNumber][1]);
close(fd_parentReads_ChildWrites[playerNumber][0]);
// CHILD DOES STUFF WITH PIPES
// When finished, close the working child pipe ends
close(fd_childReads_ParentWrites[playerNumber][0]);
close(fd_parentReads_ChildWrites[playerNumber][1]);
}
else // PARENT
{
printf("I am the parent: %d\n", getpid()); // NOT BEING PRINTED
// Close the appropriate pipe ends
for (i = 0; i < NUM_PLAYERS; i++)
{
close(fd_childReads_ParentWrites[i][0]);
close(fd_parentReads_ChildWrites[i][1]);
}
// PARENT DOES STUFF WITH PIPES
// Finally, close the working parent pipe ends
for (i = 0; NUM_PLAYERS < 8; i++)
{
close(fd_childReads_ParentWrites[i][1]);
close(fd_parentReads_ChildWrites[i][0]);
}
// Wait for the children
for (playerNumber = 0; playerNumber < NUM_PLAYERS; playerNumber++)
{
wait(NULL);
}
}
I must be doing something wrong. The program prints out the correct number of children, but the parent's printf() line is never printed. When I take out all of the close() functions it prints, but even taking out solely the children's close()'s doesn't print he parent line.
If someone could explain to me the correct way to close nonduplex unnamed pipes in a situation like this, that would be awesome.

How do you create Unix pipes dynamically?

I have a program that uses fork() to create child processes, and I want to have the children communicate back to the parent process using Unix pipes.
The problem is that multiple pipes don't seem to be created, or maybe there's a problem with my array. When I used prinf() in the parent program it reads identical data from every pipe, even though each child sends different data.
Here is my code:
// Variables
int pipes_count = 0;
int *pipes[MAXCLIENTS];
int new_pipefd[2];
int pipe_bytes;
char pipe_buffer[MAXDATASIZE];
while(1) {
// Pipe creation
pipe(new_pipefd);
pipes[pipes_count] = new_pipefd;
pipes_count++;
if (fork()) {
// unrelated code for parent here
close(new_pipefd[1]); // close the parent's write-end of the pipe
break;
} else {
// unrelated code for child here
close(new_pipefd[0]); // close the child's read-end of the pipe
break;
}
if (some condition) { break; } // The parent will stop creating pipes
}
while(condition that guarantees this is the child) {
write(new_pipefd[1], buffer, strlen(recv_buffer));
close(new_pipefd[1]);
return 0; // the child process ends
}
// This is a loop where the parent reads what the children sent
for (int i = 0; i < pipes_count; i++) {
pipe_bytes = read(pipes[i][0], pipe_buffer, sizeof pipe_buffer);
if (pipe_bytes == 0) {
close(pipes[i][0]);
} else {
printf("Testing: %s\n", pipe_buffer);
}
}
As I noted in my comments, the problem is in the assignment pipes[pipes_count] = new_pipefd; at:
int pipes_count = 0;
int *pipes[MAXCLIENTS];
int new_pipefd[2];
int pipe_bytes;
char pipe_buffer[MAXDATASIZE];
while(1) {
// Pipe creation
pipe(new_pipefd);
pipes[pipes_count] = new_pipefd;
pipes_count++;
The trouble is that the variable new_pipefd is an array, so you're copying the address of the same array into each of the elements of pipes, which means that the parent only has access to the last pipe that was created.
I think you should be using code more like:
int pipes_count = 0;
int pipes[MAXCLIENTS]; // Changed type!
int new_pipefd[2];
char pipe_buffer[MAXDATASIZE];
while (1)
{
// Pipe creation
pipe(new_pipefd);
pipes[pipes_count++] = new_pipefd[0]; // Just the read end of the pipe
if (fork())
{
// unrelated code for parent here
close(new_pipefd[1]); // close the parent's write-end of the pipe
// break; // This break is not wanted
}
else
{
// unrelated code for child here
close(new_pipefd[0]); // close the child's read-end of the pipe
break;
}
if (some condition)
break; // The parent will stop creating pipes
}
while (condition that guarantees this is the child)
{
write(new_pipefd[1], buffer, strlen(recv_buffer));
close(new_pipefd[1]);
return 0; // the child process ends
}
// This is a loop where the parent reads what the children sent
for (int i = 0; i < pipes_count; i++) {
int pipe_bytes = read(pipes[i], pipe_buffer, sizeof(pipe_buffer));
if (pipe_bytes != 0)
printf("Testing: %.*s\n", pipe_bytes, pipe_buffer); // Safe!
close(pipes[i]);
}
Were it my code, I'd have a function (which I traditionally call be_childish()) to invoke in the 'if it is a child' block of code in the loop. The function would never return, and would be passed whatever resources it needs (new_pipefd for sure, maybe other information too). I often have a function be_parental() to do the parental activities. I find this cleans up most of the code, forcing clean separation of the activities.

Resources