0

我正在尝试用c编写一个shell。它的一部分是一个处理程序,用于捕获 SIGTSTP 信号并将其设置为使程序进入和退出仅前台模式。

以下是相关的代码片段:

//global variables
int global;

//header
void catch_tstp(int);

//main function
int main(int argc, char** argv){
    ...

    // initiate sigaction struct for CTRL-Z action
    struct sigaction ctrlz_act;
    ctrlz_act.sa_handler = catch_tstp;
    ctrlz_act.sa_flags = SA_SIGINFO|SA_RESTART;
    sigfillset(&(ctrlz_act.sa_mask));
    sigaction(SIGTSTP, &ctrlz_act, NULL);
    global = 0;

    ...
}

//handler
void catch_tstp(int sig){
    if(sig == SIGTSTP){
        if(global){
            global=0;
            printf("Entering foreground-only mode (& is now ignored)\n");
        }
        else{
            global=1;
            printf("Exiting foreground-only mode\n");
        }
    }
}

现在我的输出看起来像这样:

: ^ZExiting foreground-only mode             //pressed ctrl+z
             //nothing here, had to press enter again
:            //pressing enter just gives me another :, which is what I want
: ^ZEntering foreground-only mode (& is now ignored)
^ZExiting foreground-only mode

我希望输出看起来像这样:

: ^Z
Entering foreground-only mode (& is now ignored)
:   //":"should show up own automatically on the next line after I press ctrl-z, then enter 
: ^Z
Exiting foreground-only mode
:

谁能指出我做错了什么?任何帮助将不胜感激。谢谢!

4

1 回答 1

2

您的信号处理程序以及如何设置它有几个问题。

正如我在评论中已经指出的那样,信号处理程序可以安全地访问文件范围变量,例如global仅当该变量是volatile并且具有 typesig_atomic_t时。如果它尝试访问任何其他变量,则存在看不到为该变量设置的最新值的风险,处理程序范围之外的代码将看不到处理程序写入该变量的值(如果有)变量,并且由于信号处理程序对它的写入是非原子的,因此变量的值将被破坏。

一个更严重的问题,也已经在评论中指出,是指定SA_SIGINFO标志为sigaction(),就像你所做的那样,告诉它你已经通过你的成员指定了处理程序sa_sigactionstruct sigaction而你实际上是通过sa_handler成员指定了它。 如果您通过sa_handlerthen 指定处理程序,则必须SA_SIGINFO从 flags中省略,反之亦然。如果您SA_SIGINFO不恰当地指定标志,则接收信号将导致未定义的行为,通过使用错误数量的参数调用您的处理程序或尝试通过垃圾函数指针调用函数。

此外,信号处理程序调用的所有函数都必须是异步信号安全的。手册第 7 章中的“信号”条目包含可安全调用的函数列表,但printf()不在其中。

但主要问题干扰了你的愿望

“:”应该在我按下 ctrl-z 后自动显示在下一行

似乎您已包含SA_RESTART在 sigaction flags中。该标志会导致(某些)系统函数因接收到信号而被中断,在信号处理程序完成后自动重新启动,而不是EINTR向其调用者返回错误或部分结果。在您的特定情况下,如果您的 shell 在读取命令时收到了一段SIGSTP时间,则重新启动读取意味着控制不会返回到 shell 以打印新的提示。 这也可能意味着在 ^Z 之前输入的字符会丢失,具体取决于您如何阅读命令的详细信息。


此外,从您程序的消息传递来看,您似乎对 ^Z 的含义和使用以及SIGSTP终端接口和标准 shell 的含义有误解。它们为 shell 执行的作业提供作业控制功能;它们主要不是关于 shell 本身的模式。在接收到 ^Z 后(在信号处理程序本身的范围之外),shell 无需维护标志或采取不同的行为——所需的交互行为主要源于 shell 可以从标准输入中读取的事实只有当它在前台时。

当 shell 收到一个SIGSTP,

  • 如果它在前台,它应该什么都不做(并避免丢失未处理的输入)。
  • 如果它在后台,它应该SIGSTOP向前台进程组发送一个(注意区别),并在必要时将自己放回前台。在这种情况下,它可能会发出消息和/或提示。
于 2017-02-14T20:56:56.430 回答