0

I've a problem with this famous issue. I must fork two child (producer and consumer) that communicate thanks to a pipe. The first child(producer) must read strings from stdin, send them to the second child(consumer) that must convert them to uppercase and print to stdout.

I wrote a code but it doesn't work.

  • The consumer reads the string from stdin and writes it in pipe next to it lenght(included '\0').
  • The producer reads MAXC from pipe and split in two var lenght and string. When I print lenght is setted to a big number. So even the string that must be converted is not right. Furthermore the entire program freezes.

(I tried to write here the code but maybe I didn't understand how to do it properly. Explain me! Thank you)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30
static void signalHandler(int signo)
{
  return;
}
pid_t pids[2];
int main()
{
  int fd[2], i, nW;
  size_t l;
  char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];

  signal(SIGUSR1, signalHandler);
  if(pipe(fd)==0)
    {    
      pids[0]=fork();
      if(pids[0]==0)
    {
      fprintf(stdout, "PID=%d PRODUCER\n", getpid());
      close(fd[0]); //produttore
      sleep(3);
      fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
      fscanf(stdin, "%s", string);
      while(1)
        {
          if(strcmp(tmp, "end")==0)
        break;
          l=strlen(string)+1;
          sprintf(tmp, "%2lu%s", l, string);
          printf("%s\n", tmp);
          nW=write(fd[1], tmp, (l+2));
          printf("nW(PRODUCER)=%d", nW);
          if(nW!=(l+2))
        {
          perror("wrote not whole string");
          exit(1);
        }
          sleep(5);
          kill(pids[1], SIGUSR1);
          pause();
          fprintf(stdout, "Insert string:\n");
          fscanf(stdin, "%s", string);
        }

      exit(0);
    }
      pids[1]=fork();
      if(pids[1]==0)
    {
      fprintf(stdout, "PID=%d CONSUMER\n", getpid());
      close(fd[0]); //consumer

      while(1)
        {
          pause();
          read(fd[0], tmp, MAXC+1);
          printf("tmp(CONSUMER)=%s\n", tmp); 
          sscanf(tmp, "%2lu%s", &l, stringtoup);
          printf("lenght string(CONSUMER)=%2lu\n", l);
          printf("stringtoup=%s\n", stringtoup);
          for(i=0; i<l; i++)
        stringtoup[i]=toupper(stringtoup[i]);

          fprintf(stdout, "%s\n", stringtoup);
          fflush(stdout);
          sleep(1);
          kill(pids[0], SIGUSR1);
        }
      exit(0);
    }

      sleep(4);
      for(i=0; i<2; i++)
    {
      waitpid(pids[i], NULL, 0);
      fprintf(stdout, "PID=%d exited\n", pids[i]);
    }

    }
  return(0);
}

edit: code fixed

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#define MAXC 30

pid_t pids[2];
int main()
{
  int fd[2], i, nW;
  size_t l;
  char string[MAXC+1], stringtoup[MAXC+1], tmp[MAXC+1];

  if(pipe(fd)==0)
    {    
      pids[0]=fork();
      if(pids[0]==0)
    {
      fprintf(stdout, "PID=%d PRODUCER\n", getpid());
      close(fd[0]); //produttore
      fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
      fscanf(stdin, "%s", string);
      while(1)
        {
          if(strcmp(string, "end")==0)
        break;
          l=strlen(string)+1;
          sprintf(tmp, "%2lu%s", l, string);
          nW=write(fd[1], tmp, (l+2));
          if(nW!=(l+2))
        {
          perror("wrote not whole string");
          exit(1);
        }
          sleep(1);

          fscanf(stdin, "%s", string);
        }
      kill(pids[1], SIGINT);
      exit(0);
    }
      pids[1]=fork();
      if(pids[1]==0)
    {
      fprintf(stdout, "PID=%d CONSUMER\n", getpid());
      close(fd[1]); //consumer

      while(1)
        {
          read(fd[0], tmp, MAXC+1);
          sscanf(tmp, "%2lu%s", &l, stringtoup);
          for(i=0; i<l; i++)
        stringtoup[i]=toupper(stringtoup[i]);

          fprintf(stdout, "%s\n", stringtoup);
          fflush(stdout);
          sleep(1);
        }
      exit(0);
    }

      for(i=0; i<2; i++)
    {
      waitpid(pids[i], NULL, 0);
      fprintf(stdout, "PID=%d exited\n", pids[i]);
    }
    }
  return(0);
}
Zanarkand
  • 3
  • 8
  • 3
    Considering you are not showing any code, what kind of answer do you expect? To include code in your question, just [edit] it, add the code, and press Ctrl-K to add code formatting. – Fabio says Reinstate Monica Jan 30 '17 at 18:12
  • Done. Thank you for the help! – Zanarkand Jan 30 '17 at 19:02
  • However I've fixed the code (there was an other error about killing child processes after read the string 'end') . But there's still a problem: the father doesn't wait any child. It should be enough fast to call the first waitpid. – Zanarkand Jan 30 '17 at 20:54

