36057

C, getting a parent and child process to work using signals

Question:

I'm attempting to get my child process to generate a random number between 1 -9, then send it to my parent process for which it would display it to screen each time control + Z is pressed. I'm also using dup2() to change the stream from printf and scanf to the read and write ends of the pipe for each process.

The code works as below, you press control C to start the main program. Now it waits for control Z to be pressed for each time a random number is sent from child and displayed at parent. The issue I am facing is, it seems the child only runs <strong>once</strong> when control Z is pressed again, it only runs the parent code. The pipe also only ever shows the same number and never changes so its not being updated. I also noticed that if I remove the printf line before the dup function <strong>printf("a \n")</strong>, it prints out random numbers....however the problem with the child running once still exists.

Would appreciate if someone can assist me on this. Cheers.

#include <stdio.h> #include <signal.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <string.h> #include <stdlib.h> int selection = 0; int secondSel = 0; int fd[2]; int pipe1; pid_t fork1; void handleSignal(int sig) { if (sig == SIGINT) { selection = 1; secondSel = 1; } if (sig == SIGTSTP) { selection = 1; } } int main() { int firstPipe[2]; int secondPipe[2]; //wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C \n"); exit(0); } while(1) { //wait till control c is pressed if (selection == 1) { signal(SIGINT, SIG_IGN); if (secondSel == 1) { pipe1 = pipe(fd); } if (pipe1 < 0) { printf("Error creating pipe 1 \n"); exit(1); } if (secondSel == 1) { fork1 = fork(); } if (fork1 < 0) { printf("Error with first fork. \n"); exit(1); } else if (fork1 == 0) //first child process { signal(SIGTSTP, handleSignal); pause(); printf("a \n"); int randNum1; close(fd[0]); dup2(fd[1], 1); randNum1 = rand() % 9 + 1; printf("%d ", randNum1); fflush(stdout); close(fd[1]); } else //parent { signal(SIGTSTP, handleSignal); pause(); printf("b \n"); int f; close(fd[1]); dup2(fd[0], 0); scanf("%d \n", &f); printf("%d \n", f); close(fd[0]); } secondSel = 0; } } }

Answer1:

The comment/code:

//wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR)

is a disconcerting start. The signal() function sets the signal handler for SIGINT, but it no way waits for a signal to arrive. The simplest fix for that is to add a call to <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html" rel="nofollow">pause()</a> after that block of code.

Inside the infinite loop, the code:

if (secondSel == 1) { pipe1 = pipe(fd); } if (pipe1 < 0) { printf("Error creating pipe 1 \n"); exit(1); }

is sub-optimal and/or confusing. Since pipe1 is only set when pipe() is called, there's no need to test it on each iteration. Error messages should be reported on standard error, and should not have trailing blanks. The code should be:

if (secondSel == 1) { if (pipe(fd) < 0) { fprintf(stderr, "Error creating pipe 1\n"); exit(1); } }

There's a similar test on the same variable protecting the fork().

Your code carefully closes the pipe after the first cycle, but never reopens it. That is ultimately why the second and subsequent iterations fail. Your code would be better if you didn't try to do everything on each cycle. Also, using standard output for debugging information is subject to various problems; it is better to use standard error instead — especially when standard output isn't working properly.

<h3>Instrumented code</h3>

Here's an instrumented version of your code. The err_syserr() function is almost generic; the condition using usleep() is peculiar to this code and ensures that the output of the terminated error message is normally sequenced. I put the tests on the function calls that would be failing — the first close failed on the second cycle because the pipe descriptors are all closed at the end of the first cycle. (Note that reusing pipe() is no help after the fork() — the pipes in the parent and child would not be connected to each other.)

