fork和vfork详解及区别

1.fork函数:Linux环境下,创建进程的主要方法是调用fork函数。Linux下所有的进程都是由init(PID为1)直接或间接创建。
pid_t fork(void); pid_t类型其实就是一个整型
返回值:如果执行成功,在父进程中返回子进程(新创建的进程)的PID,子进程将返回0;如果执行失败,则在子进程中返回-1,错误原因存储在errno中。

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

int n = 0;

int main()
{
  pid_t pid = fork();
  if (pid < 0) {
    printf("create fail\n");
    return 0;
  }
  else if (pid == 0) {
    n++;
    printf("child %d\n", n);
  }
  else {
    n++;
    printf("parent %d\n", n);
  }
  return 0;
}


打印结果:
father 1
child 1
可以看出:fork函数后的代码在子进程中也被执行。实际上,其他代码也在子进程的代码段,只是子进程执行的位置为fork返回位置,之前的代码无法执行。而从上面的n的返回值可以看出,父子进程各有一个自己的pcb。

1.1 fork的子进程对父进程打开的文件描述符的处理
fork函数创建子进程后,子进程将复制父进程的数据段、BSS段、代码段、堆空间、栈空间和文件描述符,而对于文件描述符关联的内核文件表项(struct file 结构),则是采用共享的方式。
在这里插入图片描述

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>

int main()
{
  pid_t pid;
  int fd;
  int status;
  const char* ch1 = "hello";
  const char* ch2 = "worid";
  const char* ch3 = "IN\n";
  if ((fd = open("text01.txt", O_RDWR | O_CREAT, 0644)) == -1) {
    perror("parent open");
  }
  if (write(fd, ch1, strlen(ch1)) == -1) { //父进程向文件中写入数据
    perror("parent write");
  }
  if ((pid = fork()) == -1) {  //创建新进程
    perror("fork");
  }
  else if (pid == 0) {
    if (write(fd, ch2, strlen(ch2)) == -1){ //子进程向文件写入
      perror("child write");
    }
  }
  else {
    sleep(1);    //等待子进程先执行
    if (write(fd, ch3, strlen(ch3)) == -1) { //父进程向文件写入
      perror("parent write");
    }
    wait(&status);
  }
  return 0;
}

打印文件内容:helloworidIN
可以看出:父进程首先创建并打开文件,然后向文件写入ch1的内容,父进程等待一秒(为了让子进程先执行),接着子进程向文件写入ch2的内容,最后父进程再写入ch3的内容。且写入数据不交叉覆盖,说明父子进程共享文件偏移,因此共享文件表项。

2.vfork函数:vfork函数创建新进程时并不复制子进程的地址空间,而是在必要的时候才重新申请新的存储空间。如果子进程执行exec()函数,则使用fork()从父进程复制到子进程的数据空间将不被使用。这样效率非常低,从而使得vfork非常有用,vfork()有时比fork()可以很大程度上提高性能。
pid_t vfork(void);
vfork在子进程环境中返回0,在父进程环境中返回子进程的进程号。

在执行过程中,fork()函数是复制一个父进程的副本,从而拥有自己独立的代码段,数据段以及堆栈空间,即成为一个独立的实体。而vfork是共享父进程的代码以及数据段。

将上面的程序稍微修改fork–>vfork

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
    int n = 0;
    pid_t pid = vfork();
    if (pid < 0) {
          printf("create fail\n");
              return 0;          
    }
    else if (pid == 0) {
          n++;
          printf("child %d\n", n);
          _exit(0);   //使用_exit退出
                
    }
    else {
          n++;
          printf("parent %d\n", n);           
    }
    return 0;
}


打印结果:
child 1
parent 2

如果将子进程处的_exit函数拿掉,会发生未知错误。
child 1
parent 32564
child 1
Segmentation fault
注意: 由于vfork后父子进程共用同一块空间,通常情况下,操作系统会优先执行子进程,如果让子进程先执行然后return掉,那么它会释放栈空间,从而导致父进程执行错误,所以需要exit或_exit函数退出。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值