【问题标题】:Running bash using posix instead of fork/execv使用 posix 而不是 fork/execv 运行 bash
【发布时间】:2016-12-17 20:20:04
【问题描述】:

我有一个 CLI,其中一个命令是进入 Linux bash shell。 这是使用 fork & execv 的代码:

if ((pid = fork()) < 0) {
    syslog_debug(LOG_ERR, "Could not fork");
}
if (pid == 0) {
    /* In child, open the child's side of the tty.  */
    int i;
    for(i = 0; i <= maxfd; i++)
    {   
        close(i);
    }       
    /* make new process group */
    setsid();
    if ((fd[0] = open(tty_name, O_RDWR /*| O_NOCTTY*/)) < 0) {
        syslog_debug(LOG_ERR, "Could not open tty");
        exit(1);
    }
    fd[1] = dup(0);
    fd[2] = dup(0);
    /* exec shell, with correct argv and env */
    execv("/bin/sh", (char *const *)argv_init);
    exit(1);
}

我想替换 fork/execv 并改用 posix_spawn:

ret = posix_spawn_file_actions_init(&action);
pipe(fd);
for(i = 0; i <= maxfd; i++)
{   
   ret = posix_spawn_file_actions_addclose (&action, i);
}    
ret = posix_spawn_file_actions_adddup2 (&action, fd[1], 0);
ret = posix_spawn_file_actions_adddup2 (&action, fd[2], 0); 
ret = posix_spawn_file_actions_addopen (&action, STDOUT_FILENO, tty_name, O_RDWR, 0);
char* argv[] = { "/bin/sh", "-c", "bash", NULL };
int status;
extern char **environ;
posix_spawnattr_t attr = { 0 };

snprintf( cmd, sizeof(cmd), "bash");
status = posix_spawn( &pid,
                    argv[0],
                    action /*__file_actions*/,
                    &attr,
                    argv,
                    environ );
posix_spawn_file_actions_destroy(&action);

但它不起作用。 有什么帮助吗?

【问题讨论】:

    标签: c linux posix ipc


    【解决方案1】:

    我们看一下原fork/exec中的代码做了什么:

    • 关闭所有文件描述符
    • 打开tty,碰巧得到0的fd,即stdin
    • 将此 fd 复制两次 (dup(0)),将它们放入 stdout 和 stderr
    • 执行shell命令

    您的新代码根本不遵循相同的模式。你想要做的是重复这个过程,但要更明确:

    关闭所有的FD:

    for(i = 0; i <= maxfd; i++)
    {
       ret = posix_spawn_file_actions_addclose (&action, i);
    }
    

    打开tty到STDIN_FILENO:

    ret = posix_spawn_file_actions_addopen (&action, STDIN_FILENO, tty_name, O_RDWR, 0);
    

    将STDIN_FILENO 复制到STDOUT_FILENO 和STDERR_FILENO:

    ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO);
    ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDERR_FILENO);
    

    那么posix_spawn 应该在正确的上下文中发生。

    对于旧的 fork/exec 进程,您应该执行以下操作:

    int fd = open(tty_name, O_RDWR /*| O_NOCTTY*/);
    if (fd != STDIN_FILENO) dup2(fd, STDIN_FILENO);
    if (fd != STDOUT_FILENO) dup2(fd, STDOUT_FILENO);
    if (fd != STDERR_FILENO) dup2(fd, STDERR_FILENO);
    

    意图更明确。

    ifs的原因是为了防止原来的fd意外的dup2变成新的fd号。唯一真正应该有这个问题的是第一个,因为fd == STDIN_FILENO 因为你没有打开任何其他文件描述符。

    要将其组合成一小段代码,使用echo something 而不是调用bash,我们有:

    #include <stdio.h>
    #include <spawn.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    void
    do_spawn()
    {
      int ret;
      posix_spawn_file_actions_t action;
      int i;
      pid_t pid;
      int maxfd = 1024;
      char *tty_name = ttyname (0);
    
      ret = posix_spawn_file_actions_init (&action);
      for (i = 0; i <= maxfd; i++) {
          ret = posix_spawn_file_actions_addclose (&action, i);
      }
      ret = posix_spawn_file_actions_addopen (&action, STDIN_FILENO, tty_name, O_RDWR, 0);
      ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO);
      ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDERR_FILENO);
      char *argv[] = { "/bin/sh", "-c", "echo something", NULL };
      int status;
      extern char **environ;
      posix_spawnattr_t attr = { 0 };
    
      posix_spawnattr_init(&attr);
      posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
      status = posix_spawn(&pid, argv[0], &action /*__file_actions*/ , &attr, argv, environ);
      printf ("%d %ld\n", status, pid);
    
      wait (0);
    
      posix_spawn_file_actions_destroy (&action);
    
    }
    
    int
    main(int argc, char **argv)
    {
      do_spawn();
    }
    

    这需要用-D_GNU_SOURCE编译,否则POSIX_SPAWN_USEVFORK没有定义。

    【讨论】:

    • 'ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO); ret = posix_spawn_file_actions_adddup2 (&action, STDIN_FILENO, STDOUT_FILENO);'第二个不应该是 STDERR_FILENO 而不是 STDOUT_FILENO?
    • 哎呀,错字,第二行应该是STDERR_FILENO,而不是STDOUT_FILENO
    • 谢谢,它澄清了一些问题。但是,看起来我仍然对 posix_spawn() 有问题。
    • 我不知道为什么,但上述解决方案仅在我删除“posix_spawn_file_actions_addclose”的“for”循环时才有效。如果循环没有被删除,我会在进程列表中看到:'[org_proc_name] ' 并且我没有看到 bash 提示或 echo 打印。
    • 我的代码使用ttyname(0) 来获取ttyname。如果这不起作用,那么您将看不到命令​​的输出——这可能是问题所在(我不知道,我只是在这里做出有根据的猜测)。 &lt;defunct&gt; 可能表示运行器中的崩溃,由 ttyname(0) 返回 NULL 触发。不幸的是,我对您的执行环境了解得不够多,无法做出任何其他猜测。
    猜你喜欢
    • 2023-03-24
    • 2012-08-09
    • 1970-01-01
    • 2018-08-25
    • 2013-10-09
    • 2017-06-21
    • 1970-01-01
    • 2017-02-14
    • 2015-05-06
    相关资源
    最近更新 更多