#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void err_syserr(const char *fmt, ...); int selection = 0; int secondSel = 0; int fd[2]; int pipe1 = 0; pid_t fork1 = 0; static void handleSignal(int sig) { if (sig == SIGINT) { selection = 1; secondSel = 1; } if (sig == SIGTSTP) { selection = 1; } } int main(void) { // wait for control+C if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C\n"); exit(1); } //printf("Waiting for interrupt\n"); //pause(); while (1) { fprintf(stderr, "Looping: %d (%d)\n", (int)getpid(), selection); // wait till control c is pressed if (selection == 1) { signal(SIGINT, SIG_IGN); if (secondSel == 1) { pipe1 = pipe(fd); fprintf(stderr, "Created pipe: %d (%d, %d)\n", pipe1, fd[0], fd[1]); } if (pipe1 < 0) { printf("Error creating pipe 1\n"); exit(1); } if (secondSel == 1) { fork1 = fork(); fprintf(stderr, "Forked: %d (%d, %d)\n", fork1, (int)getpid(), (int)getppid()); } if (fork1 < 0) { printf("Error with first fork.\n"); exit(1); } else if (fork1 == 0) // first child process { signal(SIGTSTP, handleSignal); fprintf(stderr, "Pausing C: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused C: %d\n", (int)getpid()); printf("a\n"); if (close(fd[0]) != 0) err_syserr("close(fd[0]=%d) failed", fd[0]); if (dup2(fd[1], 1) < 0) err_syserr("dup2(fd[1]=%d, 1) failed", fd[1]); int randNum1 = rand() % 9 + 1; fprintf(stderr, "Print C: %d\n", randNum1); if (printf("%d\n", randNum1) < 2) { fprintf(stderr, "Print C: failed\n"); clearerr(stdout); } fflush(stdout); if (close(fd[1]) != 0) err_syserr("close(fd[1]=%d) failed", fd[1]); } else // parent { signal(SIGTSTP, handleSignal); fprintf(stderr, "Pausing P: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused P: %d\n", (int)getpid()); printf("b\n"); if (close(fd[1]) != 0) err_syserr("close(fd[1]=%d) failed", fd[1]); if (dup2(fd[0], 0) < 0) err_syserr("dup2(fd[0]=%d, 0) failed", fd[0]); int f = 99; if (scanf("%d", &f) != 1) { fprintf(stderr, "Scanf P: failed\n"); clearerr(stdin); } printf("Parent: %d\n", f); if (close(fd[0]) < 0) err_syserr("close(fd[0]=%d) failed", fd[0]); } secondSel = 0; } } return 0; } #include <errno.h> #include <string.h> #include <stdarg.h> static void err_syserr(const char *fmt, ...) { int errnum = errno; va_list args; if (fork1 != 0) /* Parent waits 1/4 second */ usleep(250000); fprintf(stderr, "%d: ", (int)getpid()); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); if (errnum != 0) fprintf(stderr, ": %d %s", errnum, strerror(errnum)); fputc('\n', stderr); exit(EXIT_FAILURE); }

Example output:

$ ./sigbug Looping: 23528 (0) Looping: 23528 (0) … …huge numbers of 'looping' messages omitted… … Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) ^CLooping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Looping: 23528 (0) Created pipe: 0 (3, 4) Forked: 23529 (23528, 45428) Pausing P: 23528 Forked: 0 (23529, 23528) Pausing C: 23529 ^ZUnpaused C: 23529 Unpaused P: 23528 a b Print C: 5 Looping: 23529 (1) Pausing C: 23529 Parent: 5 Looping: 23528 (1) Pausing P: 23528 ^ZUnpaused P: 23528 b Unpaused C: 23529 23529: close(fd[0]=3) failed: 9 Bad file descriptor 23528: close(fd[1]=4) failed: 9 Bad file descriptor $ <h3>Bug fixed code</h3>

Here's a revised version of the code with what seems to me more appropriate logic. It doesn't include err_syserr() because the function calls aren't failing. The selection and secondSel variables aren't needed; the signal handler becomes a stub that just contains return; (which could be omitted). It doesn't loop like mad at the start because I put a pause in place to wait for the interrupt.

