Ubuntu中的execl()参数

wribegjk  于 2022-11-22  发布在  其他
关注(0)|答案(6)|浏览(233)

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

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

如果有人能解释什么是NULL参数和其他参数的目的和exec()家族函数的参数的目的,这将是一个很大的帮助我!

6jjcrrmo

6jjcrrmo1#

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

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

最后一个参数 * 必须 * 是(char*)0,否则您有未定义的行为。第一个参数是可执行文件的路径。以下参数出现在已执行程序的argv中。这些参数的列表以(char*)0结束;这就是被调用函数知道最后一个参数已经到达的方式。在它main中,它将具有等于2的argc,其中argv[0]等于"ls",且argv[1]等于"-l"
紧接着这个函数之后,应该是错误处理代码。(execl在返回时总是返回-1,所以不需要测试它。而且它只在出现某种错误时才返回。)

bakd9h0s

bakd9h0s2#

exec函数是变元函数:它们接受可变数量的参数,以便您可以将可变数量的参数传递给命令。这些函数需要使用NULL作为标记来标记参数列表的结束。
在可变参数函数中,有一个循环,它将迭代可变数量的参数。这些循环需要一个终止条件。在某些情况下,例如printf,参数的实际数量可以从另一个参数推断出来。在其他函数中,NULL用于标记列表的结束。
另一个选择是为 number of arguments 添加一个额外的函数参数,但这会使代码更脆弱,需要程序员管理一个额外的参数,而不是简单地总是使用NULL作为最后一个参数。
您还将看到(char *) 0用作标记:

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

xwmevbvl3#

/usr/include/libio.h中,自gcc 2.8起(很久以前)NULL被定义为**null(**保留给内置程序),在此之前,NULL(void *)0,在varargs的情况下,(void *)0(char *)0是无法区分的,因为该类型未被传递,例外的是如果定义了x1M6N1x,在这种情况下x1M7N1x被定义为0。
安全的做法是显式使用(void *)0,它被定义为与任何指针兼容,而不依赖于可能碰巧在标准库中的任何不可靠的#defines,尤其是在您有64位架构的情况下。

v9tzhpje

v9tzhpje4#

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

int execl(const char *path, const char *arg, ...);
  • path*:要调用的程序的位置,要调用的程序。
  • 参数 *,... *:可以视为arg 0、arg 1、...、argn。

在本例中,execl( "/bin/ls", "ls", "-l", (char*)0 );是正确的函数调用。
bin/ls”要调用的程序
ls”程序名
-l”是调用的程序的参数

mnemlml8

mnemlml85#

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

/* 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;

任何以这种方式赋值的标量零值都是false,因此使用0作为最后一个参数将终止循环。
第二个循环形成argc长度的参数数组argv。请注意,每个参数在赋值时都被显式转换为char*。在0作为最后一个参数的情况下,从宏展开得到的argv中的结果值就像你写的那样:

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

同样,这肯定是一个零值,并且在execve的循环条件中将计算为false。不受欢迎的观点,但是使用0作为execl中的最后一个参数在技术上是正确的。您不必强调int (32-bit) -> char* (64-bit)“它的实现可能不会用零填充”转换b.s.,因为va_arg是一个宏-它当场 * 定义 * 一个char*并将0分配给它,这保证了它将具有假逻辑值。

xienkqul

xienkqul6#

“NULL”表示命令的终止。

相关问题