execve 系统调用(由所有exec* 函数使用)具有char *const argv[] 形式的参数。内核通过将提供的argv 迭代为follows 来计算argc:
static int count(struct user_arg_ptr argv, int max)
{
int i = 0;
if (argv.ptr.native != NULL) {
for (;;) {
const char __user *p = get_user_arg_ptr(argv, i);
if (!p)
break;
if (IS_ERR(p))
return -EFAULT;
if (i >= max)
return -E2BIG;
++i;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
}
return i;
}
函数get_user_arg_ptr 实质上计算argv 数组的索引并返回存储在该索引处的指针。循环在四种情况下中断,其中两种与您的问题有关:
- 在 argv 数组中看到的第一个
NULL。如果argv 中的第一个NULL 后面还有其他指针,则它们将被忽略。拥有多个 NULL 听起来像是构建 argv 的程序中的错误。
- 当指针数大于等于
MAX_ARG_STRINGS时,即defined为0x7FFFFFFF。在这种情况下,系统调用会失败。
get_user_arg_ptr 返回时,i 返回的值被赋值给argc。
在argv 中终止NULL 很重要的另一种情况是应用程序本身使用argv,如下所示:
for(char **p = argv; *p != NULL; ++p)
{
// ...
}
argv 以NULL 结尾是 Linux ABI 的一部分,因此此类代码在所有 Linux 实现中都是合法且可移植的。顺便说一句,此代码在 Windows 上也是合法的。因此,提供argc 只是为了方便。
此外,C 和 C++ 标准分别在 5.1.2.2.1 和 3.6.1 中规定,如果 argc 大于零,则 argv[0] 到 argv[argc-1] 中的所有值都应为非-null 指向以 null 结尾的字符串的指针。 argv[argc] 也必须为 null 并且 argc 是非负数。另请参阅this 答案。