【问题标题】:system() function string length limitsystem() 函数字符串长度限制
【发布时间】:2019-04-13 15:08:39
【问题描述】:

一个字符串可以传递给system()多长时间?

我知道 POSIX 最小值是 4096,但我想知道我可以使用的实际大小。是否在任何标头中为此定义了任何宏,类似于FILENAME_MAX

char cmd[SOME_MACRO];

...
system(cmd);

【问题讨论】:

  • 开始担心你的字符串是否超过 100 KiB。在那之前,你应该没事。
  • 如果这对您的编程来说是个问题,您最好将命令参数写入文件并更新命令以读取该文件。许多程序,如 Windows 上的编译器和链接器,已经可以从文件中读取参数。一些 Unix 程序,如 xargs 可以读取参数流并为每个 n 参数运行一个命令模板副本。
  • 顺便说一句,system() 函数与系统调用(用户空间/内核空间边界)意义上的“系统调用”完全不同。

标签: c linux posix


【解决方案1】:

system exec 是一个带有参数 "sh","-c", YourAgumentToSystem, (char*)0 (guaranteed by POSIX) 的 shell,所以 最大长度(不包括'\0' 终止符)为ARG_MAX -1 -3 -3 - size_of_your_environment

ARG_MAXlimits.h 中定义为

" exec 函数的最大参数长度,包括 环境数据。”

如果limits.h,没有定义ARG_MAX,你should be able to call sysconf(_SC_ARG_MAX) 获取运行时限制。

execve(由系统调用)的 linux 手册页提供了更多信息:

在内核 2.6.23 之前的 Linux 上,用于存储 环境和参数字符串限制为 32 页(由 内核常量 MAX_ARG_PAGES)。在具有 4-kB 页面的架构上 大小,这产生的最大大小为 128 kB。

在内核 2.6.23 及更高版本上,大多数架构都支持大小限制 源自软 RLIMIT_STACK 资源限制(请参阅 getrlimit(2)) 这在 execve() 调用时生效。 (建筑与 没有内存管理单元除外:它们保持 在内核 2.6.23 之前生效。)此更改允许程序 有一个更大的参数和/或环境列表。对于这些 架构,总大小限制为允许堆栈的 1/4 尺寸。 (施加 1/4 限制可确保新程序始终具有 一些堆栈空间。)从 Linux 2.6.25 开始,内核放置了 32 层 此大小限制的页面,因此,即使 RLIMIT_STACK 设置得非常 低,应用程序保证至少有同样多的论点和 Linux 2.6.23 及更早版本提供的环境空间。 (这 Linux 2.6.23 和 2.6.24 中未提供保证。)此外, 每个字符串的限制是 32 页(内核常量 MAX_ARG_STRLEN), 最大字符串数为0x7FFFFFFF。

要测量环境的大小,您可以运行:

extern char **environ;
size_t envsz = 0; for(char **e=environ; *e; e++) envsz += strlen(*e)+1;

(正如Zan Lynx 在 cmets 中指出的那样,这可以加快(根据我的测量,cca 20 倍——对于我测量时的 100 字符串 6KB 环境,从 1600ns 到 80ns) 如果你假设environ 中的char* 指针指向一个连续的缓冲区,它们在程序启动后执行,但调用setenvputenvunsetenv 通常会破坏这一点:

extern char **environ;
char **e; for(e=environ; *e; e++) {}
size_t envsz =  ($_sz)(e[-1]+strlen(e[-1])+1 - *environ);

在任何情况下,如果您希望尽快 fork+exec (/system),那么以健壮性为代价的加速应该不会太重要,因为在 Linux 上 fork+exec 通常至少花费大约 1-2ms在现代机器上。)

【讨论】:

  • 请注意,对于自动存储分配,此值可能很大。
  • 它也不能保证超过它会失败——它(只是)未定义的行为,所以它可能会在你调用它时起作用,而在下一次调用它时会失败。
  • 我认为这不是计算环境大小的最有效方法。我相信你可以运行指针直到 NULL 并从环境中减去,而不涉及 strlen 调用。
  • @ZanLynx 我认为这只适用于新鲜的环境。环境操作函数似乎无法保持后端连续。
  • @PSkocik 啊,是的,这看起来是真的。嗯。
【解决方案2】:

限制高度依赖于系统。它甚至可能取决于将要使用的命令外壳。您应该测试system() 的返回值以查看系统调用是否成功:-1 表示失败,errno 应该会为您提供更多信息。应该为任何适当的 C 字符串定义行为。

POSIXsystem(command) 等价于的文档:

execl(<shell path>, "sh", "-c", command, (char *)0);

还记录了在&lt;limits.h&gt; 中定义的ARG_MAX 作为exec 和环境变量的参数组合长度的限制。

但请注意,command 可能包含通配符和/或其他外壳词,其扩展可能会超出其他限制。始终检查返回值是否失败。

【讨论】:

    【解决方案3】:

    人3系统

    给我们

    描述

    system()库函数使用fork(2)创建子进程,执行command中指定的shell命令 使用 execl(3) 如下:

           execl("/bin/sh", "sh", "-c", command, (char *) 0);
    
       system() returns after the command has been completed.
    

    所以 system() 是 execl() 的包装器

    从同一页面,我们还看到此调用符合某些标准。

    符合

    POSIX.1-2001、POSIX.1-2008、C89、C99。

    查找 POSIX.1-2008 会产生以下在线参考

    https://pubs.opengroup.org/onlinepubs/9699919799/

    我们可以在哪里搜索系统将我们带到的execl 函数的信息

    https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html

    提供以下功能

    新进程的组合参数和环境列表可用的字节数为 {ARG_MAX}。这个总数中是否包括空终止符、指针和/或任何对齐字节是由实现定义的。

    最后...

    错误

    如果出现以下情况,exec 函数将失败:

    [E2BIG] 新过程映像参数使用的字节数 列表和环境列表大于系统强加的限制 {ARG_MAX} 个字节。

    所以这里要执行的最后检查是实际的 exec 实现,而不是依赖标准,以防万一实现偏离标准。

    因此,man 3 execl 报告返回的错误与 execve(2) 记录的错误相同,man 2 execvw 报告以下内容:

    错误

    E2BIG 环境 (envp) 和参数列表 (argv) 中的总字节数太大。

    不如 POSIX 标准精确?最好检查代码或查看(现在)接受的答案:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多