Moving straight to the issue, how do I make Ctrl+z do what the title states?
My program implements a parent process which creates a single child process.
Both processes will display the process ID and once the child terminates a signal is sent to the parent process and the parent signal handler will display a text stating a signal has been captured.
On the child process, on top of displaying the child's process ID, it must generate a random number between 10 and 50 every time Ctrl + z is pressed. So far I can only make the child process generate 1 random number.
Below is my code:
void main() {
int pid;
int x;
int fd[2];
const int MAXLINE=4096;
char line[MAXLINE];
pid=fork();
if (pipe(fd) < 0) {
printf("Pipe error!");
}
if (pid < 0) {
printf("Fork error!");
} else if (pid == 0) { //Child process
signal(SIGTSTP, childsignal_handler);
printf("The process id is: %i\n", getpid());
sleep(1000); //Implemented to wait for a signal
} else {
printf("The process id is: %i\n", getpid()); //Parent process
pause(); //Waits for the Child process to finish
}
}
parent signal handler:
void parentsignal_handler(int signo) { //Signal Handler for the parent process
printf("The signal in the parent process has been captured\n");
}
child signal handler:
void childsignal_handler(int signo) { //Signal Handler for the child process
signal(SIGTSTP, childsignal_handler);
printf("\nThe signal in the child process has been captured\n");
randomnumbergenerator();
pause();
}
The random number generator:
void randomnumbergenerator() { //Random number generator to run everytime Ctrl+z is pressed
//signal(SIGTSTP, childsignal_handler);
int number;
int number2 = 10;
printf("Welcome to random number generator!");
printf("\nRandom number generated = %d\n", rand() % 40 + 10);
}
PS: I have read several documentations regarding various solutions such as sigsuspend, sigprocmask,pause and so on but none of them worked so far.
below are some of the documentations i have read so far:
https://www.gnu.org/software/libc/manual/html_node/Waiting-for-a-Signal.html#Waiting-for-a-Signal
https://man7.org/linux/man-pages/man2/pause.2.html
i need to implement a signal that will detect the child terminating
You don't exactly need to implement a signal; you just need to install the handler that you already have:
signal(SIGCLD, parentsignal_handler);
how do I make Ctrl+z do what the title states?
So far I can only make the child process generate 1 random number.
To prevent the parent process from being suspended (and therewith losing signal delivery), ignore the STOP signal in the parent:
signal(SIGTSTP, SIG_IGN);
And you should really pay attention to Ian Abbott's first comment and remove pause() from the signal handler; otherwise after Ctrl-Z was pressed the program will not end. At the same time change sleep(…) to while (sleep(…)) to keep a timed suspension instead of the unlimited pause().
Related
I'm writing myshell program.
it should supportpipe processing. It should also recognize the '&' at the end of the command and run the child process in the background if the '&' is attached.
The problem with my program is,
ls | grep txt works well, but ls | greptxt & is not working.
To this end, I declared globally the pid array that stores the IDs of the generated child processes and the reaped_id array that stores the IDs of the reaped child processes.
The second process are blocked(sigsuspended) if the first child process had not been reapped. Also, when the first process ends, sigchld handler sends a SIGCONT signal to the second process if the second process has been created that moment.
Debugging results in Sigchld handler: it does not send SIGCONT signals to the second process even after the second process has been blocked
More specifically, the incremental pcount (number of child processes created) in the parent process is not reflected in the Sigchld handler.(globally defined) Here's a part of my shell.
while(buffer[idx] != '\0'){
/*check if it is piped process*/
pipe_start_flag = pipe_end_flag; // if last process was piping process, then this process is a piped process
parse_line(&buffer[idx], argv, &idx, &pipe_end_flag); // parse until it meets '\0' or '|'(assign its (index-1) on idx)
pid[pcount] = Fork();
if(pid[pcount] == 0){
int big_bro_idx = pcount - 1; // index of last process
if(!pipe_start_flag && pipe_end_flag){
dup2(fd[WRITEEND],STDOUT_FILENO);
close(fd[READEND]);
}
else if(pipe_start_flag && !pipe_end_flag){
dup2(fd[READEND], STDIN_FILENO);
close(fd[WRITEEND]);
write(STDOUT_FILENO, "second process spawned\n", 23); // debugger
while(reaped_id[big_bro_idx] == 0){ // while last process are executing. If last process terminate, this will be its pid
write(STDOUT_FILENO, "this line works\n", 15); // debugger code
Sigsuspend(&blocking_mask);
}
}
if(execvp(argv[0], argv) < 0){
printf("%s: command not found\n", argv[0]);
exit(1);
}
}
else{ // parent process"
close(fd[WRITEEND]);
if(!bg){
while(reaped_id[pcount] <= 0 && sigtstp_flag == false){
Sigsuspend(&blocking_mask); // wait for sigchld
}
sigtstp_flag = false; // reset flag to false(it changed at sigtstp_flag)
}
else; // background process
write(STDOUT_FILENO, "pcount increased\n", 17); // debugging code
++pcount;
}
++idx;
}
Next is my SIGCHLD handler.
void sigchld_handler(int sig){
sigset_t mask, prev;
int status;
int olderrno = errno;
Sigfillset(&mask);
Sigprocmask(SIG_BLOCK, &mask, &prev);
while((reaped_id[reap_count] = waitpid(-1, &status, WNOHANG)) > 0)
{
write(STDOUT_FILENO, "process terminated\n", 19);
reap_count ++;
}
printf("%d %d %d %d ", pid[reap_count], reap_count, pcount, reaped_id[reap_count -1]);
if(pid[reap_count] > 0 || WIFSTOPPED(status)){ // if there is little brother process
Kill(pid[reap_count], SIGCONT); // send SIGCONT singal to blocked sibling process
write(STDOUT_FILENO, "I alarmed my sibling", 20); // debuggging code; not executed
}
Sigprocmask(SIG_SETMASK, &prev, NULL);
errno = olderrno;
}
process graph:
Shell spawn both child processes
The second process is blocked because the first process is not finished
First process terminated
Sigcont signal should be sent from Sigchld handler to second process but not.
(It shows that the pcount is increased, but when I print a pcount in sigchld handler, it doesn't increase at all.)
I've been up all night for this problem. If you need a full code to execute, we will send it to you.
If there is anything I need to supplement, please tell me and I will supplement it right away.
I'm trying to create a program where a process forks, creating a child process, and the parent must always finish printing to the screen before the child is finished printing to the screen, no matter what. I also wish to accomplish this using signals instead of pipelining.
It is a similar problem to the question asked here: Explanation of sigsuspend needed
I understand that kill(pid,signal); is to send a signal to that pid and tell it to finish executing and terminate.
The problem is, when it executes, the child doesn't print after the suspend. Heres the code:
int main(void){
pid_t pid;
int i;
pid = fork();
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
exit(0);
}
}
I have managed to accomplish my task (printing the parent before the child) by using a global variable int x = 0; and a signal handler method void handle1(int s){x = 1;}before the main. In the main I added signal(SIGUSR1,handle1); In the child I removed all the sigset and sigsuspend lines and instead wrote while(x==0){/*do_nothing*/} 1 line before the printf statement. So when the parent executes kill(pid,SIGUSR1) the signal handler which is inherited by the child process also gets executed and sets x=1. So the child now leaves the while loop and can print it's statement.
However I believe it would be helpful to know how to accomplish this task using sigmask_t and sigsuspend() but i cant get it to work that way.
There are 3 problems in your code:
SIGUSR1 is the signal you want to deliver to the child. You can't use sigaddset(&mask,SIGUSR1);, it does exactly the opposite of your intention.
According to POSIX standard sigsuspend() below, you should install a signal handler for SIGUSR1 to make sigsuspend() continue the following code, since the default behavior of SIGUSR1 is termination.
The sigsuspend() function shall replace the current signal mask of the calling thread with the set of signals pointed to by sigmask and then suspend the thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process.
It would be better if you collect the child from the parent, otherwise there is a race condition.
The code below will work:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void handler(int sig) {}
int main(void){
pid_t pid;
int i;
pid = fork();
signal(SIGUSR1, handler);
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
wait(NULL);
exit(0);
}
}
You have a few issues.
Your parent process should wait for the child to complete. This allows for diagnostics (such as properly waiting for the child to print), but is otherwise a bookkeeping task that is a good habit even when the waiting process will just exit:
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
exit(0);
}
waitpid(pid, NULL, 0);
exit(0);
Now, you have set SIGUSR1 in your mask to sigsuspend(), which causes the signal to be ignored. This is now more obvious once the parent is made to wait, because the parent will never exit. So, remove the line of code that sets SIGUSR1.
Finally, the default handler for SIGUSR1 will simply cause the process to exit, and so the printf will not get a chance to print. If you want it to print, you should add a signal handler for SIGUSR1. It doesn't have to do anything.
void h (int s) {}
...
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
struct sigaction sa = { .sa_handler = h };
sigaction(SIGUSR1, &sa, NULL);
sigsuspend(&mask);
printf("The child is now complete \n");
I want to write a C program that asks the user for an integer and stores it in a variable n. Then, the main process creates two
child processes (both must be children of the main process). One child exits successfully if n>10 and unsuccessfully
otherwise, whereas the other child exits successfully if n>20 and unsuccessfully otherwise. The main process must print
how many of its children ended successfully.
This is what I have so far.
#include <stdio.h>
void main (void)
{
int n;
printf("Give me a number: ");
scanf("%d", &n);
pid_t child_a, child_b;
child_a = fork();
if (child_a == 0) {
if (n>10) {
exit(1)
}else{
exit(0)
}
} else {
child_b = fork();
if (child_b == 0) {
if (n>20){
exit(1)
}else{
exit(0)
}
} else {
/* Parent Code */
}
}
}
But how do I count how many child processes end successfully?
You need to use wait() command, system call wait() function blocks the calling process until one of its child processes exits or a signal is received.
int status;
pid_t pid = wait(&status);
printf("Exit = %d, child = %d\n", WEXITSTATUS(status), pid);
see example at:
fork() and wait() with two child processes
and 2nd example using wait() and WEXITSTATUS()
Linux fork() and wait()
Also more about what happen with multiple cores scheduling of processes:
fork() and wait() calls
I'm trying to learn how to handle signals. In my program I have an array of pids of earlier created subprocesess. No I want to every couple seconds send a sigtstp signal to one of them. He just have to send sigchld to parent process and exit. Parent process should print an exit code of exited process and create next one in the place of exit one. Everything works fine in first loop but it hangs in second. So on output get:
loop
slept
forking
in to array
loop
Zakonczyl sie potomek 3934 z kodem 0.
So it's seems that sleep works in first loop but not in second. Or just main process didn't get back control after handling signal but this should't happen. So I have no idea whats may be wrong here.
while(1) {
printf("loop\n");
sleep(5);
printf("slept\n");
int r = rand() % n;
if(kill(process_tab[r],SIGTSTP) < 0) {
printf("Error while sending sigtstp signal.\n");
} else {
printf("forking\n");
if((child = fork()) < 0) {
printf("Fork failed.\n");
} else if(child == 0) {//to sie dzieje w procesie
if(signal(SIGTSTP,&catch_sigtstp)) {
printf("Error while setting signal handler.\n");
_exit(EXIT_FAILURE);
}
while(1) {
}
} else { //to sie dzieje w parencie
process_tab[r] = child;
printf("in to array\n");
}
}
}
And here are handlers.
void catch_sigtstp(int signal) {
kill(ppid,SIGCHLD);
_exit(EXIT_SUCCESS);
}
void catch_sigchld (int signal) {
int status;
pid_t child = wait(&status);
printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
Add fflush after printf.
printf("Something\n");
fflush(stdout);
Otherwise you may not get the output as stdio is buffered by default.
Edit: Issues of handler
It is pretty unsafe to use printf function in signal handler, as it is not reentrant. Also, the catch_sigchild function can be modified:
void catch_sigchld (int signal) {
int status;
pid_t child;
while ((child = waitpid(-1, &status, WNOHANG)) > 0)
{
// may be something else?
// ...printf("Zakonczyl sie potomek %d z kodem %d.\n",child,status);
}
}
The reason is that one signal can be delivered for multiple dead children.
Edit: blocking signal when printing.
To avoid deadlock inside stdio, you should block the signal:
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHILD);
...
sigprocmask(SIG_BLOCK, &set, NULL);
printf("my output");
sigprocmask(SIG_UNBLOCK, &set, NULL);
...
Edit: as #Barmar has pointed, you parent process will receive SIGCHILD signal twice: once from your child'd signal handler, and one from OS.
To fix, it might be sufficient to remove your own signal source:
void catch_sigtstp(int signal) {
// kill(ppid,SIGCHLD); //< This one causes two signals per one child
_exit(EXIT_SUCCESS);
}
What I want to do is create a parent process that lasts for 5 seconds. I also want it to send a SIGUSR1 signal every second. On this signal I want the child to do something.
The code that I put together so far is:
void alarmHandler(int sig) {
printf("\nalarm called\n");
exit(0);
}
void childHandler(int sig) {
printf("child called");
signal(SIGUSR1, childHandler);
}
int main() {
pid_t val;
if((val = fork())) { //parinte
signal(SIGALRM, alarmHandler);
printf("parent");
alarm(5);
while(1) {
kill(val, SIGUSR1);
sleep(1);
}
}else {
signal(SIGUSR1, childHandler);
printf("child");
}
return 0;
}
What I get is:
child
parent
alarm called
What I want:
child
parent
child called
child called
child called
child called
child called
alarm called
Your parent has the while loop. The child does the following:
signal(SIGUSR1, childHandler);
printf("child");
And then exits.
If it does receive SIGUSR before the exit, this will also be executed
printf("child called");
signal(SIGUSR1, childHandler);
Therefore you have a race condition as the number of child called is printed.
Just put a while (1) {} after the printf("child");
Base on the original code, add two blocks:
flush the printf: setbuf(stdout, NULL);
keep the child running: while (1) pause();
The code list as follow:
#include "stdio.h"
#include "stdlib.h"
#include <signal.h>
/* For a real-world program, printing from a signal handler is not very safe.
* A signal handler should do as little as it can, preferably only setting a flag here or there.
* And the flag should be declared `volatile`.
* Real-world example:
* I once worked on a system that used an Access database as a back end,
* and under certain circumstances a printf() call in a signal handler would write to the .mdb file instead of stdout,
* hosing the database beyond repair.
*/
void alarmHandler(int sig)
{
printf("\nparent signal alarm handler: times up\n");
exit(0);
}
void childHandler(int sig)
{
printf("\nchild signal handler\n");
// The old style should install the handler again.
signal(SIGUSR1, childHandler);
}
int main()
{
pid_t val;
signal(SIGALRM, alarmHandler);
// If not set this, we cann't the child's output.
// The stdout stream is buffered, so will only display what's in the buffer after it reaches a newline (or when it's told to).
setbuf(stdout, NULL);
if ((val = fork())) { //parinte
printf("\nparent\n");
// #Note that there is only one alarm clock per process.
// the alarm() function will return a value if another alarm has been previously set.
//
// sets a timer that generates the SIGALARM signal when times up.
// If we ignore or don’t catch this signal, the process is terminated.
alarm(5);
while(1) {
sleep(1);
kill(val, SIGUSR1);
}
printf("\nparent exit\n");
} else {
signal(SIGUSR1, childHandler);
printf("\nchild\n");
while (1)
pause(); // The pause() function suspends the process until a signal is caught.
printf("\nchild exit\n");
}
return 0;
}
And the output:
parent
child
child signal handler
child signal handler
child signal handler
child signal handler
parent signal alarm handler: times up