#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void handleSignal(int sig) { return; } int main(void) { int fd[2]; int pipe1 = 0; pid_t fork1 = 0; if (signal(SIGINT, handleSignal) == SIG_ERR) { printf("Error catching signal C\n"); exit(1); } printf("Waiting for interrupt\n"); pause(); signal(SIGINT, SIG_IGN); pipe1 = pipe(fd); if (pipe1 < 0) { fprintf(stderr, "Error creating pipe 1\n"); exit(1); } fprintf(stderr, "Created pipe: %d (%d, %d)\n", pipe1, fd[0], fd[1]); fork1 = fork(); if (fork1 < 0) { fprintf(stderr, "Error with fork.\n"); exit(1); } fprintf(stderr, "Forked: %d (%d, %d)\n", fork1, (int)getpid(), (int)getppid()); signal(SIGTSTP, handleSignal); if (fork1 == 0) { dup2(fd[1], 1); close(fd[0]); close(fd[1]); while (1) { fprintf(stderr, "Pausing C: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused C: %d\n", (int)getpid()); int randNum1 = rand() % 9 + 1; fprintf(stderr, "Print C: %d\n", randNum1); if (printf("%d\n", randNum1) < 2) { fprintf(stderr, "Print C: failed\n"); clearerr(stdout); } fflush(stdout); } } else { dup2(fd[0], 0); close(fd[0]); close(fd[1]); while (1) { fprintf(stderr, "Pausing P: %d\n", (int)getpid()); pause(); fprintf(stderr, "Unpaused P: %d\n", (int)getpid()); int f = 99; if (scanf("%d", &f) != 1) { fprintf(stderr, "Scanf P: failed\n"); clearerr(stdin); } else printf("Parent: %d\n", f); } } return 0; }

Example output:

$ ./sigbug-jl1 Waiting for interrupt ^CCreated pipe: 0 (3, 4) Forked: 23554 (23553, 45428) Pausing P: 23553 Forked: 0 (23554, 23553) Pausing C: 23554 ^ZUnpaused C: 23554 Print C: 5 Unpaused P: 23553 Pausing C: 23554 Parent: 5 Pausing P: 23553 ^ZUnpaused C: 23554 Print C: 8 Unpaused P: 23553 Pausing C: 23554 Parent: 8 Pausing P: 23553 ^ZUnpaused P: 23553 Unpaused C: 23554 Print C: 6 Pausing C: 23554 Parent: 6 Pausing P: 23553 ^\Quit: 3 $

I used SIGQUIT to terminate the program since interrupts are disabled.

Answer2:

The issue you are having is due to closing the pipe after the first number is sent from the child to the parent.

However, the main problem is that your code is disorganized, which makes it harder to figure out what is going on and increases the chance of mistakes. Rather than having everything in one big loop, I would suggest restructuring your code as follows:

wait for SIGINT create pipe fork if child: set up stdout using dup2() while true: wait for SIGTSTP write to pipe else: set up stdin using dup2() while true: wait for SIGTSTP read from pipe etc.

Recommend

  • Javascript Toggle : Hide div on click of anywhere
  • Write to file that is open in Excel
  • How do I flush NSLog's buffer?
  • How get a focus element in QWebView/QWebPage?
  • JTable Alert on invalid value
  • How do you increment a count while using the sleep function?
  • Using Node cluster module with SailsJs: EADDRINUSE
  • Associate git repo with existing fork network
  • Read stdin in chunks in Bash pipe
  • APNs messages are delivered but not received on iOS device
  • triggering user space with kernel
  • how do i write assembly code from c#?
  • Ubuntu and bcrypt
  • How to get latest version of a artifact on Bintray using JSONP
  • Combining two different ActiveRecord collections into one
  • bad substitution shell- trying to use variable as name of array
  • Redirect STDERR in OPEN pipe comand. Perl Linux
  • Using JRuby with Rails 3.2
  • wxPython: displaying multiple widgets in same frame
  • Code in Job's Script Block after Start-Process Does not Execute
  • Stop Bash Script if Hive Fails
  • C: Incompatible pointer type initializing
  • Google Custom Search with transparent background
  • Initializer list vs. initialization method
  • Control modification in presentation layer
  • Why value captured by reference in lambda is broken? [duplicate]
  • Sails.js/waterline: Executing waterline queries in toJSON function of a model?
  • Fetching methods from BroadcastReceiver to update UI
  • Obtain ObjectIdHex value from mgo query
  • Volusion's generic SQL folder, functionality
  • How to set/get protobuf's extension field in Go?
  • How to handle AllServersUnavailable Exception
  • JSON with duplicate key names losing information when parsed
  • Can I have the cursor start on a particular column by default in jqgrid's edit mode?
  • Rearranging Cells in UITableView Bug & Saving Changes
  • Circular dependency while pushing http interceptor
  • Linker errors when using intrinsic function via function pointer
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Linking SubReports Without LinkChild/LinkMaster
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass