-2

Could someone help me step through the following C code to understand what the output should be.

int x = 0;
pid_t pid;
int flag=1;

void handler1(int sig) {
    x=x+1;
    printf("%d\n",x);
    fflush(stdout);
    exit(0);
}

void handler2(int sig) {
    x = x + 2;
    flag = 0;
    waitpid(-1,NULL, 0);
}

int main() {
    signal(SIGUSR1, handler1);
    signal(SIGCHLD, handler2);
    printf("%d\n",x);
    fflush(stdout);
    if ((pid = fork()) == 0) {
        while(1) {};
    }
    kill(pid, SIGUSR1);
    while(flag) {};
    printf("%d\n",x);
    fflush(stdout);
    exit(0);
}

I thinking the ouput should be "213" but I'm not sure.

Kromster
  • 7,181
  • 7
  • 63
  • 111
user3610554
  • 23
  • 1
  • 5
  • 2
    Have you considered compiling it and executing it? – zneak May 08 '14 at 05:37
  • Why do you think it would print 2 first? (@zneak: problem with that is: what if the program has undefined behavior? The use of `flag` seems suspicious to me.) – Mat May 08 '14 at 05:49

2 Answers2

0

Technically, it is undefined, because you use stdio functions from a signal handler. I'd replace the printf/fflush combination with a single write (but bonus points for actually flushing the stream). Exiting from a signal handler is also considered bad form.

Also, both x and flag need to be made volatile.

  1. The first thing that happens is that x is printed, while it still has the value 0.
  2. Then, the process is fork()ed.
  3. The SIGUSR1 is delivered to the child, and the handler invoked in it. The handler increments x inside the child, and prints 1.
  4. The child exits, and the parent's SIGCHLD handler is invoked, incrementing the parent's copy of x (which is still 0) by 2.
  5. The parent exits the loop polling for flag, and prints the current value, 2.

So the output should be 012.

Community
  • 1
  • 1
Simon Richter
  • 28,572
  • 1
  • 42
  • 64
0

This code is the story of a parent thread 'main()', and a child thread created by 'fork()'.

The story starts with the parent thread, which sets the stage with signal handlers, and then printing out the value of x, which was previously initialized to 0.

Then, the parent gives birth to a 'child' thread, sends the child a SIGUSR1 signal, and then gets stuck in a while loop. The only way out of the loop is for something to clear the parent's (copy of the) 'flag' variable.

Now the story turns to the child; It turns out that the child was able to inherit all the riches of the parent, including it's own copy of 'x' (initialized to 0 by the parent).

Unfortunately, after being born, the child enters enters an infinite loop. The only way that the child can exit the loop is to handle a 'signal'. Fortunately, for the child, the parent happened to do just that. As stated early in the story, the parent had sent a SIGUSR1 signal to the child just prior to the parent getting stuck in a loop itself.

So, the child thread rushes to 'handler1()', (which the parent had previously designated as the 'SIGUSR1' handler). While there, it increments its (the child's) copy of 'x' from 0 to 1, and prints the new value (1). After printing, the child thread is summarily assassinated by the 'exit()' function.

...But there is more to the child's story! With the child's last gasping breath, it cries-out a 'SIGCHLD' signal to the parent (thanks to the 'exit()' function). Then, the child is laid to a final rest. (Rest in peace child, rest in peace.)

Meanwhile, the parent has been stuck in a loop; but the 'SIGCHLD' signal has caused the parent to run to 'handler2()', (which the parent had previously designated as the 'SIGCHLD' handler). In the handler, the parent first changes it's copy of 'x' from 0 to 2. But then guess what the parent finds? The 'flag' which has caused the parent to be trapped in a loop. So, the parent clears the flag, which will (after returning to the loop from 'handler2()', free the parent from the loop.

However, prior to leaving 'handler2()' the parent does one last check to ensure that the long-lost 'child' thread is dead. Upon finding that this is the case, the parent sadly returns to the loop where it has been confined.

Upon re-entering the loop, the parent finds the flag is now clear, and breaks out of the loop! It then prints it's copy of 'x' (2) and then kills itself.

THE END.

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

int x = 0;
pid_t pid;
int flag=1;

/* SIGUSR1 Signal Handler. */
/* Executed by child thread when parent sends a 'SIGUSR1' signal to child. */
void handler1(int sig)
   {
   /* Change the child's copy of 'x' from 0 to 1. */   
   x=x+1;

   /* Print the value of x [1]. 'flush()' ensures that it prints -now- */
   printf("%d\n",x);
   fflush(stdout);

   /* A 'SIGCHLD' signal is sent to the parent, and thread self-terminates. */
   exit(0);
   }

/* SIGCHLD Signal Handler. */
/* Executed by the parent (main) thread when child sends a 'SIGCHLD' signal to parent. */
void handler2(int sig)
   {
   /* Parent changes it's copy of 'x' from 0 to 2 */
   x = x + 2;

   /* Clear 'flag', allowing parent 'main()' thread to break out of 'while()' loop. */
   flag = 0;

   /* Wait for all child processes to terminate. */
   waitpid(-1,NULL, 0);
   }

int main()
  {
  /* Register 'handler1()' as the 'SIGUSR1' signal handler. */   
  signal(SIGUSR1, handler1);

  /* Register 'handler1()' as the 'SIGCHLD' signal handler. */
  signal(SIGCHLD, handler2);

  /* Print the value of x [0]. 'flush()' ensures that it prints -now- */
  printf("%d\n",x);
  fflush(stdout);

  /* 'fork()' creates a 'child' process. */ 
  if ((pid = fork()) == 0)
     {
     /* This "infanate loop" is executed by the 'child' process only. */
     while(1)
        {};
     }

  /* Parent 'main()' thread sends a 'SIGUSR1' signal to the 'child' process. */   
  kill(pid, SIGUSR1);

  /* Loop, waiting for child to clear 'flag' */
  while(flag)
     {};

  /* Print the value of x [2]. 'flush()' ensures that it prints -now- */
  printf("%d\n",x);
  fflush(stdout);

  /* Terminate parent 'main()' thread. */
  exit(0);
  }

Output:

0
1
2

Mahonri Moriancumer
  • 5,993
  • 2
  • 18
  • 28