Reputation: 353
So, my PREVIOUS POST was flagged for being off topic, too vague, and asking for opinions and recommendations for useful code. It did those things, so I am re-posting as a question on code I am working on. Thanks to those in the previous post, I was able to piece what I have here from reviewing what you guys said.
The focus here is the Parent/Child relationship and the use of fork() and pipe() to get the desired affect.
The project is a POSIX card game where the parent (dealer) forks into 5 children (players) with their own pipe from the parent. The parent deals cards to the players (at least 5 cards each) until one player has a 3 of a kind(no discard). The winning child transmits the winning hand and EOF (exit(0)) to the parent to declare victory. That triggers the parent to print the winning player and transmit EOF to the remaining children to close them out (exit(1)). The parent then closes. If the parent reaches the end of the deck without a winner, it transmits EOF to the children, waits for them to exit(1)...then closes.
The main question I had is how the child programs read each card individually rather than reading the same value from the pipe over and over. I think I am missing a method to sync the Parent writing to the pipe and the Children reading from the pipe.
I am completely new to this guys, so any help is greatly appreciated. Many thanks.
CODE UPDATED: (compiles with a lot of issues) Complete and compiles but I clearly have trouble with the pipes and relaying data through them. There's alot wrong here from failing to play the game, to failing to read from or write to the pipes, to leaving zombie processes. I'd appreciate any input on my mess. lol
This is my output when I run the program (play.c):
os@debian:~/Documents/cpsc351/projects/assn2$ gcc -o play play.c
os@debian:~/Documents/cpsc351/projects/assn2$ ./play
Pipe Success...toChild 1 created.
Pipe Success...toParent 1 created.
Pipe Success...toChild 2 created.
Pipe Success...toParent 2 created.
Pipe Success...toChild 3 created.
Pipe Success...toParent 3 created.
Pipe Success...toChild 4 created.
Pipe Success...toParent 4 created.
Pipe Success...toChild 5 created.
Pipe Success...toParent 5 created.
Parent: All players are at the table. Dealing cards...
30 5C to player 1
51 KS to player 2
9 10H to player 3
25 KD to player 4
6 7H to player 5
18 6D to player 1
45 7S to player 2
29 4C to player 3
37 QC to player 4
12 KH to player 5
7 8H to player 1
19 7D to player 2
20 8D to player 3
49 JS to player 4
35 10C to player 5
15 3D to player 1
5 6H to player 2
36 JC to player 3
0 AH to player 4
22 10D to player 5
48 10S to player 1
27 2C to player 2
42 4S to player 3
16 4D to player 4
32 7C to player 5
4 5H to player 1
14 2D to player 2
41 3S to player 3
39 AS to player 4
1 2H to player 5
26 AC to player 1
46 8S to player 2
34 9C to player 3
11 QH to player 4
24 QD to player 5
17 5D to player 1
31 6C to player 2
44 6S to player 3
40 2S to player 4
3 4H to player 5
21 9D to player 1
50 QS to player 2
13 AD to player 3
33 8C to player 4
23 JD to player 5
43 5S to player 1
2 3H to player 2
28 3C to player 3
47 9S to player 4
38 KC to player 5
10 JH to player 1
8 9H to player 2
Child: Fork Success...Player 4 is sitting at the table.
Child: Player 4 is dealt a KD. Hand Total = 1 cards.
Child: Player 4 is dealt a QC. Hand Total = 2 cards.
Child: Player 4 is dealt a JS. Hand Total = 3 cards.
Child: Player 4 is dealt a AH. Hand Total = 4 cards.
Child: Player 4 is dealt a 4D. Hand Total = 5 cards.
Child: Player 4 is dealt a AS. Hand Total = 6 cards.
Child: Player 4 is dealt a QH. Hand Total = 7 cards.
Child: Player 4 is dealt a 2S. Hand Total = 8 cards.
Child: Player 4 is dealt a 8C. Hand Total = 9 cards.
Child: Player 4 is dealt a 9S. Hand Total = 10 cards.
Child: Fork Success...Player 5 is sitting at the table.
Child: Player 5 is dealt a 7H. Hand Total = 1 cards.
Child: Player 5 is dealt a KH. Hand Total = 2 cards.
Child: Player 5 is dealt a 10C. Hand Total = 3 cards.
Child: Player 5 is dealt a 10D. Hand Total = 4 cards.
Child: Player 5 is dealt a 7C. Hand Total = 5 cards.
Child: Player 5 is dealt a 2H. Hand Total = 6 cards.
Child: Player 5 is dealt a QD. Hand Total = 7 cards.
Child: Player 5 is dealt a 4H. Hand Total = 8 cards.
Child: Player 5 is dealt a JD. Hand Total = 9 cards.
Child: Player 5 is dealt a KC. Hand Total = 10 cards.
Child: Player 5 has left the table.
os@debian:~/Documents/cpsc351/projects/assn2$ Child: Player 4 has left the table.
Child: Fork Success...Player 3 is sitting at the table.
Child: Player 3 is dealt a 10H. Hand Total = 1 cards.
Child: Player 3 is dealt a 4C. Hand Total = 2 cards.
Child: Player 3 is dealt a 8D. Hand Total = 3 cards.
Child: Player 3 is dealt a JC. Hand Total = 4 cards.
Child: Player 3 is dealt a 4S. Hand Total = 5 cards.
Child: Player 3 is dealt a 3S. Hand Total = 6 cards.
Child: Player 3 is dealt a 9C. Hand Total = 7 cards.
Child: Player 3 is dealt a 6S. Hand Total = 8 cards.
Child: Player 3 is dealt a AD. Hand Total = 9 cards.
Child: Player 3 is dealt a 3C. Hand Total = 10 cards.
Child: Player 3 has left the table.
Child: Fork Success...Player 2 is sitting at the table.
Child: Player 2 is dealt a KS. Hand Total = 1 cards.
Child: Player 2 is dealt a 7S. Hand Total = 2 cards.
Child: Player 2 is dealt a 7D. Hand Total = 3 cards.
Child: Player 2 is dealt a 6H. Hand Total = 4 cards.
Child: Player 2 is dealt a 2C. Hand Total = 5 cards.
Child: Player 2 is dealt a 2D. Hand Total = 6 cards.
Child: Player 2 is dealt a 8S. Hand Total = 7 cards.
Child: Player 2 is dealt a 6C. Hand Total = 8 cards.
Child: Player 2 is dealt a QS. Hand Total = 9 cards.
Child: Player 2 is dealt a 3H. Hand Total = 10 cards.
Child: Player 2 is dealt a 9H. Hand Total = 11 cards.
Child: Player 2 has left the table.
Child: Fork Success...Player 1 is sitting at the table.
Child: Player 1 is dealt a 5C. Hand Total = 1 cards.
Child: Player 1 is dealt a 6D. Hand Total = 2 cards.
Child: Player 1 is dealt a 8H. Hand Total = 3 cards.
Child: Player 1 is dealt a 3D. Hand Total = 4 cards.
Child: Player 1 is dealt a 10S. Hand Total = 5 cards.
Child: Player 1 is dealt a 5H. Hand Total = 6 cards.
Child: Player 1 is dealt a AC. Hand Total = 7 cards.
Child: Player 1 is dealt a 5D. Hand Total = 8 cards.
Child: Player 1 has at least "3 of a Kind". Hand Total = 8 cards.
Current Code:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "cards.h"
#include "cards.c"
#define READ 0
#define WRITE 1
#define PLAYERS 5
int main(int argc, char *argv[])
{
//loop declarations ***Would not let me initialize within a for-loop...c99 error.***
int i;
int j;
pid_t player[PLAYERS];
int toChild_pipe[PLAYERS][2];
int toParent_pipe[PLAYERS][2];
int dealt_card;
int card_count = 1;
int player_count = 0;
int status_forChild;
int status_forParent;
int player_card;
for(i = 0; i < PLAYERS; i++)
{
// Create the pipes
if (pipe(toChild_pipe[i]) < 0)
{
perror("'To-Child' Pipe Error\n");
exit(1);
}
if (pipe(toParent_pipe[i]) < 0)
{
perror("'To-Parent' Pipe Error\n");
exit(1);
}
// Fork the child (new player)
player[i] = fork();
if (player[i] < 0)
{
perror("Fork Error:");
printf(" Player %d cloud not sit at table.\n", i+1);
exit(1);
}
else if (player[i] > 0) //Parent Process
{
// Close unsed pipe ends in Parent
close(toChild_pipe[i][READ]);
close(toParent_pipe[i][WRITE]);
}
else //(player[i] == 0)-- Child Process
{
int player_num = (i+1);
int player_card;
int hand[13] = {0};
int player_card_count = 0;
bool game_over = false;
printf("Child: Fork Success...Player %d is sitting at the table.\n", player_num);
// Close unsed pipe ends in Parent
close(toParent_pipe[i][READ]);
close(toChild_pipe[i][WRITE]);
while(!game_over)
{
if ((status_forChild = read(toChild_pipe[i][READ], &player_card, sizeof(player_card))) == 0)
{
//EOF from parent. Player lost.
game_over = true;
close(toParent_pipe[i][WRITE]);
close(toChild_pipe[i][READ]);
printf("Child: Player %d has left the table.\n", player_num);
exit(1);
}
else if (status_forChild == -1)
{
perror("");
printf("Child %d: ERROR: Could not read from pipe.\n", i+1);
exit(1);
}
else
{
//Players have 5 cards, loop through hand to check for winner. If yes, WIN.
if (player_card_count == 5)
{
for (j = 0; j < 13; j++)
{
if(hand[j] >=3)
{
//WINNER! Close process (status = 0)
printf("Child: Player %d has at least. Hand Total = %d cards.\n"
, player_num, rank(player_card));
close(toParent_pipe[i][WRITE]);
close(toChild_pipe[i][READ]);
exit(0);
}
}
}
//Read the current card value dealt, increment card value in hand array
int card_index = value_index(rank(player_card));
hand[card_index]++;
player_card_count++;
printf("Child: Player %d is dealt a %s%s. Hand Total = %d cards.\n", player_num, rank(player_card),
suit(player_card), player_card_count);
if ((hand[card_index] >= 3)&&(player_card_count > 5)) //at least (3 of a kind) and (> 5 card hand)
{
//WINNER! Close process (status = 0)
printf("Child: Player %d has at least. Hand Total = %d cards.\n", player_num, rank(player_card));
close(toParent_pipe[i][WRITE]);
close(toChild_pipe[i][READ]);
exit(0);
}
}
}
}
}
shuffle();
printf("Parent: All players are at the table. Dealing cards... \n");
while ((dealt_card = deal()) != EOF)
{
//Card is written to the pipe for current player
if ((status_forParent = write(toChild_pipe[i][WRITE], &dealt_card, sizeof(dealt_card))) == -1)
{
perror("");
printf("Parent: ERROR: Could not read from pipe for Child %d.\n", i+1);
exit(1);
}
//If child process exited with status = 0, child had 3 of a kind and wins game.
else if (status_forParent == 0)
{
printf("Parent: Player %d has WON!!!\n", player_count+1, rank(player_card));
break;
}
else
{
printf(" %d %s%s to player %d\n", dealt_card, rank(dealt_card), suit(dealt_card), player_count+1);
if (player_count >= PLAYERS-1)
player_count = 0;
else
player_count++;
}
}
// Close pipe ends
close(toParent_pipe[i][READ]);
close(toChild_pipe[i][WRITE]);
wait(NULL);
return 0;
}
Upvotes: 3
Views: 5710
Reputation: 126203
The basic problem you have is that you depend on EOFs to detect things, but an EOF won't occur until ALL handles on the write end of the pipe are closed. So you have to be careful about closing ALL unneeded handles in all processes.
In your code you have a loop that creates pipes, then forks:
At this point, you have a problem -- child 1 has inherited the parent ends of the pipe to child 0, but isn't closing them. Which means that child 0 won't be able to detect an EOF when it reads from parent. The same thing happens with child 2 and later children.
Upvotes: 2
Reputation: 25908
This kind of looks like a code-only answer, but it's not really, since the comments in the code explain what's going on. It isn't the same game you're writing, but shows the mechanism you're looking for. You can adapt this kind of framework to your specific situation.
Code:
/* Demonstration of multiplayer "game" with processes.
*
* The parent sets up a number of processes equal to NUM_KIDS.
* It loops through each one in turn, and writes a character to
* each child, beginning with one. The child reads it, and if
* that character is the winning number, it writes back to the
* parent to notify it, and exits. If it's not the winning
* character, it writes a different character to the parent (which
* is ignored) and waits for another character to read. If it
* reads the game over character, it exits.
*
* It's not a very fun game, but demonstrates how a number of
* child processes can act as different players, how they can
* receive input from the parent and, based on that input, how
* they can determine a win situation and notify the parent of
* such.
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_KIDS 5
static const int CHILD_NO_WIN = 0; /* Child sends this if it doesnt win */
static const int CHILD_WIN = 1; /* Child sends this if it wins */
static const int GAME_OVER = 0; /* Child loses if it receives this */
static const int WINNER = 13; /* Child wins if it receives this */
/* Convenience function to make a pair of pipes */
void make_pipe_pair(int * pair1, int * pair2)
{
if ( pipe(pair1) == -1 || pipe(pair2) == -1 ) {
perror("couldn't create pipe");
exit(EXIT_FAILURE);
}
}
/* Convenience function to close a pair of file descriptors */
void close_pair(const int rfd, const int wfd)
{
if ( close(rfd) == -1 || close(wfd) == -1 ) {
perror("couldn't close file");
exit(EXIT_FAILURE);
}
}
/* Main child process function */
void child_func(const int rpipe, const int wpipe, const size_t child_id)
{
char out_c = CHILD_NO_WIN; /* Character to write */
char in_c; /* Character to read */
bool keep_reading = true;
while ( keep_reading ) {
/* Read a single character from the parent */
ssize_t num_read;
if ( (num_read = read(rpipe, &in_c, 1)) == -1 ) {
perror("error reading from pipe in child");
exit(EXIT_FAILURE);
}
else if ( num_read == 0 ) {
printf("Pipe from parent closed to child %zu.\n", child_id);
keep_reading = false;
}
else {
printf("Child %zu read %d from parent.\n", child_id, in_c);
if ( in_c == GAME_OVER ) {
/* We lost, so tell loop to end. No need to write()
* to parent, since it already knows a previous
* child won. */
printf("Child %zu got game over signal.\n", child_id);
keep_reading = false;
}
else {
if ( in_c == WINNER ) {
/* We won, so send won signal to parent */
out_c = 1;
}
/* Write won signal to parent if we won, or
* other character if we didn't. */
if ( write(wpipe, &out_c, 1) == -1 ) {
perror("error writing to pipe in child");
exit(EXIT_FAILURE);
}
else {
printf("Child %zu wrote %d to parent.\n", child_id, out_c);
}
}
}
}
/* Close file descriptors and exit */
close_pair(rpipe, wpipe);
}
/* Main function */
int main(void)
{
int ptoc_fd[NUM_KIDS][2]; /* Parent to child pipes */
int ctop_fd[NUM_KIDS][2]; /* Child to parent pipes */
pid_t children[NUM_KIDS]; /* Process IDs of children */
int winning_child; /* Holds number of winner */
/* Create pipe pairs and fork children */
for ( size_t i = 0; i < NUM_KIDS; ++i ) {
make_pipe_pair(ptoc_fd[i], ctop_fd[i]);
if ( (children[i] = fork()) == -1 ) {
perror("error calling fork()");
return EXIT_FAILURE;
}
else if ( children[i] == 0 ) {
printf("Child %zu created.\n", i + 1);
close_pair(ctop_fd[i][0], ptoc_fd[i][1]);
child_func(ptoc_fd[i][0], ctop_fd[i][1], i + 1);
printf("Child %zu terminating.\n", i + 1);
return EXIT_SUCCESS;
}
else {
close_pair(ptoc_fd[i][0], ctop_fd[i][1]);
}
}
/* Set up game variables and enter main loop */
char out_c = 1;
char in_c = 0;
bool won = false;
while ( !won ) {
/* Loop through each child */
for ( size_t i = 0; !won && i < NUM_KIDS; ++i ) {
/* Write next number to child */
if ( write(ptoc_fd[i][1], &out_c, 1) == -1 ) {
perror("error writing to pipe");
exit(EXIT_FAILURE);
}
else {
printf("Parent wrote %d to child %zu.\n", out_c, i+1);
}
++out_c;
/* Read status from child if game not over */
if ( !won ) {
ssize_t num_read;
if ( (num_read = read(ctop_fd[i][0], &in_c, 1)) == -1 ) {
perror("error reading from pipe");
return EXIT_FAILURE;
}
else if ( num_read == 0 ) {
printf("Pipe from child %zu closed.\n", i+1);
}
else {
printf("Parent read %d from child %zu.\n", in_c, i+1);
if ( in_c == CHILD_WIN ) {
printf("Parent got won signal from child %zu.\n", i+1);
won = true;
winning_child = i+1;
}
}
}
}
}
/* Clean up and harvest dead children */
out_c = 0;
for ( size_t i = 0; i < NUM_KIDS; ++i ) {
if ( write(ptoc_fd[i][1], &out_c, 1) == -1 ) {
perror("error writing to pipe");
exit(EXIT_FAILURE);
}
else {
printf("Parent wrote %d to child %zu.\n", out_c, i + 1);
}
if ( waitpid(children[i], NULL, 0) == -1 ) {
perror("error calling waitpid()");
return EXIT_FAILURE;
}
else {
printf("Successfully waited for child %zu.\n", i + 1);
}
close_pair(ptoc_fd[i][1], ctop_fd[i][0]);
}
/* Show who won, and then quit. */
printf("Parent terminating. Child %d won.\n", winning_child);
return EXIT_SUCCESS;
}
and output:
paul@thoth:~/src/sandbox/multipipe$ ./multipipe
Child 1 created.
Child 1 read 1 from parent.
Parent wrote 1 to child 1.
Child 1 wrote 0 to parent.
Child 3 created.
Parent read 0 from child 1.
Parent wrote 2 to child 2.
Child 2 created.
Child 2 read 2 from parent.
Parent read 0 from child 2.
Parent wrote 3 to child 3.
Child 3 read 3 from parent.
Parent read 0 from child 3.
Child 4 created.
Parent wrote 4 to child 4.
Child 3 wrote 0 to parent.
Child 2 wrote 0 to parent.
Child 4 read 4 from parent.
Child 5 created.
Parent read 0 from child 4.
Parent wrote 5 to child 5.
Child 4 wrote 0 to parent.
Child 5 read 5 from parent.
Parent read 0 from child 5.
Parent wrote 6 to child 1.
Child 5 wrote 0 to parent.
Child 1 read 6 from parent.
Parent read 0 from child 1.
Parent wrote 7 to child 2.
Child 1 wrote 0 to parent.
Child 2 read 7 from parent.
Parent read 0 from child 2.
Parent wrote 8 to child 3.
Child 3 read 8 from parent.
Parent read 0 from child 3.
Child 2 wrote 0 to parent.
Parent wrote 9 to child 4.
Child 4 read 9 from parent.
Parent read 0 from child 4.
Parent wrote 10 to child 5.
Child 3 wrote 0 to parent.
Child 4 wrote 0 to parent.
Child 5 read 10 from parent.
Child 5 wrote 0 to parent.
Parent read 0 from child 5.
Parent wrote 11 to child 1.
Child 1 read 11 from parent.
Parent read 0 from child 1.
Parent wrote 12 to child 2.
Child 2 read 12 from parent.
Child 1 wrote 0 to parent.
Parent read 0 from child 2.
Parent wrote 13 to child 3.
Child 3 read 13 from parent.
Parent read 1 from child 3.
Parent got won signal from child 3.
Parent wrote 0 to child 1.
Child 2 wrote 0 to parent.
Child 1 read 0 from parent.
Child 1 got game over signal.
Child 1 terminating.
Child 3 wrote 1 to parent.
Successfully waited for child 1.
Parent wrote 0 to child 2.
Child 2 read 0 from parent.
Child 2 got game over signal.
Child 2 terminating.
Successfully waited for child 2.
Parent wrote 0 to child 3.
Child 3 read 0 from parent.
Child 3 got game over signal.
Child 3 terminating.
Successfully waited for child 3.
Parent wrote 0 to child 4.
Child 4 read 0 from parent.
Child 4 got game over signal.
Child 4 terminating.
Successfully waited for child 4.
Parent wrote 0 to child 5.
Child 5 read 0 from parent.
Child 5 got game over signal.
Child 5 terminating.
Successfully waited for child 5.
Parent terminating. Child 3 won.
paul@thoth:~/src/sandbox/multipipe$
The output looks a little weird in place, and looks like processes are reading stuff before it's been written, but that's what happens when you work with asynchronous processes and don't bother to synchronize your input/output for demo purposes. The actual reading and writing to the pipes is well behaved and synchronized, it's just the debug messages going to standard output that look screwy. You should still be able to see what's going on.
Upvotes: 0