使用子进程处理SIGSTP,SIGCONT,SIGINT信号

时间:2017-11-25 18:14:09

标签: c linux shell signals

我目前正在C中编写一个简单的shell,而且我面临信号问题。

例如,当我启动程序时,我输入ping命令然后按CTRL-Z我希望子进程(ping命令)暂停,然后在我使用fg时返回。

我认为我需要将执行ping命令的子pid存储为全局变量。

我已经检查过其他帖子以解决我自己的问题,但我无法解决问题。

这是执行命令的代码(带有|的多个命令)以及我存储子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;

这是我的处理程序c文件。

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);
 }`

当我打印pid时,我得到了正确的PID。

最后,我调用我的处理程序管理器。

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;

提前谢谢你,对不起我的英语。

2 个答案:

答案 0 :(得分:3)

正确的作业控制信号处理可以使您的项目超出我的特点,简单的"简单的"贝壳。 GLIBC手册有a whole multi-part section来实现作业控制shell,听起来很多它都适用于你的项目。

您似乎无视的一个关键方面是管理流程组以及哪些流程控制终端。现在你正在做的事情,你的shell子进程将属于与shell本身相同的进程组,当信号(例如SIGSTP)出现时,这将会出现问题。发送给孩子的整个过程组。

为避免此类问题,您的shell应使新的子进程成为其自己进程组的进程组负责人(通过setpgid())。当这些进程组最初位于前台时,你的shell应该使它们终端控制进程组(tcsetpgrp())。

当然,还有更多的东西,但这应该让你朝着正确的方向前进。

答案 1 :(得分:0)

exec_proc文件应该

pid_t f_pid;

作为全局变量(反向两行)

pid_t runner;

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

处理程序C文件需要通过extern

访问在另一个文件中声明的全局变量
extern pid_t f_pid;

这样,两个目标文件共享相同的变量f_pid

编辑 -----

尝试更改处理程序TSTP,并添加以下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);
}

添加警报信号

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

在我的Linux机器上运行。停止发生时

  1. 禁用TSTP处理
  2. 做当前的事情
  3. 杀死主要进程!
  4. 启动警报以设置TSTP处理(将其添加到处理程序似乎会造成一些麻烦......)
  5. 请注意,alarm()需要几秒钟,setitimer()会下降到微秒(理论上)。