簡體   English   中英

處理信號並編寫我自己的 shell

[英]Handle signals and process writing my own shell

我正在嘗試制作一個提示命令並等待用戶輸入的 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-cctrl-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 來完成的。

函數process只知道一個名為exit內部命令。 正如在問題的評論中已經指出的那樣,如果用戶輸入 Ctrl-D 作為行首的第一個字符,這將自動導致 getchar 調用中的 EOF。 函數 get_cmd 然后在此處返回命令exit 或者,用戶可以輸入命令exit來結束程序。

在函數process該位置標有注釋,您可能希望在該位置使用 fork/exec 調用外部程序。 無論程序是否應該退出,它都會返回一個布爾值。 可能在這里也可以評估來自外部程序調用的狀態代碼。

這是一個小型的、自包含的示例程序,帶有您的 ctrlc_handler、get_cmd 和 process 函數布局,用 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM