在调用exec*()系列函数时,argv的char*元素都必须是唯一的吗?

bgo*_*dst 5 c posix exec

我正在尝试编写一个小实用程序,将其参数列表中继到exec'd进程,除了在构建新进程的参数列表时重复一些传入的参数.

下面是我想要做的非常简化的版本,它简单地复制每个参数一次:

#include <stdlib.h>
#include <unistd.h>

#define PROG "ls"

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

    int progArgCount = (argc-1)*2;
    char** execArgv = malloc(sizeof(char*)*(progArgCount+2)); // +2 for PROG and final 0
    execArgv[0] = PROG;
    for (int i = 0; i<progArgCount; ++i)
        execArgv[i+1] = argv[i/2+1];
    execArgv[progArgCount+1] = 0;

    execvp(PROG, execArgv );

} // end main()
Run Code Online (Sandbox Code Playgroud)

注意元素execArgv是如何唯一的.具体来说,每个重复中的两个元素是相同的,这意味着它们指向内存中的相同地址.

标准C是否说明了这种用法?是不正确的还是未定义的行为?如果不是,它是否仍然是不可取的,因为执行程序可能取决于其argv元素的唯一性?如果我错了,请纠正我,但是程序不能argv直接修改它们的元素,因为它们是非const的吗?难道这不会造成exec'd程序轻易修改argv[1](比如说)然后访问的风险argv[2],错误地假设这两个元素指向独立的字符串吗?几年前,当我开始学习C/C++时,我很确定自己这样做了,当时我并不认为这些argv元素可能不是唯一的.

我知道exec'ing涉及"替换过程图像",但我不确定这究竟是什么.我可以想象它可能涉及深度复制给定的argv参数(execArgv在上面的例子中)到新的内存分配,这可能会使事情无法解释,但我不太了解exec函数的内部说法.这将是浪费,至少如果原始数据结构可以在"替换"操作中保留,那么这是我怀疑它发生的原因.也许不同的平台/实现在这方面表现不同?请问回答者请谈谈这个吗?


我试图找到关于这个问题的文档,但我只能从http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html找到以下内容:

程序使用其中一个exec函数指定的参数应传递给相应的main()参数中的新过程映像.

上述内容并未阐明它是否是对新进程传递的参数的单一深度复制.

参数argv是一个到​​以null结尾的字符串的字符指针数组.应用程序应确保此数组的最后一个成员是空指针.这些字符串应构成新过程映像可用的参数列表.argv [0]中的值应指向与其中一个exec函数启动的进程相关联的文件名.

同上以上.

argv []和envp []指针数组以及这些数组指向的字符串不应通过调用其中一个exec函数来修改,除非是替换过程映像.

我老实说不知道如何解释上面的内容."替换过程映像"是exec函数的全部内容!如果它要修改数组或字符串,那么在某种意义上,这将构成"替换过程映像的结果".这几乎意味着exec函数将会修改argv.这段摘录简直加剧了我的困惑.

关于argv []和envp []作为常量的声明是为了使未来的语言绑定编写者明确表示这些对象是完全不变的.由于ISO C标准的限制,不可能在标准C中陈述该想法.为exec函数指定argv []和envp []参数的两个const限定级别似乎是自然的选择鉴于这些函数不修改指针数组或函数指向的字符,但这将禁止现有的正确代码.相反,只有指针数组被标记为常量.从ISO C标准派生的dst = src的赋值兼容性表总结了兼容性:

目前尚不清楚"关于argv []和envp []为常数的陈述"是指什么; 我的主要理论是它指的是文档页面顶部给出的原型中参数的const限定.但由于这些限定符只标记指针而不是char数据,因此很难明确"这些对象是完全不变的".其次,我不知道为什么该段谈到"语言绑定的作者"; 绑定到什么?这与exec函数的一般文档页面有什么关系?第三,该段的主要内容似乎是说我们坚持charargv元素指向的字符串的实际内容保留为非常量,以便与已建立的ISO C标准向后兼容并且"现有正确代码"符合它.这由文档页面上的表格确认,我在此不会引用.这些决定性地回答了我的主要问题,尽管它在摘录的中间相当清楚地说明了exec函数本身并不argv以任何方式修改给定对象.


我非常感谢有关我的主要问题的信息,以及对我对引用文献摘录的解释和理解的评论(特别是,如果我的解释是错误的).谢谢!

H.S*_*.S. 2

标准 C 对这种用法有什么说明吗?这是不正确的,还是未定义的行为?

如果两个指针指向同一内存位置,则没有问题。这不是未定义的行为。

如果不是,是否仍然不可取,因为执行的程序可能取决于其 argv 元素的唯一性?

POSIX标准没有指定有关 argv 元素的唯一性的任何内容。

如果我错了,请纠正我,但是程序是否可以直接修改它们的 argv 元素,因为它们是非常量的?

来自C 标准#5.1.2.2.1p2

The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.

所以,答案是——是的,这是可能的。

这是否会造成 exec'd 程序愉快地修改 argv [1](比如说)然后访问 argv 的风险[2],错误地假设这两个元素指向独立的字符串?

在计算中,exec是操作系统的一种功能,它在现有进程的上下文中运行可执行文件,替换以前的可执行文件。

因此,当exec执行系列系统调用时,参数中给出的程序将被加载到调用者的地址空间中并覆盖那里的程序。结果,一旦指定的程序文件开始执行,调用者地址空间中的原始程序就消失了,并被新程序和argv存储在新替换的地址空间中的参数列表所取代。

POSIX标准规定:

The number of bytes available for the new process' combined argument and environment lists is {ARG_MAX}. It is implementation-defined whether null terminators, pointers, and/or any alignment bytes are included in this total.

ARG_MAX

{ARG_MAX} Maximum length of argument to the exec functions including environment data.

这意味着为新的进程参数分配了一些空间,并且可以安全地假设参数字符串已复制到该空间。

我知道执行涉及“替换进程映像”,但我不确定这到底意味着什么。

检查这个

也许不同的平台/实现在这方面表现不同?回答者可以谈谈这个吗?

不同平台的实现可能有所不同,但 Unix 的所有变体都必须遵循相同的 POSIX 标准以保持兼容性。因此,我相信所有平台上的行为都必须相同。