【问题标题】:C: writing a bash shell, how to mimic bash shell behavior for SIGINTC:编写 bash shell,如何为 SIGINT 模拟 bash shell 行为
【发布时间】:2020-10-31 23:01:18
【问题描述】:

当接收到 SIGINT 信号时,bash shell 退出用户当前正在编写的行,并打印出一个新的提示,但它并没有退出。我怎样才能模仿这种行为?我正在编写自己的 shell,我希望 shell 继续在 SIGINT 上运行。我不确定如何正确终止以前的fget 从标准输入读取然后打印出新提示:

我的代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

char *get_args()
{
    char *args = calloc(sizeof(char), 1024);
    fgets(args, 1023, stdin);
    char *pter = strrchr(args, '\n');
    if (pter)
        *pter = 0;
    return args;
}

void sigintHandler(int sig_num)
{
    // Reset handler to catch SIGINT next time.
    signal(SIGINT, sigintHandler);
    printf("Received ctrl c\n");
}

int main(int argc, char *argv[])
{
    char host_name[64]; // hostname
    gethostname(host_name, 64);
    signal(SIGINT, sigintHandler);
    while (1)
    {
        if (isatty(STDIN_FILENO))
        {
            printf("[%s]$ ", host_name);
            char *line = get_args();
            printf("\nline: %s\n", line);
        }
    }
    return 0;
};

如何在 SIGINT 之后终止 fgets 的运行,以便再次打印提示符(主机名)?

【问题讨论】:

  • 您不能从信号处理程序调用 printf。你真的不应该。
  • 这是一个调试语句,以确保我收到了 SIGINT,但我不确定如何从读取中终止前一个 fget 并重新开始循环以便打印提示。
  • 可能导致错误的调试语句实际上是错误语句。君斯特不要这样做。即使它最初似乎有效。它会咬回来。
  • 有两种方法:1)设置(全局)标志 2)siglongjump。阅读史蒂文斯了解详情。可能还有第三种方式,我不确定。
  • 会使用 siglongjump 杀死 fget,因为它仍然会从标准输入读取

标签: c shell signals stdin


【解决方案1】:

用setlongjmp和sigsetjmp回答: 经过一些研究,取消现有的fgets、scanf 等调用在SIGINT 中断后等待从stdin 读取似乎是最简单的方法(最少的代码行)是使用siglongjmp声明。

使用sigsetjmp,你可以声明一个跳转位置(在我的例子中它在main中,这样我的无限while循环可以再次打印提示。然后,在SIGINT处理程序中,你可以调用siglongjmp .

相关代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>

// initialize marker for jump
sigjmp_buf mark;

// handler for SIGINT creates another SIGINT handler
// for the next SIGINT interrupt, and jumps back
// to infinite lopp
void sigintHandler(int sig_num)
{
    // Reset handler to catch SIGINT next time.
    signal(SIGINT, sigintHandler);
    // jump back to main to abort current stdin from fgets
    siglongjmp(mark, 1);
}

int main(int argc, char *argv[])
{
    // signal handler for SIGINT
    signal(SIGINT, sigintHandler);
    // set the marker to this point in main
    sigsetjmp(mark, 1);

    while (1)
    {
        if (isatty(STDIN_FILENO))
        {
            // some code that prints the prompt in the form
            // of username@hostname:cwd$ 
        }
    }
    return 0;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 2013-08-16
    • 2021-10-01
    • 2011-09-07
    相关资源
    最近更新 更多