1 Answers1

2

By reading the comments I guess the question is changed and concerns the kill-wait procedure.

The problem should be that the children have nothing to do with the parent's memory from the time they get forked.

The first child (producer) has the pids array filled as:

pids[0] == 0;
pids[1] == undefined;

The value of pids[1] never changes as this process never changes that part of its own memory.

The second child (consumer) has the pids array filled as:

pids[0] == first_child's pid;
pids[1] == 0;

All in all, only the parent can kill the second child (one with pids[1]), not the first child. So the "producer" kills a process with some pid you do not know (that could cause a wider system problem if not lucky), so the "consumer" never gets it.

When there are no writers on the pipe though, the read() function returns zero (0). That is what you should take advantage of, break when it happens and exit. On the other hand, when there are writers the read() function blocks until there is something to read, sosleep(1) is not needed.

As I can see the program fails right when the killing of that random process is executed, and that is why the parent does not print the exit of any child. However if you remove that kill(...), close the write-end of the pipe and check the return value of read(...) the program runs as it should.

Also, a good pattern is to close all fds you do not need (even though the exit function will do it at some point), so the parent right after having spawned all the children could close the 2 fds (it has to close the write-end for the reader to break!), and the reader could close the remaining fd before the exit. Below there are the parts of the code that need to be fixed.

...
 if(pids[0]==0)                  
            {                                           
                    fprintf(stdout, "PID=%d PRODUCER\n", getpid());   
                    close(fd[0]); //produttore                              
                    fprintf(stdout, "Insert strings (max 30 chars), finish with 'end':\n");
                    fscanf(stdin, "%s", string);                                        
                    while(1)                                                                  
                    {                                                                                         
                            if(strcmp(string, "end")==0)                                                                        
                                    break;                                                                                                      
                            l=strlen(string)+1;                                                                                           
                            sprintf(tmp, "%2lu%s", l, string);                                                                                      
                            nW=write(fd[1], tmp, (l+2));                                                                                                      
                            if(nW!=(l+2))                                                                                                                               
                            {                                                                                                                                                           
                                    perror("wrote not whole string");                                                                                                                                                                            exit(1);
                            }                                                                                                                                                                                                            sleep(1);
                                                                                                                                                                                                                                         fscanf(stdin, "%s", string);
                    }                                                                                                                                                             
                    close(fd[1]);
                    exit(0);
            }
            pids[1]=fork();
            if(pids[1]==0) 
            {              
                    fprintf(stdout, "PID=%d CONSUMER\n", getpid());                                           
                    close(fd[1]); //consumer      

                    while(1)                                        
                    {                                                             
                            if (read(fd[0], tmp, MAXC+1) == 0)
                                    break;
                            sscanf(tmp, "%2lu%s", &l, stringtoup);
                            for(i=0; i<l; i++)                                                
                                    stringtoup[i]=toupper(stringtoup[i]);                                     

                            fprintf(stdout, "%s\n", stringtoup);
                            fflush(stdout);
                            sleep(1); // this could be removed
                    }
                    close(fd[0]);
                    exit(0);
            }

            close(fd[0]);
            close(fd[1]);
            for(i=0; i<2; i++)
...
elikatsis
  • 479
  • 1
  • 3
  • 8