Handling signals SIGSTP, SIGCONT, SIGINT with chil

2019-08-23 06:17发布

I'm currently writing a simple shell in C and i'm facing issues with signals.

For example when I launch my program, I type ping command then CTRL-Z I want that the child process (the ping command) to be paused and then to come back when I use fg.

I think that I need to store as a global variable the child pid of the one that execute the ping command.

I already checked other posts to resolve the problem by my own but I can't get it working.

This is the code to execute a command (multiple commands with |) and where I store the child pid.

int exec_proc(int input, int output, char** command) {

pid_t runner;

runner = fork();
f_pid = runner;

if (runner == 0) {
    // Use input for stdin
    if (input != 0) {
        dup2(input, 0);
        close(input);
    }

    // Use output for stdout
    if (output != 1) {
        dup2(output, 1);
        close(output);
    }
    // Return command code
    execvp(command[0], command);
}

// An error occured
return -1;

This is my handler c file.

pid_t f_pid;
/**
* Handles every handler !
*/
void handlerManager()
{
    signal(SIGINT,INTHandler);
    signal(SIGTSTP,TSTPHandler);
    signal(SIGCONT,CONTHandler);
}

/**
 * Handler for CTRL-C
 * @param sig
*/
void INTHandler(int sig)
{
    printf("\nDo you really want to quit ? [y/n] \n");
    int answer = getchar();

    if(toupper(answer) == 'Y')
        kill(f_pid,SIGINT);
}

/**
 * Handler for CTRL-Z (processus sleep)
 * @param sig
*/
void TSTPHandler(int sig)
{
    printf("\nGoing to sleep! \n");
    printf("%d", f_pid);
    kill(f_pid,SIGTSTP);
}

/**
   * Handler to reset a processus
   * @param sig
*/
 void CONTHandler(int sig)
 {
     printf("\nHey i'm awake\n");
     kill(f_pid,SIGCONT);
 }`

When I print the pid I am getting the right PID.

Finally this where I call my handler Manager.

int main() {


char* line;
char** args[MAX_ARG_SIZE] = {NULL};
int status;

handlerManager();

do {

    fflush(stdin);

    prompt();

    line = readline();

    char linecpy[strlen(line)];
    strcpy(linecpy, line);

    splitBy(line, " ", args);

    status = exec(args, linecpy);

    switch (status) {
        case EMPTY_LINE:
            break;
    }

} while (status);


return 0;

Thank you in advance and sorry for my english.

2条回答
SAY GOODBYE
2楼-- · 2019-08-23 06:52

The exec_proc file should have

pid_t f_pid;

as a global variable (reverse the two lines)

pid_t runner;

int exec_proc(int input, int output, char** command) {

The handler C file needs to access the global variable declared in the other file via extern

extern pid_t f_pid;

This way, both object files share the same variable f_pid.

edit -----

Try to change the handler TSTP, and add the following ALRM

void TSTPHandler(int sig)
{
    signal(SIGTSTP,SIG_DFL);      // <+++++
    printf("\nGoing to sleep! \n");
    printf("%d", f_pid);
    kill(f_pid,SIGTSTP);
    kill(getpid(),SIGTSTP);       // <+++++
    alarm(1);                     // <+++++
}
void ALRMHandler(int sig)         // <+++++
{
    signal(SIGTSTP,TSTPHandler);
}

Add Alarm signal

void handlerManager()
{
    signal(SIGINT,INTHandler);
    signal(SIGTSTP,TSTPHandler);
    signal(SIGCONT,CONTHandler);
    signal(SIGALRM,ALRMHandler);  // <+++++
}

Works on my Linux box. When a stop occurs

  1. disable TSTP handling
  2. do the current stuff
  3. kill the main process!
  4. start an alarm to set the TSTP handling back (adding that to the handler seems to create some trouble...)

Note that alarm() takes seconds, setitimer() goes down to the microsecond (in theory).

查看更多
Anthone
3楼-- · 2019-08-23 07:03

Doing proper job-control signal handling lifts your project a bit beyond what I would characterize as a "simple" shell. The GLIBC manual has a whole multi-part section on implementing a job-control shell, and it sounds like much of it would apply to your project.

A key aspect that you seem to be disregarding is managing process groups and which of those has control of the terminal. The way you're doing things now, your shell's child processes will belong to the same process group as the shell itself, and that will present a problem when a signal, such as SIGSTP, is sent to the child's whole process group.

To avoid such problems, your shell should make new child processes be process-group leaders of their own process groups (via setpgid()). When such process groups are meant to initially be in the foreground, your shell should make them terminal's controlling process group (tcsetpgrp()).

There's more to it than that, of course, but that should get you going in the right direction.

查看更多
登录 后发表回答