1
votes

I have encountered some problems while dealing with pipes and child processes.

I need to

  1. create four child using fork()
  2. establish two-way communication using pipe() between parent and each child
  3. read data from a file card.txt containing number of strings
  4. distribute strings in a round-robin manner to each child A B C D E F G H, child 1 get A E, child 2 get B F and so on
  5. select one member variable -> send variable to parent using write() and read() as well as close() to close unused pipe end
  6. print received variable in parent process

An expected output is:

$ ./a.out 1C < card.txt
    Child : 1, pid 1593 : 
    <S2 S3 S9 ><H9 H6 HA H8 ><C6 CK ><D8 DQ D7 D3 >
    Child : 2, pid 1594 : 
    <SA S6 S7 S4 ><H4 HJ H7 ><CQ C9 CT ><DT D2 D9 >
    Child : 3, pid 1595 : 
    <SQ S5 ><H2 ><C7 C5 C8 CA C4 CJ C3 ><DA D5 > 
    Child : 4, pid 1596 : 
    <S8 SJ SK ST ><HK HT H5 H3 HQ ><C2 ><D4 D6 >
    child 1: H9 
    parent: child 1 played H9 
    child 2: CQ 
    parent: child 2 played CQ  
    child 3: H2
    parent: child 3 played H2 
    child 4: HK 
    parent: child 4 played HK 

However, actual output is the following:

parent: child 1 played (null)parent: child 2 played (null)parent: child 3 played (null)parent: child 4 played (null)

Why is that? How to solve the problem?

The code created this output:

#include <stdio.h>
#include <string.h>

#define BUFFERSIZE 51 
 int i=0;
   int currpid, s; 
  char *buf[BUFFERSIZE]; 
    char *array[BUFFERSIZE];
    int n;
    char *buffer[100]; 
/** child to parent pipe */
    int child_parent[2];
    /** parent to child pipe */
    int parent_child[2];

     /** child2 to parent pipe */
    int child2_parent[2];
    /** parent to child2 pipe */
    int parent_child2[2];

     /** child3 to parent pipe */
    int child3_parent[2];
    /** parent to child3 pipe */
    int parent_child3[2];

     /** child4 to parent pipe */
    int child4_parent[2];
    /** parent to child4 pipe */
    int parent_child4[2];

