【问题标题】:UNIX: Calling exec on the parent process after a forkUNIX:分叉后在父进程上调用 exec
【发布时间】:2010-09-25 05:12:13
【问题描述】:

我正在用我的语法,在 LEX 中编写一些代码来 fork() 我的进程并运行一个子进程。孩子实际上是从父母那里得到一些输入,然后返回一个结果。

我需要在加载父级的同一个二进制文件上调用 exec,但我遇到了问题。我知道 exec 并不意味着完全有意义,但我这样做是因为我在 LEX 中有一些以前的语法,我只是想摆脱它,因此重新加载过程更容易。

在 fork() 之后,我在孩子中有以下代码:

char *path = strdup(getenv("PWD"));
size_t size = strlen(path) + strlen("/shell") + 1;
path = (char *) realloc(path, sizeof(char) * size);
path = strcat(path, "/shell");

// call exec
execl(path, NULL);

此代码的问题在于,如果进程从同一目录启动,它可以工作,但如果我尝试从该目录中的文件夹加载,例如 ../shell,那么路径实际上是错误的,它将包含此目录。

我想知道如何获得进程的正确路径,如果有办法也可以获得进程的实际名称吗?我查看了环境变量,但没有发现任何有用的东西。

非常感谢,

杰瑞

【问题讨论】:

  • 请注意,您显示的对execl() 的调用不会为被调用程序提供argv[0](或者,更准确地说,它将空指针提供为argv[0] - 如果NULL 定义为@ 987654325@; 否则它给出一个零整数,这在 64 位机器上可能不够大)。为了安全起见,您应该使用execl(path, path, (char *)NULL);

标签: unix exec fork


【解决方案1】:

据我了解,目标是让子进程重新执行代表父进程的相同程序。

在没有使用来自 /proc 文件系统的数据的技巧的情况下,我认为没有完全可靠的方法来做到这一点。

通常,您依靠argv[0] 的值足以找到程序,并使用execvp() 通过$PATH 找到程序。

尝试 2 - 可能不那么混乱

如果您不介意,我会改写[问题]:现在,execl() 确实执行二进制文件,但前提是我直接从其文件夹加载父级。如果我进入一个子文件夹,我们称之为 fd,那么路径将是“correctPath/fd/shell”而不是“correctPath/shell”。问题似乎是调用 getenv("PWD") 来查找路径并不总是正确的。

所以目标是加载相同的进程或二进制文件;假设二进制文件称为“shell”。问题是找到路径。我展示的代码适用于从它本身的文件夹加载(父)外壳的情况,否则它不起作用。我怀疑调用 getenv("PWD") 是不对的,但我不确定还能调用什么。

您说得对,使用getenv("PWD")getcwd() 通常不正确。

char *arg0 = 0;

int main(int argc, char **argv)
{
    ...declarations...

    arg0 = argv[0];

    ...actions...
}

因此,主程序将其argv[0] 的值存储在全局变量arg0 中,以使其可用于进程的其他部分 - 特别是,将(重新)运行命令的代码.

如果使用“./shell”在可执行文件所在的同一目录中调用程序,则argv[0](因此arg0)将包含该路径名。如果它是使用依赖 $PATH 来定位程序的“shell”执行的,那么argv[0] 将只包含“shell”或“shell”的绝对路径名(不太常见)。

如果程序是从fd 子目录调用的,它可能被调用为“../shell”,或者它可能被调用为“/absolute/path/to/shell”,或者它可能被调用为“ shell”依赖 $PATH 来查找可执行文件。同样,在任何这些情况下,arg0 中的值都是最初调用程序的名称,或者与其等效。

唯一失败的情况是有人故意混淆可执行文件,(幸运的是)这种情况很少发生。

所以,在子代码中,你可以使用:

char *args[] = { arg0, 0 };
execvp(arg0, args);

按照最初执行的方式重新执行命令(除了最初传递的任何辅助参数)。

尝试 1 - 有点混乱

假设argv[0] 的值可通过变量char *arg0 获得,那么只需:

char *args[] = { arg0, 0 };
execvp(arg0, args);

如果你必须使用execl(),那么你需要:

execl(arg0, arg0, (char *)0);

演员表是必要的; execl() 是一个可变参数列表函数,如果你写 0,它将被转换为 int,这在 int 为 32 位且指针为 64 位的 64 位系统上将失败。但是,如果arg0 不代表可执行文件的路径(相对或绝对),那将失败。然后,您必须决定如果execl() 返回时该怎么办 - 您可以放弃或通过$PATH 搜索该程序,但在这种情况下,为什么不首先使用execvp() 来拯救自己痛。

【讨论】:

  • 对不起,我对您的回答感到困惑。如果您不介意,我会改写:现在, execl 确实执行二进制文件,但前提是我直接从它的文件夹加载父级。如果我进入一个子文件夹,我们称之为 fd,那么路径将是“correctPath/fd/shell”而不是“correctPath/shell”。问题似乎是调用 getenv("PWD") 来查找路径并不总是正确的。
  • 不,没关系,我可以重新解释,直到你明白为止,我能理解它怎么可能不清楚。所以目标是加载相同的进程或二进制文件,假设二进制文件称为“shell”。问题是找到路径。我展示的代码适用于从它本身的文件夹加载(父)外壳的情况,否则它不起作用。我怀疑调用 getenv("PWD") 是不对的,但我不确定还能调用什么。
  • 啊,我想我现在明白你的意思了,谢谢!我会试一试,告诉你它是否有效。
  • 非常感谢!你解决了我的问题!我现在得到了您的解决方案,它比我一直拥有和工作的要简单得多。完美的!谢谢。
  • 感谢您抽出宝贵时间重新整理您的回复!
猜你喜欢
  • 2012-11-30
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 2018-10-10
  • 2016-10-08
  • 2012-02-19
相关资源
最近更新 更多