Я написал программу, которая должна вычислять общий размер аргументов, переданных системному вызову execve
.
Я протестировал эту программу с максимальным размером аргументов, ожидая, что ошибка Список аргументов слишком длинный произойдет только при превышении предела ARG_MAX
. На мой взгляд, максимальный суммарный размер командной строки должен быть как можно ближе к лимиту ARG_MAX
, то есть без превышения этого лимита нельзя добавить дополнительный аргумент (имя файла).
Но я вижу другое поведение: количество неиспользуемых байтов колеблется непредсказуемым образом, в то время как среда и имя программы остаются неизменными, меняется только количество аргументов.
Вопросы:
- программа подсчета неверна и пропустила некоторые значения? Почему Слишком длинный список аргументов появляется раньше, чем должен?
- это нормальное поведение, а неиспользуемые байты - это тип заполнения/выравнивания памяти/что угодно? Где тогда это поведение упоминается в исходниках ядра? Я прочитал linux/fs/exec.c и не видел ответа на мой вопрос.
Программа
Алгоритм подсчета следующий:
размер argv
+ размер envp
+ размер argc
argv
- это массив указателей на строки (указатель наchar
), поэтому прокрутите этот массив и добавьте к результату длины строк, помня, что каждая из них заканчивается нулевым байтом. Затем добавьте их указатели к результату - размер указателя 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 байта — 2 279 байт не используются.
$ ./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
Этот вопрос является частью моего другого вопроса: Как рассчитать количество файлов, которые можно передать в качестве аргументов какой-либо команде для пакетной обработки?
xarg
]? Есть ряд [стандартных] программ, которые уже решают эту проблему. - person Craig Estey   schedule 18.09.2020e2big.c
в src/so-1855-9403. - person Jonathan Leffler   schedule 23.09.2020envp
дляmain()
, он признает его как общее расширение — см. Приложение J §J.5.1 Аргументы среды. В Linux этот аргумент доступен, и вопрос помечен тегом Linux. - person Jonathan Leffler   schedule 23.09.2020