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函數退出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章