简体   繁体   English

为 SIGTSTP 信号配置处理程序以显示消息但不终止当前进程

[英]configuring handler for SIGTSTP signal to display a message but not terminate the current process

I have this very basic shell program that just keeps prompting for an input until user presses control+c.我有这个非常基本的 shell 程序,它一直提示输入,直到用户按下 control+c。

I am trying to configure the SIGTSTP signal's handler function to display a message and not terminate the program.我正在尝试配置 SIGTSTP 信号的处理程序 function 以显示消息而不终止程序。 (SIGTSTP is triggered by control+z and by default terminates the program and all child processes.) (SIGTSTP 由 control+z 触发,默认终止程序和所有子进程。)

The problem: As soon as I press control+z, which triggers the SIGTSTP signal, the program crashes.问题:一旦我按下触发 SIGTSTP 信号的 control+z,程序就会崩溃。

Below is my code which includes my basic shell program and my attempt to define the custom handler function for SIGTSTP.下面是我的代码,其中包括我的基本 shell 程序和我尝试为 SIGTSTP 定义自定义处理程序 function。

Thank you for any help and suggestions!感谢您的任何帮助和建议!

#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/types.h>
#include <signal.h>

void handle_SIGTSTP(int signo){
   char* message = "\ncontrol + z pressed\n";
   write(STDOUT_FILENO, message, 21);
   fflush(stdout);
};

int main() {
   struct sigaction SIGTSTP_custom = {0};
   SIGTSTP_custom.sa_handler = handle_SIGTSTP;
   sigaction(SIGTSTP, &SIGTSTP_custom, NULL);
   while(true) {
      char *user_input = malloc(200);
      memset(user_input, '\0', 200);
      size_t max_input_size = 199;
      printf("enter input: ");
      fflush(stdout);
      getline(&user_input, &max_input_size, stdin);
   };
   return 0;
};

You need to pass the SA_RESTART flag to the sigaction structure:您需要将 SA_RESTART 标志传递给 sigaction 结构:

...
   struct sigaction SIGTSTP_custom = {0};
   SIGTSTP_custom.sa_flags |= SA_RESTART; // add this
   SIGTSTP_custom.sa_handler = handle_SIGTSTP;
   sigaction(SIGTSTP, &SIGTSTP_custom, NULL);
...

The behavior of the signal handler will depend on those flags, information on all flags is available on the man page (man sigaction).信号处理程序的行为将取决于这些标志,所有标志的信息都可以在手册页(man sigaction)上找到。

Pablo found your main bug: you need to set the SA_RESTART flag to have interrupted system calls restarted. Pablo 发现了您的主要错误:您需要设置SA_RESTART标志才能重新启动中断的系统调用。

When SIGTSTP arrives, your program is presumably waiting for I/O in getline , with a system call such as read(2) .当 SIGTSTP 到达时,您的程序可能正在等待getline中的 I/O,并使用诸如read(2)之类的系统调用。 Without SA_RESTART , this system call fails when the signal arrives, and sets errno to EINTR .如果没有SA_RESTART ,此系统调用会在信号到达时失败,并将errnoEINTR The failure of read() causes getline to set the error flag on stdin , which makes all successive getline calls fail immediately, and puts you in an infinite loop of printf . read()失败会导致getlinestdin上设置错误标志,这会使所有后续的getline调用立即失败,并将您置于printf的无限循环中。 If you had checked the return value from getline() and done proper error reporting, it would have helped you find this.如果您检查了getline()的返回值并进行了正确的错误报告,它会帮助您找到它。

The infinite loop by itself wouldn't make your program crash, but you have another bug: you allocate memory on every iteration of the loop, and then leak it.无限循环本身不会使您的程序崩溃,但您还有另一个错误:您在循环的每次迭代中分配 memory,然后泄漏它。 You really want to declare and initialize user_input and max_input_size outside your loop.您真的想在循环之外声明和初始化user_inputmax_input_size You also don't need to allocate memory yourself;你也不需要自己分配memory; you can let getline() do it the first time by initializing to NULL and 0. The usual idiom for getline would look like:您可以通过初始化为 NULL 和 0 让getline()第一次执行此操作。 getline的常用习语如下所示:

char *user_input = NULL;
size_t max_input_size = 0;
while (getline(&user_input, &max_input_size, stdin) != -1) {
    // do stuff with user_input
}
// either error or EOF, handle appropriately
free(user_input);

And one more bug: it's not safe to call fflush or other stdio functions in a signal handler.还有一个错误:在信号处理程序中调用fflush或其他 stdio 函数是不安全的。 It's also not necessary because the write() doesn't go through the stdio buffers anyway.这也不是必需的,因为无论如何write()都不会 go 通过 stdio 缓冲区。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM