[APUE]Synchronization between parent and child processes is achieved using signals
Question:
Write a program to test the five functions mentioned in APUE(TELL_WAIT, TELL_CHILD, TELL_PARENT, WAIT_PARENT, WAIT_CHILD). This process to create a file, write an integer 0 into this file as count value and calls fork to create child process. After that, parent and child process alternate increase the count value in the file, every time the process information will be printed while count value increases 1.
Below is the code:
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> static volatile sig_atomic_t sigflag; static sigset_t newmask, oldmask, zeromask; static void sig_usr(int signo) { sigflag = 1; } void TELL_WAIT() { if (signal(SIGUSR1, sig_usr) == SIG_ERR) { printf("SIGUSR1 error\n"); } if (signal(SIGUSR2, sig_usr) == SIG_ERR) { printf("SIGUSR2 error\n"); } sigemptyset(&newmask); sigemptyset(&zeromask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { printf("SIG_BLOCK error\n"); } } void TELL_CHILD(pid_t pid) { kill(pid, SIGUSR2); } void TELL_PARENT(pid_t pid) { kill(pid, SIGUSR1); } void WAIT_PARENT() { while(sigflag == 0) { sigsuspend(&zeromask); //wait for a signal, then the sig_usr is invoked, so the sigflag = 1; } sigflag = 0; // if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { // printf("sig_setmask error\n"); // } } void WAIT_CHILD() { while(sigflag == 0) { sigsuspend(&zeromask); } sigflag = 0; // if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { // printf("sig_setmask error\n"); // } } int main() { pid_t pid; FILE *fp = NULL; if ((fp = fopen("data.txt", "w+")) == NULL) { printf("open file error\n"); } fprintf(fp, "%d\n", 0); fclose(fp); TELL_WAIT(); if ((pid = fork()) < 0) { printf("fork error\n"); } else if (pid == 0) { int i = 0; for (i = 0; i < 10; i++) { if ((fp = fopen("data.txt", "r+")) == NULL) { printf("open file error\n"); } int cnt = 0; fscanf(fp, "%d", &cnt); cnt++; fseek(fp, 0, SEEK_SET); fprintf(fp, "%d", cnt); printf("\tAdd 1 in child, now cnt = %d\n", cnt); fclose(fp); TELL_PARENT(getppid()); WAIT_PARENT(); } exit(0); } int i = 0; int cnt = 0; for (i = 0; i < 10; i++) { //wait a signal, after receive a signal from child, sigflag = 1, break while, then sigflag = 0; //so the WAIT_PARENT is in while until TELL_CHILD send a signal. WAIT_CHILD(); if ((fp = fopen("data.txt", "r+")) == NULL) { printf("open file error\n"); } fscanf(fp, "%d", &cnt); ++cnt; fseek(fp, 0, SEEK_SET); //because we need to write in 0 pos. fprintf(fp, "%d", cnt); printf("Add 1 in parent, now cnt = %d\n", cnt); fclose(fp); TELL_CHILD(pid); } //Reset signal mask to original value. if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { printf("sig_setmask error\n"); } return 0; }
Output:
Add 1 in child, now cnt = 1
Add 1 in parent, now cnt = 2
Add 1 in child, now cnt = 3
Add 1 in parent, now cnt = 4
Add 1 in child, now cnt = 5
Add 1 in parent, now cnt = 6
Add 1 in child, now cnt = 7
Add 1 in parent, now cnt = 8
Add 1 in child, now cnt = 9
Add 1 in parent, now cnt = 10
Add 1 in child, now cnt = 11
Add 1 in parent, now cnt = 12
Add 1 in child, now cnt = 13
Add 1 in parent, now cnt = 14
Add 1 in child, now cnt = 15
Add 1 in parent, now cnt = 16
Add 1 in child, now cnt = 17
Add 1 in parent, now cnt = 18
Add 1 in child, now cnt = 19
Add 1 in parent, now cnt = 20
Description:
- Pay attention to the fopen parameter when reading or writing to the file. In my case, the first time I open and write 0 to it using “w+” parameter. This is to create a file if it doesn’t exist, and to truncate the file. So using this parameter can increase the robustness of the code, regardless of whether the file exists, regardless of what the file was written before. For the following part, use “r+” to read and write to the file without truncating it. That’s exactly what the problem wants.
- The TELL_WAIT function masks SIGUSR1 and SIGUSR2 signals, removes the mask from sigsuspend and suspends the process, this is a one-atom operation. The pending process doesn’t end until the signal arrives. At this point, the global variable sigflag changes to 1, breaking out of the while loop, and then immediately sets the value to 0, so another WAIT function is waiting for the signal and suspend the process until another TELL occurs.
- I put restoring the original mask at the end of the parent process, rather than in the WAIT function, because I felt that this was a loop and it was not appropriate to restore the original mask the first time I called WAIT.
Any feedbacks are welcome.
Share this article to your social media