c中的shell:仅在按键盘上的enter键后响应SIGTSTP

c中的shell:仅在按键盘上的enter键后响应SIGTSTP,c,signals,job-control,C,Signals,Job Control,我想用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 s

我想用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
:

有人能指出我做错了什么吗?任何帮助都将不胜感激。谢谢大家!

您的信号处理程序和设置方式有几个问题

正如我在评论中已经指出的那样,信号处理程序可以安全地访问文件范围变量,例如
全局
,前提是该变量是
易失的
,并且类型为
sig\u atomic\t
。如果它试图访问任何其他变量,则有可能看不到该变量的最新值集,处理程序范围之外的代码将看不到处理程序写入该变量的值(如果有),由于信号处理程序对变量的写入是非原子的,因此变量的值将被破坏

注释中还指出了一个更严重的问题,即将
SA_SIGINFO
标记指定为
sigaction()
,告诉它您已经通过
struct sigaction
SA_sigaction
成员指定了处理程序,而实际上,您已经通过
sa_处理程序
成员指定了它如果通过
sa_handler
指定处理程序,则必须从标志中省略
sa_sigino
,反之亦然。如果不适当地指定了
SA_SIGINFO
标志,则接收到该信号将导致未定义的行为,或者使用错误的参数调用处理程序,或者尝试通过垃圾函数指针调用函数

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

但是影响你欲望的主要问题是

“:”按ctrl-z键后,应在下一行自动显示own

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


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

当外壳接收到
SIGSTP

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

首先,如果要从信号处理程序安全地访问非常量文件作用域变量,如
global
,它必须是
易失性的
,并且具有类型
sig\u atomic\t
。但是,我怀疑您没有这样做并不是问题的直接原因。我相应地修改了
global
,但您是对的,它没有解决问题。此外,您不应该将
SA_sigino
标志指定为
sigaction()
,因为您使用的是
struct sigaction
sa_处理程序
成员,而不是
sa_sigaction
成员。这是一个严重的问题,但我敢打赌,删除该标志不会改变观察到的行为。我无法根据您提供的代码复制您描述的行为。我假定您的程序在键入^Z时正在执行某种I/O操作,例如尝试读取命令。这可能与观察到的行为有关,但也有一种情况,即shell应该静默地使用信号,因为它本身就是前台进程;bash是一个非常特殊的解释器,它是POSIX sh规范的一个大型超集,而像这样的玩具外壳是很小的子集——它们几乎没有任何共同之处。关于这一点,shell标签的wiki明确表示,它的重点是用POSIX兼容语言寻找答案的问题,而不是关于实现类似于shell的程序的问题。感谢您的详细解释。我对最后一部分有点困惑。在我的shell中,在添加
^Z
处理程序之前,每当我输入
^Z
时,我的shell输出
[1]+停止
。我想让它做的是,如原文章中所述,进入前台模式,同时显示“进入/退出前台模式”。在修复了sau重启标志之后,它仍然没有这样做,所以我假设t