【发布时间】:2020-09-18 16:07:07
【问题描述】:
我写了一个程序来计算传递给execve系统调用的参数的总大小。
我已经用最大参数大小测试了这个程序,预计“参数列表太长”错误只会在超过 ARG_MAX 限制时发生。在我看来,命令行的最大总大小应该尽可能接近ARG_MAX的限制,即在不超过这个限制的情况下不能添加额外的参数(文件名)。
但我看到了另一种行为:“未使用”字节的数量以不可预测的方式波动,而环境和程序名称保持不变,只有参数的数量在变化。
问题:
- 计数程序不正确并且缺少一些值?为什么“Argument list too long”发生得早于应有的时间?
- 这是正常行为,未使用的字节是内存填充/对齐/其他类型?那么在内核源代码中哪里提到了这种行为?我已阅读 linux/fs/exec.c 并没有看到任何东西可以回答我的问题。
计划
接下来是计数算法:
argv 的大小 + envp 的大小 + argc 的大小
-
argv是指向字符串的指针数组(指向char的指针),因此循环该数组并将字符串的长度添加到结果中,记住每个字符串都以 NULL 字节结尾。然后将它们的指针添加到结果中 - 指针的大小为 8 字节。因此:the number of pointers * 8+lengths of strings (each with a NULL byte) -
envp的情况几乎相同——字符串长度为 NULL 字节和指针。但是最后一个指针通过指向 NULL 字节来指示数组的末尾,因此将其添加到结果8 bytes + 1 bytes。 -
argc很简单int。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[], char *envp[]) {
size_t char_ptr_size = sizeof(char *);
// The arguments array total size calculation
size_t arg_strings_size = 0;
size_t string_len = 0;
for(int i = 0; i < argc; i++) {
// Every string ends up with a nullbyte, so the 1 byte is added
string_len = strlen(argv[i]) + 1;
arg_strings_size += string_len;
// printf("%zu:\t%s\n", string_len, argv[i]);
}
size_t argv_size = arg_strings_size + argc * char_ptr_size;
printf( "arg strings size: %zu\n"
"number of pointers to strings %i\n\n"
"argv size:\t%zu + %i * %zu = %zu\n",
arg_strings_size,
argc,
arg_strings_size,
argc,
char_ptr_size,
argv_size
);
// The enviroment variables array total size calculation
size_t env_size = 0;
for (char **env = envp; *env != 0; env++) {
char *thisEnv = *env;
// Every string ends up with a nullbyte, so the 1 byte is added
env_size += strlen(thisEnv) + 1 + char_ptr_size;
}
// The last element of "envp" is a pointer to the NULL byte, so size of pointer and 1 is added
printf("envp size:\t%zu\n", env_size + char_ptr_size + 1);
size_t overall = argv_size + env_size + sizeof(argc);
printf( "\noverall (argv_size + env_size + sizeof(argc)):\t"
"%zu + %zu + %zu = %zu\n",
argv_size,
env_size,
sizeof(argc),
overall);
// Find ARG_MAX by system call
long arg_max = sysconf(_SC_ARG_MAX);
printf("ARG_MAX: %li\n\n", arg_max);
printf("Number of \"unused bytes\": ARG_MAX - overall = %li\n\n", arg_max - (long) overall);
return 0;
}
测试
1 字节文件名 - 975 字节未使用。
$ ./program $(yes A | head -n 209222) # 209223 will cause "Argument list too long"
arg strings size: 418454
number of pointers to strings 209223
argv size: 418454 + 209223 * 8 = 2092238
envp size: 3944
overall (argv_size + env_size + sizeof(argc)): 2092238 + 3935 + 4 = 2096177
ARG_MAX: 2097152
Number of "unused bytes": ARG_MAX - overall = 975
2 字节文件名 - 3206 字节未使用。
$ ./program $(yes AA | head -n 189999)
arg strings size: 570007
number of pointers to strings 190000
argv size: 570007 + 190000 * 8 = 2090007
envp size: 3944
overall (argv_size + env_size + sizeof(argc)): 2090007 + 3935 + 4 = 2093946
ARG_MAX: 2097152
Number of "unused bytes": ARG_MAX - overall = 3206
3 字节文件名 - 2279 字节未使用。
$ ./program $(yes AAA | head -n 174243)
arg strings size: 696982
number of pointers to strings 174244
argv size: 696982 + 174244 * 8 = 2090934
envp size: 3944
overall (argv_size + env_size + sizeof(argc)): 2090934 + 3935 + 4 = 2094873
ARG_MAX: 2097152
Number of "unused bytes": ARG_MAX - overall = 2279
这个问题是我另一个问题的一部分:How calculate the number of files which can be passed as arguments to some command for batch processing?
【问题讨论】:
-
envp 不是 C 标准。为了回答您的问题,“如何计算可以作为参数传递给某些命令以进行批处理的文件数量?”,您最好阅读这篇有趣的文章:serverfault.com/questions/163371/… 简而言之,限制通常是系统特定的。跨度>
-
我想我会问这个问题:这个规模吗?如果你必须处理 100,000,000 个文件 [并且可以不使用
xarg风格的解决方案]?有许多 [标准] 程序已经解决了这个问题。 -
@CraigEstey 不,这个问题纯粹是理论上的——出于教育目的。
-
您可以在我对 SO 问题 To check the E2BIG condition in exec 的回答中找到一些代码。它通过反复试验(和二进制搜索)来确定大小,以达到可用空间的千字节左右。它也可以在我的SOQ(堆栈溢出问题)存储库中作为src/so-1855-9403 子目录中的文件
e2big.c找到。 -
虽然 C 标准并未强制要求
main()使用envp参数,但它确实将其视为通用扩展 - 请参阅附件 J §J.5.1 Environment arguments。在 Linux 上,这个论点是可用的,并且这个问题被标记为 Linux。
标签: c linux bash linux-kernel execve