June 5, 2015 - coding cpp

Max number with pipes in C++ and Unix

Xavier Geerinck

@XavierGeerinck

Let's say we have x children, these x children all generate a number and give this to the parent. The parent now finds the highest number and sends the children the process id of the winning child. If the winning child recognises itself then it prints that it is the winner, else they print the winning process id.

Let's break this down:

  1. x children ⇒ argv[1] = x ⇒ for loop that forks children
  2. The children send a random number to the parent ⇒ Create a file descriptor that writes the number, let the parent read from this file descriptor.
  3. Parent finds highest number ⇒ Just create a if > and save the new id if it is.
  4. Sends the children the process id ⇒ This is trickier, we need to create another file descriptor and save the write descriptor for the children that we created. On the end we can now use this file descriptor to send the result to the child. We also will let the child read from this file descriptor so it can find the answer.
  5. Print the winning process id. ⇒ Just an if getpid() matches what we read, then print I'm the winner, else print the process that won.

After this breakdown it is pretty straightforward to code it. We can find all the methods in the man pages so we know which includes that we have to do. At the end I came up with this code:

Code:

// 1 argument: Number of child process to generate.
// Every child process generates a number and sends it to the parent.
// The parent then finds the biggest of the numbers and sends that back to the children
// When the children are done writing this number to the screen the parent exits.
// ==> 2 pipes, one for reading and one for writing
#include // srand, rand
#include // pipe, close, write
#include // printf
#include <sys/types.h> // waitpid
#include <sys/wait.h> // waitpid
typedef struct process {
int pid;
int fd_write;
} process;
int winner_id = 0;
int winner_value = 0;
int main(int argc, char ** argv) {
if (argc != 2) {
printf("Expected 1 parameter.n");
exit(1);
}
process processes[atoi(argv[1])]; // our process table
int received_numbers = 0;
int fd1[2];
int fd2[2];
// Create the processes
for (int i = 0; i < atoi(argv[1]); i++) {
// Create the pipe for this process
if (pipe(fd1) < 0 || pipe(fd2) < 0) {
perror(argv[0]);
exit(1);
}
// Create the child process
if ((processes[i].pid = fork()) < 0) {
perror(argv[0]);
exit(1);
} else if (processes[i].pid == 0) {
// CHILD
close(fd1[0]); // Close unused read end
close(fd2[1]); // Close unused write end
// Generating the random number
srand(getpid());
int number = rand();
// Send this number to the parent
if ((write(fd1[1], &number, sizeof(int)) != sizeof(int)) < 0) {
perror(argv[0]);
exit(1);
};
// Wait for the response
int winner_pid;
while (read(fd2[0], &winner_pid, sizeof(int)) != sizeof(int));
// Determine if the process is the winner or not
if (winner_pid == getpid()) {
printf("I'm the winner!n");
} else {
printf("Process %d is the winnern", winner_pid);
}
// Exit child
exit(0);
} else {
// PARENT
close(fd1[1]); // Close unused write end
close(fd2[0]); // Close unused read end
processes[i].fd_write = fd2[1];
// Wait for the processes to close
int number;
if (read(fd1[0], &number, sizeof(int)) != sizeof(int)) {
perror(argv[1]);
exit(1);
}
printf("Number Received: %-10d from process: %dn", number, processes[i].pid);
// Determine winner
if (winner_value < number) {
winner_id = i;
}
}
}
// Send the winner back
for (int i = 0; i < atoi(argv[1]); i++) {
if ((write(processes[i].fd_write, &processes[winner_id].pid, sizeof(int)) != sizeof(int)) < 0) {
perror(argv[1]);
exit(1);
}
}
// Wait for the childs to exit
waitpid(-1, NULL, 0);
return 0;
}

Xavier Geerinck © 2020

Twitter - LinkedIn