So, my task is to sync parent and his 2 children in this way:
one child sends SIGUSR2 signal to parent and then blocks waiting parent msg.
The sync is implemented by global flags, so the parent waits for any of the flag_ch become 1 (it happens when child sends SIGUSR2) and then sends signal SIGUSR1 to this child, and child resumes (cause global flag_p becomes 1)
the trouble is that parent receives signals only from one child, and then blocks waiting for second child signals, but they don't appear
.
any idea?..
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/signalfd.h>
#define LPC 10
pid_t ch[2];
sig_atomic_t flag_ch[2] = {0, 0};
sig_atomic_t flag_p = 0;
int get_ind(pid_t ch_pid) {
int i;
for (i = 0; i < 2; ++i) {
if (ch_pid == ch[i])
return i;
}
return -1;
}
void usr_handler(int signo, siginfo_t* si, void* unused) {
int ch_index;
switch(signo) {
case SIGUSR2:
ch_index = get_ind(si->si_pid);
if (ch_index >= 0)
flag_ch[ch_index] = 1;
else
fprintf(stderr, "signal handled not from child pid %d\n", si->si_pid);
break;
case SIGUSR1:
flag_p = 1;
break;
}
}
void set_usr_handler(void) {
struct sigaction sa;
sa.sa_sigaction = usr_handler;
sa.sa_flags = SA_SIGINFO;
sa.sa_restorer = NULL;
sigemptyset(&sa.sa_mask);
if (0 != sigaction(SIGUSR1, &sa, NULL))
abort_prg("signal [SIGUSR1] error");
if (0 != sigaction(SIGUSR2, &sa, NULL))
abort_prg("signal [SIGUSR2] error");
}
void child_proc(void) {
int i;
for (i = 0; i < LPC; ++i) {
if (0 != kill(getppid(), SIGUSR2))
exit(1);
while (0 == flag_p) { };
flag_p = 0;
}
}
int wait_child(void) {
while (0 == flag_ch[0] && 0 == flag_ch[1]) { };
if (1 == flag_ch[0]) {
flag_ch[0] = 0;
return ch[0];
}
flag_ch[1] = 0;
return ch[1];
}
void parent_proc(void) {
int i;
pid_t ch_pid;
for (i = 0; i < LPC * 2; ++i) {
ch_pid = wait_child();
printf("Parent: Received from pid [%d]\n", ch_pid);
if (0 != kill(ch_pid, SIGUSR1))
exit(1);
}
}
int main(int argc, char* argv[]) {
set_usr_handler();
int i;
for (i = 0; i < 2; ++i) {
pid_t child = fork();
if (0 > child)
exit(1);
if (0 == child) {
child_proc();
return 0;
}
ch[i] = child;
}
parent_proc();
return 0;
}
My guess is that it's missing volatile in a few global variable declarations. For example, flag_p not being volatile means that the loop
while (flag_p == 0) { }
can run forever: GCC probably compiles it to load the global variable only once into a register, and then loop until this register is non-zero (which never occurs).
A conservative approximation is that you should make volatile all mutable variables that are read from or written to in a signal handler.
EDIT: another source of problem I can think of is that signals are not cumulative: either the parent process has no SIGUSR2 pending, or it has one. If both children send it to the parent process at the same time, only one might be delivered, as far as I know.
EDIT: I think a "better" solution (more flexible and more portable) would be along the lines of: do not use signals at all, but use pipes. You make one pipe between the parent and each of the children, and the children send a character 'X' over the pipes when they are done. The parent waits with select(); or if it just wants to wait until both children are ready, it can read the 'X' character from one pipe and then the other, blocking in both cases (the order doesn't matter).
Related
struct job_t { /* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};
struct job_t jobs[MAXJOBS]; /* The job list */
/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
job->pid = 0;
job->jid = 0;
job->state = UNDEF;
job->cmdline[0] = '\0';
}
/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
clearjob(&jobs[i]);
}
/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob(struct job_t *jobs, pid_t pid)
{
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == pid) {
clearjob(&jobs[i]);
nextjid = maxjid(jobs)+1;
return 1;
}
}
return 0;
}
/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].state == FG)
return jobs[i].pid;
return 0;
}
/*********************functions above for prepare***********************/
/*
* waitfg - Block until process pid is no longer the foreground process
*/
void waitfg(pid_t pid)
{
sigset_t mask, prev, emp;
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &prev);
while (fgpid(jobs) == pid) {
printf("waitfg\n");
sigsuspend(&prev); /* Just wait for a signal */
}
sigprocmask(SIG_SETMASK, &prev, NULL);
return;
}
/*
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
* user types ctrl-c at the keyboard. Catch it and send it along
* to the foreground job.
*/
void sigint_handler(int sig)
{
int olderrno = errno;
pid_t pid;
pid = fgpid(jobs);
if (pid == 0)
return; /* Seem always return here */
/* Just print something */
Sio_puts("sigint runned: "); /* But never print ? */
Sio_putl((long) pid);
Sio_puts("\n");
kill(pid, SIGINT);
errno = olderrno;
return;
}
/*
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie), or stops because it
* received a SIGSTOP or SIGTSTP signal. The handler reaps all
* available zombie children, but doesn't wait for any other
* currently running children to terminate.
*/
void sigchld_handler(int sig)
{
int olderrno = errno;
sigset_t mask_all, prev_all;
pid_t pid;
sigfillset(&mask_all);
while ((pid = waitpid(-1, NULL, 0)) > 0) { /* Reap child */
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
if (!deletejob(jobs, pid))
Sio_puts("warning!: deletejob return 0\n");
sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
if (errno != ECHILD)
Sio_error("waitpid error");
errno = olderrno;
return;
}
In my assumption this sequencing should be run in order of commands. But in fact it seems to run kill(pid, SIGINT);first, when the control reaches if (pid == 0), pid's value is 0 which is another value When entering this function(i'm not sure).As the result, "sigint runned: " never print.
Another question:
void sigint_handler(int sig)
{
int olderrno = errno;
pid_t pid;
pid = fgpid(jobs);
// if (pid == 0)
// return;
/* Just print something */
Sio_puts("sigint runned: "); /* But never print ? */
Sio_putl((long) pid);
Sio_puts("\n");
kill(pid, SIGINT);
errno = olderrno;
return;
}
When i remove if (pid == 0) return;, run the program. It will print "sigint runned: 0" in a infinite loop.
This is very different from what I expected. I am very confused. Any answer will be appreciated.
I'd love to know how to debug the signal problem in VsCode. If I can debug the signal I might be able to figure it out myself.
The problem that has been bothering me for a week is finally solved.HA.
I finally solved him by learning the advanced features of debug.
The tutorial(Really appreciate his video) :https://www.youtube.com/watch?v=xAW_pNBlfnI&list=PLZbQA0LtbR-275j6lHwmtZgKzOxfIkUmo&index=1&t=1133s
As for the problem itself, my system handles the SIGCHLD signal first, and then the SIGINT, which is inconsistent with what I've learned, and I've never been able to understand it. I take it for granted to think according to what I have learned, so I have been unable to solve it. In the end, practice is the answer to all questions, and it is really important to learn to debug well.
Thanks to all gays who enthusiastically gave suggestions.
I was trying to fork processes and make them in a topology of a binary tree of N+1 nodes (1 is for the root) and then each process can send signals to process with pids ranging from [parent_pid-N,pid+N] (inclusive). I try sending the custom signal that is SIGUSR1, it terminates my terminal. I am guessing it happens because SIGUSR1's default action is to terminate and one of the process signals the process which has opened the terminal application, leading to terminal closing. So I added sleep(5) so that, my signal handlers are attached before sending signal
You can take a look at my code, maybe you can help me. I have also attached the logs.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
void tree() {
int idx = 1;
while (1)
{
int left;
if (2 * idx > N + 1)
{
break;
}
left = fork();
if (left == 0)
{
idx *= 2;
continue;
}
int right ;
if (2 * idx + 1 > N + 1)
{
break;
}
right = fork();
if (right == 0)
{
idx = 2 * idx + 1;
continue;
}
break;
}
}
void sigHandler(int sig, siginfo_t *si, void *context)
{
printf("Signal received\n");
return;
}
int main(int argc, char* argv[])
{
N = atoi(argv[1]);
int lvl = (N + 1) / 2;
int root = getpid();
//A = atoi(argv[2]);
//S = atoi(argv[3]);
printf("I am root %d\n", root);
//points = N;
//validPIDs = (int *)malloc(sizeof(int) * N);
//int counter = 0;
struct sigaction siga;
siga.sa_sigaction = *sigHandler;
siga.sa_flags |= SA_SIGINFO;
if ( sigaction(SIGUSR1, &siga, NULL) != 0)
{
printf(" Error in applying signal handler\n");
exit(0);
}
tree();
int pid = getpid();
int ppid = getppid();
int status;
for (int id = ppid - N; id <= pid + N; id++)
{
printf("from: %d,to :%d\n", getpid(), id);
sleep(5);
status = kill(id, SIGUSR1);
sleep(5);
}
wait(NULL);
wait(NULL);
return 0;
}
You are picking "random" processes to send signals tonwith your for loop. PIDs are not given in any particular way you can rely on.
You need to get the PIDs in some other way, such as return value of fork (the normal way to manage child processes) or reading from a pid file (if target process can be confgired to write a PID file), or even searching through all processes (like killall command does).
About your comment on installing signal handler: You install signal handler in your own processes, but you are killing other "random" processes, which may not have this signal handler. You can't install a signal handler on these.
I want to have a parent process and three child processes. I want these child processes to know the pids of the other child processes.
The problem is that when I do fork and then I do it again, the second fork is also executed in the child process creating an extra process (or so I think).
How could I solve it?
Thanks.
The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.
After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.
Example code (long, but that's the nature of C):
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM_CHILDREN 3
/* Entry point for the child processes */
int child_main(int pipe_read_end) {
pid_t my_pid = getpid();
/* Read child pids from pipe */
int child_pids[NUM_CHILDREN];
unsigned int bytes_read = 0;
while (bytes_read < sizeof(child_pids)) {
ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
if (result < 0) {
perror("error reading from pipe");
return 1;
} else if (result == 0) {
fprintf(stderr, "unexpected end of file\n");
return 1;
} else {
bytes_read += result;
}
}
close(pipe_read_end);
/* Do something useful with these child pids */
for (int i = 0; i < NUM_CHILDREN; i++) {
printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
}
return 0;
}
/* Entry point for the parent process. */
int main() {
int child_pids[NUM_CHILDREN];
int pipe_write_ends[NUM_CHILDREN];
for (int i = 0; i < NUM_CHILDREN; i++) {
/* Create the pipe for child i */
int pipefd[2];
if (pipe(pipefd)) {
perror("error creating pipe");
return 1;
}
int pipe_read_end = pipefd[0];
int pipe_write_end = pipefd[1];
/* Fork child i */
pid_t child_pid = fork();
if (child_pid < 0) {
perror("error forking");
return 1;
} else if (child_pid == 0) {
printf("Child %d was forked\n", getpid());
close(pipe_write_end);
return child_main(pipe_read_end);
} else {
printf("Parent forked child %d\n", child_pid);
close(pipe_read_end);
pipe_write_ends[i] = pipe_write_end;
child_pids[i] = child_pid;
}
}
/* Send pids down the pipes for each child */
for (int i = 0; i < NUM_CHILDREN; i++) {
unsigned int bytes_written = 0;
while (bytes_written < sizeof(child_pids)) {
ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
if (result < 0) {
perror("error writing to pipe");
return 1;
} else {
bytes_written += result;
}
}
close(pipe_write_ends[i]);
}
/* Wait for children to exit */
for (int i = 0; i < NUM_CHILDREN; i++) {
if (waitpid(child_pids[i], 0, 0) < 0) {
perror("error waiting for child");
return 1;
}
}
}
As #PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.
However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.
You can use shared memory or some other kind of IPC to communicate the PIDs, but you probably shouldn't even try.
PIDs are subject to recycling and you can only ever know for sure if a PID refers to the process you think it refers to if that PID belongs to a child process of yours (because then you can know if you've waited on it or not).
Otherwise, PIDs (of non-children) are racy references which are basically only usable for hacky debugging.
In following code, I want child c1 blocks (via a pipe) until child c2 done (via call pipe_close() in c2). However it does not work. c1 would block forever. It works if I move pipe_close() out of c2 and put it in parent.
Is this because pipes created from parent are not shareable between sibling children processes?
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
static void pipe_close(int *);
static void pipe_read(int *);
int
main(void)
{
int pp_main[2] = {-1, -1};
pipe(pp_main);
int ppid = getpid();
int pgid = getpgrp();
int pid_c1 = fork();
if (pid_c1 == 0)
{ // in child c1
int cpid_c1 = getpid();
int gid = getpgrp();
printf("[c1][%d][%d] is made, wait pipe ready\n", cpid_c1, gid);
pipe_read(pp_main);
printf("[c1][%d][%d] done\n", cpid_c1, gid);
}
else
{ // in parent
printf("[main][%d][%d] created child c1: %d\n", ppid, pgid, pid_c1);
int pid_c2 = fork();
if (pid_c2 == 0)
{ // in child c2
int cpid_c2 = getpid();
int gid = getpgrp();
printf("[c2][%d][%d] is made\n", cpid_c2, gid);
for (int i = 2; i > 0; --i)
{
printf("[c2][%d][%d] count down %d\n", cpid_c2, gid, i);
sleep(1);
}
pipe_close(pp_main);
printf("[c2][%d][%d] done\n", cpid_c2, gid);
}
else
{ // in parent
printf("[main][%d][%d] created child c2: %d\n", ppid, pgid, pid_c2);
}
int status;
waitpid(pid_c2, &status, 0);
}
int status;
waitpid(pid_c1, &status, 0);
return 0;
}
static void
pipe_read(int *pp)
{
char ch;
if (pp[1] >= 0)
{
close (pp[1]);
pp[1] = -1;
}
if (pp[0] >= 0)
{
while (read (pp[0], &ch, 1) == -1 && errno == EINTR);
}
}
static void
pipe_close(int *pp)
{
if (pp[0] >= 0)
close (pp[0]);
if (pp[1] >= 0)
close (pp[1]);
pp[0] = pp[1] = -1;
}
It works if I move pipe_close() out of c2 and put it in parent.
That is expected behaviour. A pipe is not fully closed until all references to it are closed. In your code the pipe is created before the fork. Hence both the parent and the child processes have that pipe opened. The c2 child will exit so that closes its references to the pipe. However, the parent process blocks waiting for c1 to exit. Which will never happen because the parent process does not explicitly or implicitly (via exit) close the pipe.
I am writing a program that takes a list of UNIX commands from a file and executes them sequentially. To keep everything in order, I must have each command initialized and kept waiting for SIGUSR1 via sigwait(). When every command is initialized, then every command can execute.
Usage: > program.c input.txt
However, it appears that SIGUSR1 is repeatedly called, completely surpassing sigwait(). What is going on here? I've tried so many different things, but it's recently modeled after this answer. To rephrase, I want the signal to be raised for commands immediately after initialization. I want the signal to be unblocked when all commands are completely initialized
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void on_sigusr1(int sig)
{
// Note: Normally, it's not safe to call almost all library functions in a
// signal handler, since the signal may have been received in a middle of a
// call to that function.
printf("SIGUSR1 received!\n");
}
int main(int arc, char* argv[])
{
FILE *file;
file = fopen(argv[1] ,"r");
int BUF_SIZE = 100;
char *token;
char buffer[BUF_SIZE];
char programs[BUF_SIZE];
char *commands[BUF_SIZE];
int i = 0;
int counter = 1;
while (fgets(buffer, sizeof buffer, file ) != NULL)
{
strcpy(programs, buffer);
int length = strlen(buffer)-1;
if (buffer[length] == '\n')
{
buffer[length] = '\0';
}
i = 0;
token = strtok(buffer," ");
while(token != NULL)
{
commands[i++] = token;
token = strtok(NULL, " ");
}
commands[i] = 0;
pid_t pids[counter];
// Set a signal handler for SIGUSR1
signal(SIGUSR1, &on_sigusr1);
// At program startup, SIGUSR1 is neither blocked nor pending, so raising it
// will call the signal handler
raise(SIGUSR1);
// Now let's block SIGUSR1
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
// SIGUSR1 is now blocked, raising it will not call the signal handler
printf("About to raise SIGUSR1\n");
raise(SIGUSR1);
printf("After raising SIGUSR1\n");
for(i = 0; i < counter; ++i)
{
pids[i] = fork();
if(pids[i] > 0)
{
printf("Child process %d ready to execute command %s", getpid(), programs);
// SIGUSR1 is now blocked and pending -- this call to sigwait will return
// immediately
int sig;
int result = sigwait(&sigset, &sig);
if(result == 0) {
printf("Child process %d executing command %s", getpid(), programs);
execvp(commands[0], commands);
}
}
}
// All programs have been launched
for(i = 0; i < counter; ++i)
{
wait(&pids[i]);
}
// All programs are waiting to execute
for (i = 0; i < counter; ++i)
{
// SIGUSR1 is now no longer pending (but still blocked). Raise it again and
// unblock it
raise(SIGUSR1);
printf("About to unblock SIGUSR1\n");
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
printf("Unblocked SIGUSR1\n");
}
}
exit(0);
fclose(file);
return 0;
}
UPDATE: Tried changing signal() to sigaction(). No change.
You should consider calling sigwait after checking to see if that pid is a child process.
So maybe put
int sig;
and
int result = sigwait(&sigset, &sig);
within an if statement that checks if the pid is == 0 which would indicate that it is a child. Otherwise you would be sigwaiting a parent process.
If the pid is greater than 0 it's a parent process id and if it's less than zero its an error.
Then for each process in your array of pids you would call kill(pid_array[i], SIGUSR1) to unblock it.