【问题标题】:Handling signals SIGSTP, SIGCONT, SIGINT with child process处理带有子进程的信号 SIGSTP、SIGCONT、SIGINT
【发布时间】:2018-05-09 09:42:41
【问题描述】:

我目前正在用 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。

最后在这里我调用了我的处理程序 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;

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

【问题讨论】:

    标签: c linux shell signals


    【解决方案1】:

    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 处理(将其添加到处理程序似乎会造成一些麻烦...)

    请注意,alarm() 需要几秒钟,setitimer() 需要微秒(理论上)。

    【讨论】:

    • 嗨,谢谢你的回答我做了改变,但是当我在 ping 命令上按 Ctrl-Z 时,它确实改变了进程的状态,但是当我输入 fg 时,我在子进程上被阻止了(ping 命令)。
    【解决方案2】:

    进行适当的作业控制信号处理会使您的项目稍微超出我所描述的“简单”外壳。 GLIBC 手册中有 a whole multi-part section 介绍了实现作业控制 shell,听起来其中大部分内容都适用于您的项目。

    您似乎忽略的一个关键方面是管理进程组以及哪些进程组可以控制终端。你现在做事的方式,你的 shell 的子进程将与 shell 本身属于同一个进程组,当一个信号,比如SIGSTP,被发送到孩子的整个进程组时,就会出现问题。

    为避免此类问题,您的 shell 应该让新的子进程成为它们自己进程组的进程组领导者(通过setpgid())。当此类进程组最初应位于前台时,您的 shell 应使它们成为终端的控制进程组 (tcsetpgrp())。

    当然不止这些,但这应该会让你朝着正确的方向前进。

    【讨论】:

      猜你喜欢
      • 2013-12-21
      • 2022-06-15
      • 1970-01-01
      • 2012-10-08
      • 1970-01-01
      • 1970-01-01
      • 2015-05-15
      • 1970-01-01
      • 2012-09-23
      相关资源
      最近更新 更多