目錄
前言?
?01 fork()創建子進程
示例 1使用 fork()創建子進程。
02 fork創建新進程時發生了什么事?
?2.1 父、子進程中對應的文件描述符指向了相同的文件表?
前言?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
🎬 個人主頁:@ChenPi
🐻推薦專欄1: 《Linux C應用編程(概念類)_@ChenPi的博客-CSDN博客》????
🔥 推薦專欄2: 《C++_@ChenPi的博客-CSDN博客》???
🛸推薦專欄3: ??????《鏈表_@ChenPi的博客-CSDN博客 》 ???
🌺本篇簡介 ?:? 上一章我們講了Linux進程的概念以及獲取進程ID號和獲取父進程的ID? ? ? ? ? ? ? ? ? ? ? ? ? 這一章我們引進新的概念,父子進程
Linux 是一個多用戶多任務的操作系統,每個用戶可以同時運行多個程序
進程是程序運行的主體,包括進程的創建,調度和消亡的整個過程
當用戶執行一個指令或者啟動一個程序時,就創建了一個進程
一個運行的程序也可能有多個進程。
每個進程將被分配各種資源 ? ?
?01 fork()創建子進程
一個現有的進程可以調用 fork()函數創建一個新的進程,
調用 fork()函數的進程稱為父進程,
由 fork()函 數創建出來的進程被稱為子進程(child process),
fork()函數原型如下所示(fork()為系統調用):
2.1 fork()函數原型:
#include <unistd.h>?pid_t fork(void);?
返回值:?
- 失敗返回-1 不創建子進程,并設置 errno?
- 父進程的返回值pid為子進程的pid號,子進程的返回值為0
在諸多的應用中,創建多個進程是任務分解時行之有效的方法
- 譬如,某一網絡服務器進程可在監聽客 戶端請求的同時,為處理每一個請求事件而創建一個新的子進程,與此同時,服務器進程會繼續監聽更多的 客戶端連接請求
- 在一個大型的應用程序任務中,創建子進程通常會簡化應用程序的設計,同時提高了系統 的并發性(即同時能夠處理更多的任務或請求多個進程在宏觀上實現同時運行)。
理解 fork()系統調用的關鍵在于,完成對其調用后將存在兩個進程,
一個是原進程(父進程)、另一個 則是創建出來的子進程,
并且每個進程都會從 fork()函數的返回處繼續執行,會導致調用 fork()返回兩次值,
子進程返回一個值、父進程返回一個值,所以fork返回值后面的代碼塊也會調用兩次
在程序代碼中,可通過返回值來區分是子進程還是父進程。
fork()調用成功后,將會在父進程中返回子進程的 PID,而在子進程中返回值是 0;
如果調用失敗,父進 程返回值-1,不創建子進程,并設置 errno。
fork()調用成功后,子進程和父進程會繼續執行 fork()調用之后的指令,子進程、父進程各自在自己的進程空間中運行。
事實上,子進程是父進程的一個副本
- 譬如子進程拷貝了父進程的數據段、堆、棧以及繼承 了父進程打開的文件描述符
- 父進程與子進程并不共享這些存儲空間
- 這是子進程對父進程相應部分存儲 空間的完全復制
- 執行 fork()之后,每個進程均可修改各自的棧數據以及堆段中的變量,而并不影響另一個進程
示例 1使用 fork()創建子進程。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main()
{int pid = ?fork(); //父進程的返回值pid為子進程的pid號,子進程的返回值為0switch (pid){case -1:printf("fork error\n");break;case 0:printf("this is child ;PID = %d\n",getpid());break;default:printf("this is father;PID = %d\n",getpid());break;}return 0;
}
?
?從打印結果可知,fork()之后的語句被執行了兩次,所以 switch…case 語句被執行了兩次
- 第一次進入 到了"case 0"分支,表示進入子進程
- 第二次進入到了 default 分支,表示當前處于父進程
- 父進程的返回值pid為子進程的id號,子進程的返回值為0?
02 fork創建新進程時發生了什么事?
調用 fork()函數之后,子進程會獲得父進程所有文件描述符的副本
這些副本的創建方式類似于 dup(), 這也意味著父、子進程對應的文件描述符均指向相同的文件表,如下圖所示:
由此可知
子進程拷貝了父進程的文件描述符表,使得父、子進程中對應的文件描述符指向了相同的文件表
也意味著父、子進程中對應的文件描述符指向了磁盤中相同的文件,
因而這些文件在父、子進程間實 現了共享,
譬如,如果子進程更新了文件偏移量,那么這個改變也會影響到父進程中相應文件描述符的位置 偏移量。 ?
?2.1 父、子進程中對應的文件描述符指向了相同的文件表?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define MY_FILE "./file.txt"int main()
{char buf [128];int fd = open(MY_FILE,O_RDWR|O_CREAT,0600);if(-1 == fd){perror("open error? :");exit(-1);}int pid = fork();switch (pid){case -1:printf("fork error\n");break;case 0:for(int i = 0;i<4;i++)write(fd," child",strlen(" child"));close(fd);break;default:for(int i = 0;i<4;i++)write(fd," father",strlen(" father"));close(fd);break;}return 0;
}
上述代碼中,父進程 open 打開文件之后,才調用 fork()創建了子進程,
所以子進程了繼承了父進程打 開的文件描述符 fd,我們需要驗證的便是兩個進程對文件的寫入操作是分別各自寫入、還是每次都在文件 末尾接續寫入
有上述測試結果可知,此種情況下
父、子進程分別對同一個文件進行寫入操作,結果是接續寫
不管是父進程,還是子進程,在每次寫入時都是從文件的末尾寫入很像使用了 O_APPEND 標志的效果。
其原 因也非常簡單
子進程繼承了父進程的文件描述符,兩個文件描述符都指向了一個相同的文件表,意味著它們的文件偏移量是同一個、綁定在了一起,相互影響,子進程改變了文件的位置 偏移量就會作用到父進程,同理,父進程改變了文件的位置偏移量就會作用到子進程
但是執行結果不一定是父進程先執行或者子進程先執行,這個不一定的,這個要看系統的調度了?
?