【问题标题】:execve: how can I initialise char *argv[ ] with multiple commands instead of a single command?execve:如何使用多个命令而不是单个命令来初始化 char *argv[ ]?
【发布时间】:2017-07-26 06:12:04
【问题描述】:

execve:如何使用多个命令而不是单个命令来初始化 char *argv[ ]
如果要执行4条命令,可以用下面的语句吗?

char *argv[4][ ] = { {...}, {...}, {...} };

要使用 execve 执行它们,我可以使用 var 从 1 到 4 的循环吗?

【问题讨论】:

  • exec 系列函数只能执行 one 程序。您不能使用它们来执行多个程序。尤其不是通过argv 数组,exec 不会使用它来查找要执行的程序。
  • 您将不得不使用循环。 execve 将只执行第一个程序并将其他命令视为第一个程序的参数。你是对的,你必须使用循环。
  • 哦,记得在循环中给fork一个新进程,因为成功的execve调用不会返回。
  • 执行一批命令的工具是 shell,所以你可以 execve() /bin/sh 使用适当的参数列表 --- 或者如前所述,自己做 shell 的工作(@ 987654331@、execve()wait() ... 或 posix_spawn()wait())
  • @Someprogrammerdude,更好的说法是是一个系统调用,旨在将程序映像加载到内存中并开始执行它。恕我直言。

标签: c linux posix pid


【解决方案1】:

您不能仅通过一个execve 调用来执行多个命令。在一个循环中,您将需要 fork 您的程序执行多个 execve 调用。在manpage of execve 中写道:

execve() 成功时不返回,调用进程的文本、数据、bss 和堆栈被加载的程序覆盖。 [...]

返回值
成功时, execve() 不返回,错误时返回 -1,并且 errno 设置得当。

使用fork的方法:

输出:

Hello 1
Hello 2
Hello 3

代码:

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

int main(void)
{
   int idx;
   char *argv[][4] = { {"/bin/sh", "-c", "echo Hello 1", 0},
                       {"/bin/sh", "-c", "echo Hello 2", 0},
                       {"/bin/sh", "-c", "echo Hello 3", 0} };
   for (idx = 0; idx < 3; idx++)
   {
      if (0 == fork()) continue;
      execve(argv[idx][0], &argv[idx][0], NULL);
      fprintf(stderr, "Oops!\n");
   }

   return 0;
}

使用命令连接的方法:
一种解决方法是使用 shell 连接命令:

输出:

Hello 1
Hello 2

代码:

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

int main(void)
{
   char *argv[] = {"/bin/sh", "-c", "echo Hello 1 && echo Hello 2", 0};
   execve(argv[0], &argv[0], NULL);
   fprintf(stderr, "Oops!\n");

   return 0;
}

【讨论】:

  • 嗨@Linuxknowledgeseeker,如果这个或任何答案已经解决了您的问题,请点击复选标记考虑accepting it。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己提供了一些声誉。没有义务这样做。
【解决方案2】:

exec*() 系列函数用新的可执行文件替换当前正在运行的进程。所以没有办法执行多个命令,因为一旦你调用execve(),你自己的程序就不再运行了——进程现在正在执行新程序。

现有答案显示了使用fork() 创建新进程并在那里调用exec*() 函数的“经典”方法。这有一些复制一些与进程相关的资源的开销,这些资源会立即被exec*() 调用再次替换。为了解决这种低效率,发明了vfork()vfork() 不应该进行任何复制,因此,除了调用 _exit()vfork() 创建的子项中的 exec*() 函数之一之外,还可以做任何事情未定义的行为 .

这是一个巨大的错误来源,后来又从 POSIX 标准中删除,因此您不应在现代程序中使用vfork()。如今,有一种新的方法可以解决这个问题:posix_spawn()。此函数直接使用新的可执行文件创建一个新进程。由于这非常适合您在问题中尝试实现的目标,因此这里有一个很小的用法示例:

#include <stdio.h>
#include <spawn.h>
#include <sys/types.h>
#include <sys/wait.h>

extern char **environ;

int main(void)
{
    char *argv[][3] = {
        { "echo", "First command", 0},
        { "echo", "Second command", 0}
    };

    for (int i = 0; i < 2; ++i)
    {
        pid_t pid;
        if (posix_spawn(&pid, "/bin/echo", 0, 0, argv[i], environ) != 0)
        {
            fputs("Error spawning child.\n", stderr);
        }
        else
        {
            // could get exit code etc here, see
            // https://linux.die.net/man/2/waitpid
            wait(0);
        }
    }
    return 0;
}

【讨论】:

  • 如果每个命令的参数个数不同,我们应该如何初始化并在循环中调用呢?
  • @Linuxknowledgeseeker 最简单的方法是:在末尾填充更多 0 值,因此它们的长度相同。或者也许不使用循环。或者使用char ** 的数组而不是char * 的二维数组。这取决于你想做什么。
  • 能否请您发布一个带有 char ** 的示例以执行 4 个具有可变参数数量的命令
【解决方案3】:

嗯,你不能。简单地。 execve(2) 系统调用不是执行多个命令的调用,而只是通过将新的程序映像加载到其虚拟空间中来覆盖进程分配的空间。这意味着从execve(2) 执行多个命令的唯一方法是加载一个shell 并使其执行多个命令。您必须了解这意味着将程序加载到内存中并开始执行它,而不是一般地执行程序或 shell 脚本。当你理解了这一点,你就可以理解加载一个shell来连续执行多个命令的原因(比如下面的例子或者system(3)函数调用的作用)

execlp("/bin/bash", "bash", "-c", "echo foo; echo bar", NULL);

例如,将执行两个不同的 echo 命令,它们在单个参数字符串中传递给 bash(1) shell。

注意

为了简单起见,我使用了类似的execvp(2) 系统调用,但同样适用于execve(2)(这是完整的通用系统调用接口,尽管两者都转换为相同的exec() 系统调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-19
    • 1970-01-01
    • 2023-02-04
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2017-10-23
    相关资源
    最近更新 更多