我应该处理哪个信号以及如何处理?

Which signal should I handle and how?

我应该 "wait until children are finished " 在我的代码中的哪个位置?我有一个类似于自定义 shell 的 C 程序。现在我有一个内置函数 checkEnv 可以打印排序的环境变量。所以我可以启动我的 shell 并列出我的环境变量:

$ ./a.out 
miniShell>> checkEnv
"'><;|&(:
_=./a.out
CLUTTER_IM_MODULE=xim
COMPIZ_CONFIG_PROFILE=ubuntu
COMP_WORDBREAKS=        
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-mh5oMhyCI6
DEFAULTS_PATH=/usr/share/gconf/ubuntu.default.path
DESKTOP_SESSION=ubuntu

背后的代码如下:

 if(StartsWith(line, "checkEnv")) {
     built_in_command=1;
     pagerValue = getenv ("PAGER");
     if (! pagerValue) {
       if (ret == 0) {
         pager_cmd[0]="less";
       } else {
         pager_cmd[0]="more";
       }
     }
    else {
     pager_cmd[0]=pagerValue;
    }


    if(i==1) {
       cmd[0].argv= printenv;
       cmd[1].argv= sort;
       cmd[2].argv= pager_cmd;
       fork_pipes(3, cmd);


    }
    else {

     for (k = 1; k < i; k++)
    {
         len += strlen(argv2[k]) + 2;
    }
    tmp = (char *) malloc(len);
    tmp[0] = '[=11=]';
    for (k = 1; k < i; k++)
    {
       pos += sprintf(tmp + pos, "%s%s", (k == 1 ? "" : "|"), argv2[k]);
    }
    grep[0]="grep";
    grep[1]="-E";
    grep[2]= tmp;
    grep[3]= NULL;
    cmd2[0].argv= printenv;
    cmd2[1].argv= grep;
    cmd2[2].argv= sort;
    cmd2[3].argv= pager_cmd;
    fork_pipes(4, cmd2);
    free(tmp);

            }

现在我想在使用寻呼机列出环境变量时捕获输出终止信号,以便程序 returns 到自定义 shell 而不是终止整个程序。所以我想我应该使用一些信号处理,但是如何以及使用哪个信号?

真正的fork来自这个函数。

/* Helper function that forks pipes */
void fork_pipes(int n, struct command *cmd) {
    int i;
    int in = 0;
    int fd[2];
    /** loop and fork() */
    for (i = 0; i < n - 1; ++i) {

        if (pipe(fd) == -1) {
            err_syserr("Failed creating pipe");
        }

        spawn_proc(in, fd[1], cmd + i);
        close(fd[1]);
        in = fd[0];
    }
    if (dup2(in, 0) < 0) {
        err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
    }
    fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd[i].argv[0]);
    execvp(cmd[i].argv[0], cmd[i].argv);
    err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}

信号处理应该去哪里?我正在尝试做的事情的最小工作示例在哪里?我没有看到这样做的例子,我认为文档很糟糕,只有片段,没有完整的例子。

我的辅助函数是

/* Helper function that spawns processes */
int spawn_proc(int in, int out, struct command *cmd) {
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        if (in != 0) {
            if (dup2(in, 0) < 0)
                err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
            close(in);
        }
        if (out != 1) {
            if (dup2(out, 1) < 0)
                err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
            close(out);
        }
        printf("** we are executing parent ***");
        fprintf(stderr, "%d: executing %s\n", (int) getpid(), cmd->argv[0]);
        execvp(cmd->argv[0], cmd->argv);
        err_syserr("failed to execute %s: ", cmd->argv[0]);
    }
    else if (pid < 0) {
        err_syserr("fork failed: ");
    } else {
        /* */
         printf("** we are the parent ***");
    }
    return pid;
}

我的 main() 现在看起来像这样:

