Reputation: 167
I'm workng on a C program with which I'd like to implement a scenario when a parent and two children processes are communicating with each other via two separate unnamed pipes reserved for each of the children. Before this both children should send a signal to the parent. That's not a problem. However, in case the parent would like to write the second unnamed pipe fds_2
in order for the second child to access its message, then I'm unable to achieve the deisred goal.
I attempted to accomplish the desired outcome with the help of the SIGUSR1
and SIGUSR2
constants. They're reserved for the first and second child, respectively. First, the parent has to receive all the signals. and only then it shall send the message to both children on separate pipes. In the parent I attempt to open the pipes for writing only. In the children following the sigqueue call I attempt to open the pipes for reading only.
I experience that only the first child receives the text message and then the execution hangs.
I know that printf()
inside a handler function body is not desired, but I'm only including that for experimenting purposes.
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#define CHLD_1_NOTIFY_PARENT SIGUSR1
#define CHLD_2_NOTIFY_PARENT SIGUSR2
#define PARENT_NOTIFY_CHLD_1 SIGUSR1
#define PARENT_NOTIFY_CHLD_2 SIGUSR2
int main()
{
pid_t parent_pid, pid, pid_2;
sigset_t newmask, oldmask;
union sigval value;
struct sigaction action;
int fds[2];
pipe(fds);
int fds_2[2];
pipe(fds_2);
parent_pid = getpid();
if ( (pid = fork() ) )
{ /*parent*/
void parent_handler( int signo, siginfo_t* info, void* context );
sigemptyset(&newmask);
sigaddset(&newmask, CHLD_1_NOTIFY_PARENT);
sigaddset(&newmask, CHLD_2_NOTIFY_PARENT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = parent_handler;
if (sigaction(CHLD_1_NOTIFY_PARENT, &action, NULL) == -1) {
perror("sigusr: sigaction");
}
if (sigaction(CHLD_2_NOTIFY_PARENT, &action, NULL) == -1) {
perror("sigusr: sigaction");
}
if( ( pid_2 = fork() )== 0 ) //2. child
{
printf("2. child started\n");
value.sival_int = 43;
sigqueue( parent_pid, CHLD_2_NOTIFY_PARENT, value );
printf("2. child sends CHLD_2_NOTIFY_PARENT\n");
close(fds[0]);
close(fds[1]);
close(fds_2[1]);
char buffer[6];
read(fds_2[0], buffer, sizeof(buffer));
printf("2. child receives through pipe: %s.\n", buffer);
close(fds_2[0]);
printf("2. child terminates\n");
}
else
{
//value.sival_int = 44;
//sigqueue( pid, PARENT_NOTIFY_CHLD_1, value );
sleep(1);
sigsuspend(&oldmask);
close(fds[0]);
write(fds[1], "hello", 6);
wait(NULL);
sigsuspend(&oldmask);
close(fds[1]);
close(fds_2[0]);
write(fds_2[1], "hello", 6);
wait(NULL);
close(fds_2[1]);
waitpid(pid, NULL, 0 );
waitpid( pid_2, NULL, 0 );
printf("Parent terminates\n");
}
}
else /* 1. child */
{
printf("1. child starts\n");
//void chld_handler( int signo, siginfo_t* info, void* context );
sigemptyset(&newmask);
sigaddset(&newmask, PARENT_NOTIFY_CHLD_1);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
action.sa_flags = SA_SIGINFO;
//action.sa_sigaction = chld_handler;
if (sigaction( PARENT_NOTIFY_CHLD_1, &action, NULL) == -1) {
perror("sigusr: sigaction");
}
value.sival_int = 42;
sigqueue( parent_pid, CHLD_1_NOTIFY_PARENT, value );
printf("1. child sends CHLD_1_NOTIFY_PARENT signal to parent\n");
close(fds[1]);
char buffer[6];
read(fds[0], buffer, sizeof(buffer));
printf("1. child receives through pipe: %s.\n", buffer);
close(fds[0]);
printf("1. child terminates\n");
}
}
void parent_handler(int signo, siginfo_t* info, void* context)
{
int int_msg = info->si_value.sival_int;
int chld = 2;
if( int_msg % 2 == 0 )
{
chld = 1;
}
printf("parent receives %i from child %i. with data %i\n", signo, chld, int_msg );
}
Thank you very much in advance!
EDIT: The modified version in code:
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#define CHLD_1_NOTIFY_PARENT SIGUSR1
#define CHLD_2_NOTIFY_PARENT SIGUSR2
#define PARENT_NOTIFY_CHLD_1 SIGUSR1
#define PARENT_NOTIFY_CHLD_2 SIGUSR2
void parent_handler(int signo, siginfo_t* info, void* context)
{
int int_msg = info->si_value.sival_int;
int chld = 2;
if( int_msg % 2 == 0 )
{
chld = 1;
}
printf("parent receives %i from child %i. with data %i\n", signo, chld, int_msg );
}
int main()
{
pid_t parent_pid, pid, pid_2;
sigset_t oldmask_1, oldmask_2, newmask_1, newmask_2;
union sigval value;
struct sigaction action;
int fds[2];
pipe(fds);
int fds_2[2];
pipe(fds_2);
parent_pid = getpid();
sigemptyset(&newmask_1);
sigaddset(&newmask_1, CHLD_1_NOTIFY_PARENT);
sigprocmask(SIG_BLOCK, &newmask_1, &oldmask_1);
sigemptyset(&newmask_2);
sigaddset(&newmask_2, CHLD_2_NOTIFY_PARENT);
sigprocmask(SIG_BLOCK, &newmask_2, &oldmask_2);
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = parent_handler;
if (sigaction(CHLD_1_NOTIFY_PARENT, &action, NULL) == -1) {
perror("sigusr: sigaction");
}
if (sigaction(CHLD_2_NOTIFY_PARENT, &action, NULL) == -1) {
perror("sigusr: sigaction");
}
if ( (pid = fork() ) )
{ /*parent*/
if( ( pid_2 = fork() )== 0 ) //2. child
{
printf("2. child started\n");
value.sival_int = 43;
sigqueue( parent_pid, CHLD_2_NOTIFY_PARENT, value );
printf("2. child sends CHLD_2_NOTIFY_PARENT\n");
close(fds[0]);
close(fds[1]);
close(fds_2[1]);
char buffer[6];
read(fds_2[0], buffer, sizeof(buffer));
printf("2. child receives through pipe: %s.\n", buffer);
close(fds_2[0]);
printf("2. child terminates\n");
}
else
{
//value.sival_int = 44;
//sigqueue( pid, PARENT_NOTIFY_CHLD_1, value );
sleep(1);
sigsuspend(&oldmask_1);
close(fds[0]);
write(fds[1], "hello", 6);
wait(NULL);
sigsuspend(&oldmask_2);
close(fds[1]);
close(fds_2[0]);
write(fds_2[1], "hello", 6);
wait(NULL);
close(fds_2[1]);
waitpid(pid, NULL, 0 );
waitpid( pid_2, NULL, 0 );
printf("Parent terminates\n");
}
}
else /* 1. child */
{
printf("1. child starts\n");
value.sival_int = 42;
sigqueue( parent_pid, CHLD_1_NOTIFY_PARENT, value );
printf("1. child sends CHLD_1_NOTIFY_PARENT signal to parent\n");
close(fds[1]);
char buffer[6];
read(fds[0], buffer, sizeof(buffer));
printf("1. child receives through pipe: %s.\n", buffer);
close(fds[0]);
printf("1. child terminates\n");
}
}
The modified version regarding a terminating output:
1. child starts
1. child sends CHLD_1_NOTIFY_PARENT signal to parent
2. child started
2. child sends CHLD_2_NOTIFY_PARENT
parent receives 10 from child 1. with data 42
1. child receives through pipe: hello.
1. child terminates
parent receives 12 from child 2. with data 43
2. child receives through pipe: hello.
2. child terminates
Parent terminates
The new version sometimes doesn't block, sometimes blocks.
Upvotes: 0
Views: 52
Reputation: 10271
If you want the first sigsuspend to allow CHLD_1_NOTIFY_PARENT
to be delivered and CHLD_2_NOTIFY_PARENT
to remain blocked, you need to call it with a mask that blocks CHLD_2_NOTIFY_PARENT
. Your current code is:
sigemptyset(&newmask_1);
sigaddset(&newmask_1, CHLD_1_NOTIFY_PARENT);
sigprocmask(SIG_BLOCK, &newmask_1, &oldmask_1);
sigemptyset(&newmask_2);
sigaddset(&newmask_2, CHLD_2_NOTIFY_PARENT);
sigprocmask(SIG_BLOCK, &newmask_2, &oldmask_2);
...
sigsuspend(&oldmask_1);
but this will allow delivery of both CHLD_1_NOTIFY_PARENT
and CHLD_2_NOTIFY_PARENT
. Here is the output when I ran it:
2. child started
2. child sends CHLD_2_NOTIFY_PARENT
1. child starts
1. child sends CHLD_1_NOTIFY_PARENT signal to parent
parent receives 12 from child 2. with data 43
parent receives 10 from child 1. with data 42
1. child receives through pipe: hello.
1. child terminates
^C
The first sigsuspend should be changed to use a mask that is the union of the process's original mask and CHLD_2_NOTIFY_PARENT
.
Your second sigsuspend,
sigsuspend(&oldmask_2);
is OK, because you want to allow CHLD_2_NOTIFY_PARENT
to be delivered, and oldmask_2
is the process's original mask with CHLD_1_NOTIFY_PARENT
added.
(At this point, child 1 has exited and has been waited for, so even the orignial signal mask could be used here.)
Here are the changes I made:
34c34
< sigset_t oldmask_1, oldmask_2, newmask_1, newmask_2;
---
> sigset_t origmask, mask_1, mask_2;
45,47c45,48
< sigemptyset(&newmask_1);
< sigaddset(&newmask_1, CHLD_1_NOTIFY_PARENT);
< sigprocmask(SIG_BLOCK, &newmask_1, &oldmask_1);
---
> sigprocmask(SIG_BLOCK, NULL, &origmask);
> mask_1 = origmask;
> sigaddset(&mask_1, CHLD_1_NOTIFY_PARENT);
> sigprocmask(SIG_BLOCK, &mask_1, NULL);
49,51c50,52
< sigemptyset(&newmask_2);
< sigaddset(&newmask_2, CHLD_2_NOTIFY_PARENT);
< sigprocmask(SIG_BLOCK, &newmask_2, &oldmask_2);
---
> mask_2 = origmask;
> sigaddset(&mask_2, CHLD_2_NOTIFY_PARENT);
> sigprocmask(SIG_BLOCK, &mask_2, NULL);
99c100
< sigsuspend(&oldmask_1);
---
> sigsuspend(&mask_2);
106c107
< sigsuspend(&oldmask_2);
---
> sigsuspend(&mask_1);
The output is this:
2. child started
2. child sends CHLD_2_NOTIFY_PARENT
1. child starts
1. child sends CHLD_1_NOTIFY_PARENT signal to parent
parent receives 10 from child 1. with data 42
1. child receives through pipe: hello.
1. child terminates
parent receives 12 from child 2. with data 43
2. child receives through pipe: hello.
2. child terminates
Parent terminates
Upvotes: 1