提问人:lfalkau 提问时间:3/20/2020 更新时间:3/21/2020 访问量:1036
处理信号并编写我自己的 shell
Handle signals and process writing my own shell
问:
我正在尝试制作一个提示命令并等待用户输入的外壳。
我希望我的 shell 在用户按下时打印另一个提示,并在用户按下时退出。ctrl-c
ctrl-d
这是主循环:
int my_shell(char **env)
{
char *cmd_line;
while (true)
{
print_prompt();
cmd_line = get_cmd();
process(cmd_line);
}
return (0);
}
我能够捕捉和发出信号,但我不知道如何构建主循环以在好地方退出。我尝试使用几个,但我做错了。ctrl-c
ctrl-d
fork()
wait()
getpid()
这是我的一次尝试:
int extern_pid;
int intern_pid;
int my_shell(char **env)
{
char *cmd_line;
extern_pid = getpid();
while (true)
{
if (fork() == 0)
{
intern_pid = getpid();
print_prompt();
cmd_line = get_cmd();
process(cmd_line);
exit(0);
}
else
{
wait(0);
}
}
return (0);
}
对于这些信号处理程序:
void ctrlc_handler(int signal)
{
if (getpid() == intern_pid)
exit(0);
}
void ctrld_handler(int signal)
{
if (getpid() == extern_pid)
exit(0);
}
注意:信号在函数中处理。ctrl-d
get_cmd()
答:
无需使用 fork 创建子进程即可在自定义 shell 中处理 Ctrl-C 信号。一种可能性是将信号处理程序与 sigsetjmp/siglongjmp 一起使用。
程序如下:
- 信号处理程序已安装
- 在启动主循环之前,调用环境会使用 SIGSETJMP 存储在 env 中
- 在信号处理程序中,调用 siglongjmp() 恢复 sigsetjmp() 保存的环境,并执行非本地跳转到调用 sigsetjmp 的位置
由于信号处理程序总是可以调用的,甚至可能在调用 sigsetjmp() 之前,因此必须确保 siglongjmp() 已经可以被调用。这是通过设置一个名为 jmp_set 的易失变量sig_atomic_t来完成的。
该函数只知道一个名为 的内部命令。正如在问题注释中已经指出的那样,如果用户在行首输入 Ctrl-D 作为第一个字符,这将自动导致 getchar 调用中的 EOF。然后,函数get_cmd在此处返回命令。或者,用户可以输入命令来结束程序。process
exit
exit
exit
在函数中,该位置标有注释,您可能希望使用 fork/exec 调用外部程序。它返回一个布尔值,说明是否应该退出程序。也许也可以在这里相应地评估来自外部程序调用的状态代码。process
这是一个小型的、独立的示例程序,其中包含您的ctrlc_handler、get_cmd和进程函数布局,并使用 sigsetjmp()/siglongjmp() 进行了扩展,当然并不完整,但可能是一个起点:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <setjmp.h>
#define MAX_CMD_LENGTH 1024
static const char *const EXIT_CMD = "exit";
static sigjmp_buf env;
static volatile sig_atomic_t jmp_set;
static void ctrlc_handler(int signal) {
if (jmp_set == 0)
return;
if (signal == SIGINT) {
siglongjmp(env, 1);
}
}
static char *get_cmd(void) {
char *cmd_buf = calloc(MAX_CMD_LENGTH, sizeof(char));
if (!cmd_buf) {
perror("malloc failed\n");
exit(1);
}
char *ptr = cmd_buf;
int ch = getchar();
while (ch != EOF && ch != '\n' && ptr < cmd_buf + MAX_CMD_LENGTH - 1) {
*ptr++ = (char) ch;
ch = getchar();
}
if (ch == EOF) {
strcpy(cmd_buf, EXIT_CMD);
}
return cmd_buf;
}
static bool process(char *cmd) {
if (strcmp(cmd, EXIT_CMD) == 0) {
return true;
}
// a call to fork together with a function from the
// exec family could be used to call external programs
printf("process '%s'\n", cmd);
return false;
}
static int cnt = 0;
int main(void) {
if (signal(SIGINT, ctrlc_handler) == SIG_ERR) {
perror("signal error");
exit(1);
}
if (sigsetjmp(env, 1)) {
printf("\n");
cnt++;
}
jmp_set = 1;
bool exit;
do {
printf("%d> ", cnt);
char *cmd = get_cmd();
exit = process(cmd);
free(cmd);
} while (!exit);
return 0;
}
评论
read
read