int main(int argc, char *argv[]) {

    sourceCount = 0;
    const char *commandFile;

    commandFile = NULL;
    char *pathValue;
/*    struct sigaction sa, osa;
    struct sigaction sa2;*/
    int errflag;
    int cOption;
    struct sigaction action;
    /* use getopt_long()  */
    char *argv1[] = {"version", "par2", 0};
/*    char *argv2[] = {"help", "-m", "arg1", 0};*/

    /* use sigaction */

    sigemptyset(&action.sa_mask);
    action.sa_handler = handle_sigchld;
    action.sa_flags = 0;

    sigaction(SIGPIPE, &action, NULL);   //Not work with kill -13 process_id
    //works well
    sigaction(SIGINT, &action, NULL);    //work with kill -2 process_id

    errflag = 0;

    /* use getopt_long()  */
    while ((cOption = getopt(2, argv1, "m:t:n:fs?")) != -1) {

        switch (cOption) {
            case 'a':
                printf("apples\n");
                break;
            case 'b':
                printf("bananas\n");
                break;
            case 't':
                printf("tree = %s\n", optarg);
                break;
            case '?':
                ++errflag;
                break;
        }
    }
/*
    while (( cOption = getopt (3, argv2, "m:t:n:fs?")) != -1) {
        switch (cOption) {
            case 'm':
                printf("\n Help msg : %s \n", optarg);
                exit(0);
            case '?':
                printf("\n -? Arg : %s \n", optarg);
                break;
            case 'n':
                printf("\n -n Arg : %s \n", optarg);
                break;
        }
    }
*/


    /* get the PATH environment to find if less is installed */
    pathValue = getenv("PATH");
    if (!pathValue || getenv("PATH") == NULL) {
        printf("'%s' is not set.\n", "PATH");

        /* Default our path if it is not set. */

        putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
    }
    else {
        printf("'%s' is set to %s.\n", "PATH", pathValue);
    }
    exec_program(commandFile);
    return (0);
}

如果我 运行 我的 shell 在 gdb 我得到一个正常的出口。

(gdb) run
Starting program: /home/dac/ClionProjects/shell2/openshell/shell 
'PATH' is set to /home/dac/proj/google-cloud-sdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin.
dac:/home/dac/ClionProjects/shell2/openshell $ checkenv
7429: executing printenv
7430: executing grep
7417: executing less
7431: executing sort
process 7417 is executing new program: /bin/less
[Inferior 1 (process 7417) exited normally]
(gdb) 

您想捕获 SIGCHLD,并使用 wait() 函数之一获取子状态。

以下信息来自online tutorial

void handle_sigchld(int sig) {
    int saved_errno = errno;
    while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
    errno = saved_errno;
}

struct sigaction sa;
sa.sa_handler = &handle_sigchld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
    perror(0);
    exit(1);
}

此外,您可能希望忽略 SIGPIPE

struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGPIPE, &sa, 0) == -1) {
  perror(0);
  exit(1);
}

在 parent 中,您不需要接收任何信号,只需调用 wait() 直到 returns 您的 child 已经死亡并且现在正在收割。您的 shell 现在可以执行新任务了。

你在原始请求中的代码如果很不完整,那么这里有一个例子:

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

void foobar()
{
  int pipe_fd[2];
  pid_t pid1, pid2;

  pipe(pipe_fd);

  pid1=fork();
  if (pid1 == 0)
  {
    /* child1 - let us pretend that we wanted to replace stdin and this child */
    close (0);
    dup(pipe_fd[0]);
    close(pipe_fd[0]);
    close(pipe_fd[1]);
    execlp("wc", "wc", NULL);
    perror ("execlp(wc)");
    _exit(0);
  }

  pid2=fork();
  if (pid2 == 0)
  {
    /* child - let us pretent that we wanted to replace stdout */
    close (1);
    dup(pipe_fd[1]);
    close(pipe_fd[0]);
    close(pipe_fd[1]);
    execlp("ls", "ls", "-l", NULL);
    perror ("execlp(ls)");
    _exit(0);
  }

  close(pipe_fd[0]);
  close(pipe_fd[1]);

  /* wait until children are finished */
  while ((pid1 >= 0) || (pid2 >= 0))
  {
    pid_t pid;
    int status;
    pid = wait(&status);
    if (pid < 0)
    {
      continue;
    }
    if (pid == pid1)
    {
      pid1 = -1;
    }
    if (pid == pid2)
    {
      pid2 = -1;
    }
  }
}

int main(int argc, char *argv[])
{
        foobar();
        return 0;
}