在函数中调用vfork(),对结果感到困惑

时间:2015-04-05 05:16:42

标签: c unix fork vfork

这是“APUE”第8章(练习8.2,第2版)中的练习。所有的描述都是:

  

回想一下图7.6中典型的内存排列。因为堆栈帧   对应于每个函数调用通常都存储在堆栈中,并且因为之后   vfork子进程在父进程的地址空间中运行,如果调用vfork会发生什么   来自除main之外的函数,并且子函数在此函数之后返回   vfork的?编写一个测试程序来验证这一点,并绘制出正在发生的事情的图片。

在我的计划中:

static void f1(void), f2(void);

int main(void) {
    printf("main address: %d\n", main);
    f1();
    f2();
    _exit(0);
}

static void f1(void) {
    printf("f1 address: %d\n", f1);
    pid_t pid;

    if ((pid = vfork()) < 0)
        err_sys("vfork error");
}

static void f2(void) {
    printf("f2 address: %d\n", f2);
    char buf[1000];
    int i;

    for (i = 0; i < sizeof(buf); ++i)
        buf[i] = 0;
}

我运行程序,输出是:

main address: 4196560
f1 address: 4196604
f2 address: 4196663
f1 address: 4196604
[1]    12929 segmentation fault  ./a.out

我对输出感到困惑。

  1. print f1 address: xxx,我们调用vfork(),子进程首先运行。
  2. print f2 address: xxx,然后子进程调用_exit(0)。
  3. 主要进度从f1()返回,f1的堆栈帧被f2改变,可能导致分段错误。
  4. 但为什么要打印f1 address: 4196604两次以及为什么f1和f2的地址不相同?

2 个答案:

答案 0 :(得分:0)

我不确定你的意思&#34; f1的statck框架由f2&#34;改变了。

f2()中的代码在任何情况下都可能会出现细分错误,无论vfork()如何。 buf未初始化。没有理由相信它包含以null结尾的字符串。因此,对strlen()的调用可以读取缓冲区的末尾。

无论如何,我不确定你期望循环做什么。在第一次迭代中,i为0.如果对strlen()的调用不是段错误,则循环体在buf[0]中存储0。因此,在循环的下一次迭代中,strlen(buf)将为0,i将为1(不小于0),因此循环将终止。

f1 address: 4196604的第二个打印是父进程在vfork() - ed子进程退出后继续。父进程继续并调用f1()来打印它。

您打印的数字是f1f2本身的地址。为什么您希望f1的地址与f2的地址相同?它们不是,所以它们打印不同的地址。

另一方面,f1的地址在父进程和子进程中是相同的,因为子进程共享父进程的地址空间。因此,两次都会为f1打印相同的地址。

答案 1 :(得分:0)

根据vfork documentation,您不应该从当前功能返回。

  

vfork()与fork(2)的不同之处在于调用线程被挂起          直到孩子终止(通常,通过调用_exit(2),或          异常,在发出致命信号后),或拨打电话          的execve(2)。在那之前,孩子与其共享所有记忆          父,包括堆栈。 孩子一定不能从          当前函数或调用exit(3),但可以调用_exit(2)

另外,请注意:

  

vfork()是clone(2)的一个特例。 用于创建新的          进程而不复制父进程的页表。它          可能对孩子所在的性能敏感应用程序很有用          创建然后立即发出execve(2)。

由于vfork不会从父项复制页表,因此从当前函数返回很有意义。当孩子退出时,它将从父母那里弄乱堆栈框架。

您还可以查看following answer