void childFunction(){
    int j;
         for( i = s+1; i < BUFFERSIZE; i += 4 )
         {   
            buf[j] = array[i];  
            j++; 
         } 
         printf("\n<");
         int r;
         char *e;
         for( r = 0; r < j; r++ )
         { 
            int index;
            e = strchr(buf[r],'S');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){

                    printf("%s ", buf[r]) ; 
                } 
            }  
        } 
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'H');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){ 
                    printf("%s ", buf[r]) ; 
                } 
            }  
        }   
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'C');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){
                    printf("%s ", buf[r]) ; 
                } 
            }  
        } 
        printf(">");
        printf("<");
        for( r = 0; r < j; r++ )
        { 
            int index;
            e = strchr(buf[r],'D');
            if (e!=NULL){
                index = (int)(e-buf[r]);
                if (index == 0){
                    printf("%s ", buf[r]) ; 
                }
            }  
        } 
        printf(">\n");
        switch (s){
            case 0:
              close(parent_child[1]);
              close(parent_child[0]); 
              close(child_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child_parent[1], &buffer[0], strlen(buffer[0]));
              close(child_parent[1]); 
              break;
            case 1:
              close(parent_child2[1]);
              close(parent_child2[0]); 
              close(child2_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child2_parent[1], &buffer[0], strlen(buffer[0]));
              close(child2_parent[1]); 
              break;
            case 2:
              close(parent_child3[1]);
              close(parent_child3[0]); 
              close(child3_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child3_parent[1], &buffer[0], strlen(buffer[0]));
              close(child3_parent[1]); 
              break;
            case 3:
              close(parent_child4[1]);
              close(parent_child4[0]); 
              close(child4_parent[0]); 
              buffer[0] = buf[0];
              printf("child %d: %s", s+1, buffer[0]); 
              write(child4_parent[1], &buffer[0], strlen(buffer[0]));
              close(child4_parent[1]); 
              break;
        }
} 
void parentFunction(){

                switch (s){
                    case 0:
                      close(child_parent[1]);
                      close(parent_child[1]);  
                      close(parent_child[0]);
                      read(child_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child_parent[0]);
                      break;
                     case 1:
                      close(child2_parent[1]);
                      close(parent_child2[1]);  
                      close(parent_child2[0]);
                      read(child2_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child2_parent[0]);
                      break;
                     case 2:
                      close(child3_parent[1]);
                      close(parent_child3[1]);  
                      close(parent_child3[0]);
                      read(child3_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child3_parent[0]);
                      break;
                     case 3:
                      close(child4_parent[1]);
                      close(parent_child4[1]);  
                      close(parent_child4[0]);
                      read(child4_parent[0],&buffer[0], sizeof(buffer[0])); 
                      printf("parent: child %d played %s", s+1, buffer[0]);
                      close(child4_parent[0]);
                      break;   
                }
}
int main(int argc, char *argv[])
{
    int ch; 
    ssize_t rread;
    char *line = NULL;
    size_t len = 0; 
    while (rread = getdelim( &line, &len, '\0', stdin) != -1) { 
    }  

    array[i] = strtok(line," ");

    while(array[i]!=NULL)
    {
        array[++i] = strtok(NULL," ");
    } 

    int childlimit = 4;
    int childpids[childlimit];
    int currpid; 


    if (pipe(child_parent) == 0 && pipe(parent_child) == 0 && pipe(child2_parent) == 0 && pipe(parent_child2) == 0 && pipe(child3_parent) == 0 && pipe(parent_child3) == 0 && pipe(child4_parent) == 0 && pipe(parent_child4) == 0)
    {
      for(s=0; s<childlimit; s++){
        switch(currpid = fork()){
        case 0: 
         printf("Child : %d, pid %d : ", s+1, getpid() );
         childFunction(); 
         break;
        case -1:
         printf("Error when forking\n");
         return 1;
        default:
        // in the father
        childpids[s] = currpid;  
        parentFunction();
        break;
        } 
          }   
    //wait for all child created to die
    waitpid(-1, NULL, 0); 
    }
}
1
Have you heard of these things called 'functions'? They're useful to make code clearer. There is far too much code for the child processing in the main function. Keep process management in main(); delegate the string processing and I/O operations to a function that only runs in the children. You aren't really closing enough file descriptors in the children. You're closing too many file descriptors in the parent if the parent needs to write to its children; if the parent doesn't need to write to the children, why are you bothering with the pipes from parent to children?Jonathan Leffler
@JonathanLeffler, updated code move parent and child code into different functions, in my implementation, it will need parent write to children but at this point, it has not been developed yet because of the problem.Sam Annie
Using global variable i for loop controls in functions is a disaster. Sometimes, global variables are necessary — when they are, you should use them. Mostly, they're not — and when they aren't, you should not use them. Single-letter global variable names are seldom appropriate. (It is much better to repeat the definition of i in each function where it is needed than to do as you've done.) This makes your code very hard to analyze. Your string reading loop only preserves the last 'line' (null-terminated 'line'). I guess that is a way of slurping the entire file in a single operation.Jonathan Leffler
Your code doesn't write the selection of strings to the children — you aren't using the parent-child pipes at all. That defies the specification you give for the program. Since you've got 8 pipes, 16 descriptors, your child processes should be closing 14 descriptors each — they only need to keep open the read end of the pipe from the parent to that child and the write end of the from the child to the parent. Your call to waitpid() waits for any one of the children to die; you need a loop to wait for them all to die.Jonathan Leffler
It would also help a lot if you provided the input data from which you should get the expected output data you show. It isn't obvious how it is all laid out.Jonathan Leffler

1 Answers

1
votes

When you start using names like child2_parent and parent_child2, there's an array trying to escape from your variables. When you have 4 sets of these variables, it is dire.

There were many, many problems with the code. This is a total rewrite. Errors are reported to stderr. Most system calls are checked. There's diagnostic printout of the data that's read. There's a function split_string() that splits a string into words on white space. It's used in both the parent and the child components. The children no longer have access to the string data — all the global variables have gone.

The code is still far from optimal. There's no particular need to open all the pipes before starting the children; the process is synchronous. It would be feasible and even sensible to create the pipes for each child in turn. As it stands, each child only closes a quarter of the pipe descriptors that are available to it.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

enum { MAX_KIDS  = 4 };
enum { LIST_SIZE = 1024 };
enum { MAX_WORDS = LIST_SIZE / 3 };

static int split_string(char *data, char *words[], int max_words)
{
    int num_words = 0;
    char *src = data;
    char *token;
    while ((token = strtok(src, " \n\t")) != NULL)
    {
        words[num_words++] = token;
        if (num_words >= max_words)
            break;
        src = NULL;
    }
    return num_words;
}

static void childFunction(int s, int f_pipe[2], int t_pipe[2])
{
    srand(getpid());
    close(f_pipe[1]);
    close(t_pipe[0]);
    char data[LIST_SIZE];
    int nbytes;
    if ((nbytes = read(f_pipe[0], data, sizeof(data))) <= 0)
    {
        fprintf(stderr, "child %d (PID %d): failed to read pipe\n", s, (int)getpid());
        exit(1);
    }
    data[nbytes] = '\0';
    close(f_pipe[0]);

    char *words[MAX_WORDS];
    int num_words = split_string(data, words, MAX_WORDS);
    char *word = words[rand() % num_words];
    int len = strlen(word);
    if (write(t_pipe[1], word, len) != len)
    {
        fprintf(stderr, "child %d (PID %d): failed to write [%s] to pipe\n", s, (int)getpid(), word);
        exit(1);
    }
    close(t_pipe[1]);
    exit(0);
}

static void parentFunction(int s, int c_pid, int t_pipe[2], int f_pipe[2], char *words[], int max_words)
{
    close(t_pipe[0]);
    close(f_pipe[1]);

    char buffer[LIST_SIZE];
    char *data = buffer;
    const char *pad = "";
    for (int i = s; i < max_words; i += MAX_KIDS)
    {
        int n = snprintf(data, (size_t)(buffer + sizeof(buffer) - data), "%s%s", pad, words[i]);
        data += n;
        pad = " ";
    }
    printf("Send [%s] to child %d\n", buffer, s);

    /* Write list of words to child */
    int nbytes = data - buffer;
    if (write(t_pipe[1], buffer, nbytes) != nbytes)
    {
        fprintf(stderr, "parent: failed to write to child %d (PID %d)\n", s, c_pid);
        exit(1);
    }
    close(t_pipe[1]);

    /* Read selected word from child */
    nbytes = read(f_pipe[0], buffer, sizeof(buffer));
    if (nbytes <= 0)
    {
        fprintf(stderr, "parent: failed to read from child %d (PID %d)\n", s, c_pid);
        exit(1);
    }
    buffer[nbytes] = '\0';
    close(f_pipe[0]);

    printf("parent: child %d (PID %d) played %s\n", s, c_pid, buffer);
}

int main(void)
{
    ssize_t rread;
    char *line = NULL;
    size_t len = 0;
    if ((rread = getdelim(&line, &len, '\0', stdin)) == -1)
    {
        fprintf(stderr, "Unexpected EOF on standard input\n");
        return 1;
    }

    printf("line [%s]\n", line);

    char *wordlist[MAX_WORDS];
    int num_words = split_string(line, wordlist, MAX_WORDS);

    printf("Word list:");
    int i;
    for (i = 0; i < num_words; i++)
    {
        if (i % 10 == 0 && i > 0)
            printf("%10s", "");
        printf(" %2d [%s]", i, wordlist[i]);
        if (i % 10 == 9)
            putchar('\n');
    }
    if (i % 10 != 0)
        putchar('\n');
    printf("%d words\n", i);

    for (int kid = 0; kid < MAX_KIDS; kid++)
    {
        printf("Words for child %d:", kid);
        for (int word = kid; word < i; word += MAX_KIDS)
            printf(" [%s]", wordlist[word]);
        putchar('\n');
    }

    int child_parent[MAX_KIDS][2];
    int parent_child[MAX_KIDS][2];

    for (int i = 0; i < MAX_KIDS; i++)
    {
        if (pipe(child_parent[i]) != 0 || pipe(parent_child[i]) != 0)
        {
            fprintf(stderr, "failed to create pipes for child %d\n", i);
            exit(1);
        }
    }

    for (int s = 0; s < MAX_KIDS; s++)
    {
        int currpid = fork();
        if (currpid < 0)
        {
            fprintf(stderr, "Error when forking\n");
            exit(1);
        }
        else if (currpid == 0)
        {
            printf("Child : %d, pid %d:\n", s, getpid() );
            childFunction(s, parent_child[s], child_parent[s]);
        }
        else
            parentFunction(s, currpid, parent_child[s], child_parent[s], wordlist, num_words);
    }

    int corpse;
    int status;
    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("PID %d died with exit status 0x%.4X\n", corpse, status);
}

Given a data file:

S2 SA SQ S8 S3 S6 S5 SJ S9 S7 H2 SK H9 S4 C7
ST H6 H4 C5 HK HA HJ C8 HT H8 H7 CA H5 C6 CQ
C4 H3 CK C9 CJ HQ D8 CT C3 C2 DQ DT DA D4 D7
D2 D5 D6 D3 D9

one example output from the program is:

line [S2 SA SQ S8 S3 S6 S5 SJ S9 S7 H2 SK H9 S4 C7
ST H6 H4 C5 HK HA HJ C8 HT H8 H7 CA H5 C6 CQ
C4 H3 CK C9 CJ HQ D8 CT C3 C2 DQ DT DA D4 D7
D2 D5 D6 D3 D9
]
Word list:  0 [S2]  1 [SA]  2 [SQ]  3 [S8]  4 [S3]  5 [S6]  6 [S5]  7 [SJ]  8 [S9]  9 [S7]
           10 [H2] 11 [SK] 12 [H9] 13 [S4] 14 [C7] 15 [ST] 16 [H6] 17 [H4] 18 [C5] 19 [HK]
           20 [HA] 21 [HJ] 22 [C8] 23 [HT] 24 [H8] 25 [H7] 26 [CA] 27 [H5] 28 [C6] 29 [CQ]
           30 [C4] 31 [H3] 32 [CK] 33 [C9] 34 [CJ] 35 [HQ] 36 [D8] 37 [CT] 38 [C3] 39 [C2]
           40 [DQ] 41 [DT] 42 [DA] 43 [D4] 44 [D7] 45 [D2] 46 [D5] 47 [D6] 48 [D3] 49 [D9]
50 words
Words for child 0: [S2] [S3] [S9] [H9] [H6] [HA] [H8] [C6] [CK] [D8] [DQ] [D7] [D3]
Words for child 1: [SA] [S6] [S7] [S4] [H4] [HJ] [H7] [CQ] [C9] [CT] [DT] [D2] [D9]
Words for child 2: [SQ] [S5] [H2] [C7] [C5] [C8] [CA] [C4] [CJ] [C3] [DA] [D5]
Words for child 3: [S8] [SJ] [SK] [ST] [HK] [HT] [H5] [H3] [HQ] [C2] [D4] [D6]
Send [S2 S3 S9 H9 H6 HA H8 C6 CK D8 DQ D7 D3] to child 0
Child : 0, pid 89366:
parent: child 0 (PID 89366) played HA
Send [SA S6 S7 S4 H4 HJ H7 CQ C9 CT DT D2 D9] to child 1
Child : 1, pid 89367:
parent: child 1 (PID 89367) played S4
Send [SQ S5 H2 C7 C5 C8 CA C4 CJ C3 DA D5] to child 2
Child : 2, pid 89368:
parent: child 2 (PID 89368) played C5
Send [S8 SJ SK ST HK HT H5 H3 HQ C2 D4 D6] to child 3
Child : 3, pid 89369:
parent: child 3 (PID 89369) played D6
PID 89368 died with exit status 0x0000
PID 89367 died with exit status 0x0000
PID 89366 died with exit status 0x0000
PID 89369 died with exit status 0x0000