对 execvp 系统调用的调用如何工作?

How do calls to the execvp system call work?

我正在研究另一个 Whosebug question 的内容,我认为现在是温习 unix 系统调用知识的好时机。

在尝试 execvp 时(WITHOUT 故意分叉)我 运行 遇到了让我感到困惑的事情

我写了4个测试程序

计划 1

#include <stdio.h>

int main() {
        //printf("Doge\n");
        execvp("ls");
        printf("Foo\n");
        return 0;
}

程序按预期运行,打印了目录的内容,Foo打印语句没有

计划 2

然而,当我取消注释第一个打印语句并让程序成为这样时

#include <stdio.h>

int main() {
        printf("Doge\n");
        execvp("ls");
        printf("Foo\n");
        return 0;
}

execvp returns a -1 并且发出了两个打印语句。为什么?

计划 3

我依稀记得在大学里试验 unix 系统调用时不得不使用 unistd.h

所以我包含了它,但 execvp 没有不同的签名,它需要更多的参数,而不仅仅是程序的名称。所以我这样做了

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

int main() {
        printf("Doge\n");
        char *const parmList[] = {"ls", NULL};
        execvp("ls", parmList);
        printf("Foo\n");
        return 0;
}

这行得通。这让我很困惑。为什么 exec 在第一个程序中工作? 我还使用 This 作为系统调用的参考。

终于写好了

节目 4

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

int main() {
        printf("Doge\n");
        char *const parmList[] = {"ls", NULL};
        execvp("ls", parmList);
        printf("Foo\n");
        return 0;
}

这也符合预期。

谁能解释一下这是怎么回事?

有了这个片段

#include <stdio.h>

int main() {
        execvp("ls");
        printf("Foo\n");
        return 0;
}

您正在调用未定义的行为。您没有提供 execvp 的原型,它需要一个参数列表(以 null 终止)作为第二个参数。

在没有任何警告选项的情况下使用 gcc 会静默使用 execvp 作为隐式声明,并且不检查参数。它只是调用函数。然后该函数查找第二个参数并遇到...调用堆栈(或寄存器,取决于调用约定)剩下的任何内容,这就是为什么先前的 printf 调用可以改变行为的原因。

使用 gcc -Wall 给出以下警告:

test.c:5:9: warning: implicit declaration of function 'execvp' [-Wimplicit-function-declaration]
         execvp("ls");

包括正确的包含 (#include <unistd.h>) 导致:

test.c:6:9: error: too few arguments to function 'execvp'
         execvp("ls");
         ^~~~~~

这就是为什么你有奇怪的行为。别再看了。将 execvp 与 2 个参数、句点一起使用。在你的情况下 "Program 3" 是要走的路,并且尽可能将警告级别设置为最大(gccclang-Wall -Wextra -pedantic -Werror