linux中fork和vfork

发布时间:2024年01月22日

fork

fork是用于创建新进程的系统调用之一,它会创建一个与当前进程完全相同的子进程。

子进程将复制父进程的所有资源,包括代码段、数据段、堆栈、文件描述符等。父进程和子进程之间的执行是并发的,它们在不同的地址空间中运行,通过返回值来区分父进程和子进程。

vfork

vfork也是用于创建新进程的系统调用之一,它会创建一个新进程,但子进程会与父进程共享地址空间。

这意味着子进程使用父进程的地址空间,而不是创建自己的副本。子进程与父进程共享代码段、数据段和堆栈,因此父进程和子进程执行的顺序不确定,直到子进程调用 exec 系列函数或 _exit 终止进程。

基础原理

fork()的基础原理

fork() 的实现非常复杂,但它的基本原理是通过操作系统的机制来创建一个新的进程。

当调用 fork() 时,操作系统创建一个当前进程的副本,创建了一个新的进程控制块(PCB)来管理新进程的状态。

父进程的代码段、数据段和堆栈等资源都会被复制到子进程,以便子进程能够继续执行和修改它们。子进程得到的返回值是0,而父进程得到的返回值是子进程的进程ID。

vfork的基础原理

vfork 的实现原理与 fork() 有所不同。

vfork的关键点是共享父进程的地址空间,它通过暂时挂起父进程,使得子进程能够在父进程的地址空间中执行。

当调用 vfork时,操作系统会创建一个新的进程控制块(PCB)和页表,但不会为子进程创建新的地址空间。相反,子进程使用父进程的地址空间。

通过这种方式,可以避免复制父进程的地址空间,提高了性能。但是,由于子进程与父进程共享地址空间,需要谨慎处理共享资源,以免出现竞争条件和错误结果。

综上所述,fork() 和 vfork() 在功能和基础原理上有明显的区别。

fork创建一个与父进程完全独立的子进程,而 vfork() 创建一个与父进程共享地址空间的子进程。这两个函数在使用时需要考虑到它们的特性和限制,并根据具体情况选择合适的函数。

在现代系统中,fork() 更为常见和推荐使用,而 vfork() 往往用于特殊场景,例如在创建新进程后立即调用 exec 函数来替换地址空间。

特点
我们汇总一下fork函数和vfork函数的特点

函数特点
fork1、使用写时拷贝技术 2、子进程创建之后父子进程执行的顺序具有不确定性
vfork1、直接共享父进程的地址空间和数据 2、子进程创建之后可以确定子进程先执行,当它调用exit退出或者调用exec函数执行新程序后父进程才可能被调度

简单代码示例

fork示例

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>


int main(int argc, char **argv)
{
        int count = 0;
        pid_t pid;
        int wstatus;
        char *p = NULL;
        time_t current_time;

        pid = fork();
        if(pid < 0)
        {
                printf("creat a child fail");
                return -1;
        }
        else if(pid == 0)
        {
                count++;
                printf("child's id:%d,[%p]count:%d\n", (int)getpid(),&count,count);
                if(execlp("sleep", "sleep", "16", NULL) < 0)
                {
                        perror("exec a new program fail");
                        exit(1);
                }
                //sleep(16);
                exit(0);
        }
        else
        {
                printf("father's id:%d,[%p]count:%d\n",(int)getpid(), &count, count);
                current_time = time(NULL);
                printf("current time is:%s\n", ctime(&current_time));
                pid = waitpid(pid, &wstatus, 0);
                current_time = time(NULL);
                printf("current time is:%s\n", ctime(&current_time));
                if((int)pid < 0)
                {
                        printf("wait child fail\n");
                        return -1;
                }

                exit(0);
        }
}
hhh@ubuntu:~/tmp$ ./a.out
father's id:3066,[0x7ffe645b021c]count:0
current time is:Thu Jan 18 15:46:37 2024

child's id:3067,[0x7ffe645b021c]count:1
current time is:Thu Jan 18 15:46:53 2024

分析:由于父子进程count变量的地址相同,所以可以确定子进程复制了父进程的虚拟空间;而且count的值不同,可以确定子进程通过写时拷贝拥有了自己的物理空间数据。
另外,父子进程的执行顺序具有不确定性,从实际测试来看一般是父进程先执行。

vfork示例

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>


int main(int argc, char **argv)
{
        int count = 0;
        pid_t pid;
        int wstatus;
        char *p = NULL;
        time_t current_time;

        pid = vfork();
        if(pid < 0)
        {
                printf("creat a child fail");
                return -1;
        }
        else if(pid == 0)
        {
                count++;
                printf("child's id:%d,[%p]count:%d\n", (int)getpid(),&count,count);
                if(execlp("sleep", "sleep", "16", NULL) < 0)
                {
                        perror("exec a new program fail");
                        exit(1);
                }
                //sleep(16);
                exit(0);
        }
        else
        {
                printf("father's id:%d,[%p]count:%d\n",(int)getpid(), &count, count);
                current_time = time(NULL);
                printf("current time is:%s\n", ctime(&current_time));
                pid = waitpid(pid, &wstatus, 0);
                current_time = time(NULL);
                printf("current time is:%s\n", ctime(&current_time));
                if((int)pid < 0)
                {
                        printf("wait child fail\n");
                        return -1;
                }

                exit(0);
        }
}
hhh@wyl-ubuntu:~/tmp$ ./a.out
child's id:4634,[0x7fff5515fa2c]count:1
father's id:4633,[0x7fff5515fa2c]count:1
current time is:Thu Jan 18 15:50:57 2024

current time is:Thu Jan 18 15:51:13 2024

分析:
从结果来看,可以确定子进程先执行,父进程后执行;count变量的地址和值都相同,可以确定子进程共享了父进程的地址空间和数据。子进程执行完新程序ps后退出了,然后父进程执行最后退出。

文章来源:https://blog.csdn.net/weixin_43412488/article/details/135676318
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。