Linux中fork和vfork的区别

fork函数

函数原型

 #include   //头文件
 pid_t fork(void); 
 //返回值:成功时父进程返回子进程的id,子进程返回0;失败时父进程返回-1;先返回谁是不确定的,不同平台不一样

我们可以用一段程序来测试fork函数

#include
#include
#include
#include
int main()
{
    int count = 1;
    pid_t pid ;
    pid = fork();
    if(pid < 0)
    {
        perror("fork error:");
        return -1;
    }
    else if(pid == 0)
    {
        count++;
        printf("this is child, pid=%d,count=%d(%p)\n",getpid(),count,&count);
    }
    else
    {
    	wait(NULL);
        printf("this is father, pid=%d,count=%d(%p)\n",getpid(),count,&count);
    }
    return 0;
}

在这里插入图片描述

由运行结果可以看到,父子进程的pid不一样;count值不一样,但是虚拟地址相同。
解释:
1.每个进程被创建都有一个独一无二的标识符,所以父子进程pid不一样。
2.然后在程序中当pid等于0,也就是当运行子进程的时候我们让count++,而父进程不做变化,之后打印出来的两个count值不一样,可以说明父子两个进程是各自拥有自己的数据空间(新版本的fork只复制父进程的页表,与父进程共享虚拟地址空间),也就是他们得数据是不共享的。
3.但是两个count的地址一样(这里打印出来的是虚拟地址),原因在于虚拟地址和计算机内存中物理地址之间是通过MMU和页表联系在一块的,(虚拟地址映射到物理地址的方法不一样)虽然虚拟地址一样,他们映射到内存中的物理地址不一样,打印出来的count值也就不一样。

根据上面的结果我们可以总结fork函数的以下特点:

fork函数的特点

  • fork函数所创建的子进程是父进程的完整副本,复制了父进程的资源,包括内存的task_struct内容。
  • 子进程拥有自己的虚拟地址空间,父子进程数据独有,(写时复制)代码共享。
  • fork函数的返回值起到分流的作用,可以用fork的返回值判断哪个是父进程或子进程。

写时复制(copy-on-write)

内核只为新生成的子进程创建虚拟空间结构,它们复制于父进程的虚拟空间结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应的段的行为发生时,再为子进程相应的段分配物理空间。

Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个地址空间,而是让父进程和子进程共享一个拷贝(物理页面)。只有在需要写入的时候,数据才会复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。这种技术使地址空间的页的拷贝被推迟到实际发生写入的时候。

对于上面的代码来说,当没有执行count++操作时,此时父子进程是共享物理页面的,内核只为子进程分配了虚拟地址空间和唯一的进程标识符,而当执行count++操作后,内核才会为子进程分配物理页面,并且复制父进程的数据。所以当一个子进程没有进行写操作的时候,内核是不会分配物理页面给子进程的,从而节省了内存,并且提高了运行效率。

vfork函数

#include   //头文件
#include  

pid_t vfork(void);
#include
#include
#include
#include
int main()
{
    int count = 1;
    pid_t pid ;
    pid = vfork();
    if(pid < 0)
    {
        perror("fork error:");
        return -1;
    }
    else if(pid == 0)
    {
        count++;
        printf("this is child, pid=%d,count=%d(%p)\n",getpid(),count,&count);
        exit(0);
    }
    else
    {
    	wait(NULL);
        printf("this is father, pid=%d,count=%d(%p)\n",getpid(),count,&count);
    }
    return 0;
}

在这里插入图片描述
结果与fork的结果类似,但是有一个唯一的区别是count值相等,原因是此时父子进程共享页表项。

vfork函数特点

  • vfork函数相比fork函数更加粗暴,内核连子进程的虚拟地址都不创建了,而是直接共享父进程的,从而物理地址也就被理所当然的共享了。
  • 父进程会保证子进程先运行,在子进程调用exec(进程替换)或exit后才可能被调度运行。

fork和vfork的区别

  • fork复制父进程的页表项,当进行写操作时,内核会给子进程分配一个新的物理页面;而vfork与父进程共用页表项,当写操作时,是直接写在父进程的物理页面。

参考资料:
Linux—创建进程(fork和vfork的区别)

《linux内核设计与分析》

你可能感兴趣的:(计算机基础,操作系统,linux,多线程)