【问题标题】:fork vs vfork functionality in a C programC 程序中的 fork 与 vfork 功能
【发布时间】:2015-08-02 19:50:28
【问题描述】:

我正在做一些自学的C练习,遇到了以下问题:

第一部分:

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

    int a = 5, b = 8;                                                     
    int v;                                                                

    v = fork();                                                           
    if(v == 0) {                                                          
        // 10                                                             
        a = a + 5;                                                        
        // 10                                                             
        b = b + 2;                                                        
        exit(0);                                                          
    }                                                                     
    // Parent code                                                        
    wait(NULL);                                                           
    printf("Value of v is %d.\n", v); // line a                           
    printf("Sum is %d.\n", a + b); // line b                              
    exit(0);                                                              

} 

第二部分:

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

    int a = 5, b = 8;                                                     
    int v;                                                                

    v = vfork();                                                               
    if(v == 0) {                                                          
        // a = 10                                                         
        a = a + 5;                                                        
        // b = 6                                                          
        b = b - 2;                                                        
        exit(0);                                                          
    }                                                                     
    // Parent code                                                        
    wait(NULL);                                                           
    printf("Value of v is %d.\n", v); // line a                           
    printf("Sum is %d.\n", a + b); // line b                              
    exit(0);                                                              

}

我们必须比较line aline b 的输出。

a部分的输出是:

Value of v is 79525.
Sum is 13.

b部分的输出是:

Value of v is 79517.
Sum is 16.

在a部分出现,sum是ab的初始声明的总和,而在b部分,sum包括子进程内的总和。

我的问题是 - 为什么会这样?

据此post

两者的基本区别在于,当一个新进程被 使用 vfork() 创建,父进程被暂时挂起,并且 子进程可能会借用父进程的地址空间。这 奇怪的事态一直持续到子进程 退出,或调用 execve(),此时父进程继续。

父进程的定义暂时中止对我来说没有多大意义。这是否意味着对于1b,程序会等到子进程完成运行(因此为什么子进程变量会被求和),然后父进程才会运行?

问题陈述还假设“内核维护的父进程的进程ID为2500,并且新进程是在子进程创建之前由操作系统创建的”。

根据这个定义,两个程序的v 的值是多少?

【问题讨论】:

  • "vfork() 函数应等效于fork(),除非vfork() [...] 创建的进程修改除类型变量之外的任何数据时行为未定义pid_t 用于存储来自vfork() 的返回值” - 这个问题是“我的代码的行为未定义,但它为什么会做这个特殊的 事情?”的另一个实例
  • vfork 之后,父级被挂起,直到子级调用exec* 函数或直到它_exits,以先发生者为准。在常规分叉中,您根本不会重写变量,因为每个进程都有自己的副本。
  • @PSkocik 谢谢你的回答。如果我理解正确,fork() 会生成一个单独的副本,这就是为什么ab 保留它们的初始化值并且不被写入。对于vfork(),子执行,这就是求和发生在子内部的原因。
  • 如果你对vfork感到好奇,它的目的是在fork/exec中使用它来启动一个新程序,但不复制原程序的内存。这在操作系统中很重要,因为操作系统必须复制内存页面而不是将它们标记为 COW。今天,它在配置为禁用内存过量使用的操作系统上仍然很重要。在没有过度使用的 Linux 上,使用 fork/exec 的 2 GB Java 进程将需要 2 GB 可用 RAM。 vfork/exec 不会。
  • 调用系统函数:fork()或vfork()时,有三种结果。 1) 函数失败,在这种情况下返回 -1 2) 函数成功并且父进程正在执行(返回子进程的 pid) 3) 函数成功并且子进程正在执行(返回 0 的 pid) 调用时无论是这些功能,代码都应该检查所有三个条件,而不仅仅是“成功”条件。\

标签: c fork vfork


【解决方案1】:

父进程暂时挂起

基本上,在子进程调用_exitexec 函数之一之前,父进程不会运行。在您的示例中,这意味着子级将运行并因此在父级运行并执行打印之前执行求和。

至于:

我的问题是 - 为什么会这样?

首先,您的 b 部分具有未定义的行为,因为您违反了 vfork 语义。程序的未定义行为意味着程序不会以可预测的方式运行。有关更多详细信息,请参阅this SO post on undefined behavior(它包括一些 C++,但大多数想法是相同的)。来自POSIX specs on vfork

vfork() 函数与 fork(2) 具有相同的效果,不同之处在于 如果 vfork() 创建的进程要么 修改除用于存储的 pid_t 类型变量以外的任何数据 vfork() 的返回值,或从其中的函数返回 vfork() 被调用,或在成功之前调用任何其他函数 调用 _exit(2) 或 exec(3) 系列函数之一。

所以你的 b 部分真的可以做任何事情。但是,您可能会看到 b 部分的输出有些一致。这是因为当您使用vfork 时,您并没有创建新的地址空间。相反,子进程基本上“借用”了父进程的地址空间,通常是为了调用exec 函数之一并创建一个新的程序映像。相反,在您的 b 部分中,您正在使用父地址空间。基本上,在孩子调用exit(这也是无效的,因为它应该调用_exit)之后,a 很可能等于 10,b 很可能等于 6在父级中 .因此,总和为 16,如 b 部分所示。我说最有可能,因为如前所述,该程序具有未定义的行为。

对于使用fork 的部分a,子代有自己的地址空间,其修改在父代中看不到,因此打印的值为13 (5 + 8)。

最后,关于v 的值,这似乎只是问题要说明的问题,以使其显示的输出有意义。 v 的值可以是vforkfork 返回的任何有效值,不必限制为2500。

【讨论】:

  • 这是一个了不起的答案,谢谢。你能描述一下“未定义的行为”是什么意思吗?这是否意味着程序错误?还是缺少信息?
  • 'exit' 函数工作得很好,实际上更好,因为它确实通过关闭文件等进行清理,而 _exit 和 _Exit 函数不关闭文件,不要调用 'atexit()'函数等
  • @user3629249 实际上,规范说调用除 _exit 和 exec 变体之外的任何函数都是未定义的行为,而您刚才所说的原因就是它不好的原因。子进程没有自己的地址空间,因此它将对父进程的内存进行清理,例如刷新真正属于父进程的缓冲区。
猜你喜欢
  • 2021-06-07
  • 1970-01-01
  • 2011-06-18
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 2016-12-11
  • 2020-06-18
  • 1970-01-01
相关资源
最近更新 更多