8

我正在学习 linux 编程,遇到了非常有用的 exec 函数。但问题是 exec 函数参数非常混乱,我无法掌握哪个参数用于什么目的。在下面的代码execl()中,函数是从通过创建的子调用fork()的,最后一个参数 ( NULL)的目的是什么execl()

execl("/bin/ls","ls","-l",NULL);

如果有人能解释NULL论证和其他论证的目的是什么以及exec()家庭功能论证的目的,那对我有很大帮助!

4

5 回答 5

17

创建未定义的行为。这不是对execl. 正确的调用可能是:

execl( "/bin/ls", "ls", "-l", (char*)0 );

最后一个参数必须(char*)0,否则您有未定义的行为。第一个参数是可执行文件的路径。以下参数出现在argv执行的程序中。这些参数的列表以(char*)0;结尾。这就是被调用函数知道已到达最后一个参数的方式。例如,在上面的示例中,可执行文件 at"/bin/ls"将替换您的代码;在其中main,它将具有argc等于 2、具有argv[0]等于"ls"argv[1]等于"-l"

在这个函数之后,你应该有错误处理代码。(execl当它返回时总是返回-1,所以你不需要测试它。它只在出现某种错误时返回。)

于 2012-10-01T16:25:47.427 回答
4

这些exec函数是可变的:它们采用可变数量的参数,以便您可以将可变数量的参数传递给命令。函数需要NULL用作标记来标记参数列表的结尾。

在可变参数函数中有一个循环,它将遍历可变数量的参数。这些循环需要终止条件。在某些情况下,例如printf,参数的实际数量可以从另一个参数中推断出来。在其他函数中,NULL用于标记列表的结束。

另一种选择是为参数数量添加一个额外的函数参数,但这会使代码更脆弱,需要程序员管理一个额外的参数,而不是简单地总是使用 aNULL作为最终参数。

您还将看到(char *) 0用作标记:

execl("/bin/ls", "ls", "-l", (char *) 0);
于 2012-10-01T16:19:06.780 回答
1

/usr/include/libio.h中,由于 gcc 2.8(很久以前)NULL被定义为null(为内置函数保留),在此之前,由于NULL未传递类型,因此与在某种情况下(void *)0无法区分,例外情况是在这种情况下定义了if定义为 0。(char *)0varargs__cplusplusNULL

特别是如果您有 64 位架构,安全的做法是明确使用(void *)0定义为与任何指针兼容的指针,而不依赖于#defines标准库中可能出现的任何狡猾的东西。

于 2014-03-19T12:05:42.673 回答
0

结束参数的目的(char *) 0是终止参数。如果缺少它,可能会导致未定义的行为。手册页将execl签名定义为:

int execl(const char *path, const char *arg, ...);

path:要调用的程序的位置,要调用的程序。

arg , ...*: 可以认为是 arg0, arg1, ..., argn。

在您的情况下execl( "/bin/ls", "ls", "-l", (char*)0 );是正确的函数调用。

bin/ls ” 要调用的程序

ls ” 程序名称

-l ”是该程序的参数,称为

于 2018-09-10T21:39:47.287 回答
0

POSIX 函数的 glibc-2.34 实现execl如下所示。

/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/param.h>
#include <stddef.h>

/* Execute PATH with all arguments after PATH until
   a NULL pointer and environment from `environ'.  */
int
execl (const char *path, const char *arg, ...)
{
  ptrdiff_t argc;
  va_list ap;
  va_start (ap, arg);
  for (argc = 1; va_arg (ap, const char *); argc++)
    {
      if (argc == INT_MAX)
    {
      va_end (ap);
      errno = E2BIG;
      return -1;
    }
    }
  va_end (ap);

  /* Avoid dynamic memory allocation due two main issues:
     1. The function should be async-signal-safe and a running on a signal
        handler with a fail outcome might lead to malloc bad state.
     2. It might be used in a vfork/clone(VFORK) scenario where using
        malloc also might lead to internal bad state.  */
  ptrdiff_t i;
  char *argv[argc + 1];
  va_start (ap, arg);
  argv[0] = (char *) arg;
  for (i = 1; i <= argc; i++)
    argv[i] = va_arg (ap, char *);
  va_end (ap);

  return __execve (path, argv, __environ);
}
libc_hidden_def (execl)

第一个循环计算argc的参数个数execl(包括第一个参数path)。第一个循环的条件是va_arg (ap, const char *)。如果您使用0作为最后一个参数,宏将扩展为:

const char* temp = 0;
...
return temp; 

以这种方式分配的任何标量零值都是错误的。所以使用0作为最后一个参数将终止循环。

第二个循环形成argc-length 参数数组,argv。请注意,每个参数都char*在分配时显式转换。在0作为最后一个参数的情况下,argv宏扩展的结果值就像你写的一样:

char* p = 0;
argv[i] = p;
...

同样,这绝对是一个零值,并且将在 execve 内的循环条件中评估为 false。不受欢迎的意见,但0作为最后一个论点使用在execl技术上是正确的。您不必强调int (32-bit) -> char* (64-bit)“它是实现并且可能不会用零填充”转换 bs,因为 va_arg 是一个宏 - 它在现场定义a并分配给它,这保证它会有一个错误的逻辑值。char*0

于 2021-08-27T05:14:04.467 回答