先給大家簡單介紹下 fork 函數
1、函數原型:
#include <unistd.h>
pid_t fork(void);
創建一個子進程
3、返回值:
若函數執行失敗,返回 - 1;若執行成功:
(1)、父進程返回子進程的ID(非負)
(2)、子進程返回 0
注: (1)、pid_t 類型表示進程 id ,但為了表示 -1 ,它是有符號整型(0 不是有效進程,init 最小,為 -1)
(2)、並不是 fork 函數有兩個返回值,而是 fork 后,fork 函數變成兩個, 父子進程各自有一個返回值。
4、父子進程的深度理解:
(1)、子進程是父進程的副本,它將獲得父進程的數據空間、棧、堆等資源的副本。父子進程共享代碼段,但是分別擁有自己的數據段和堆棧段。
由於在創建進程時,會消耗大量的系統資源,所以為了可以避免拷貝大量根本就不會被使用的數據(地址空間里常常包含數十兆的數據)。由於 Unix/Linxu 強調進程快速執行的能力,這里就使用了寫 實拷貝技術 ,優化系統資源的使用。
此處先簡要介紹下COW(Copy-on-write)機制,大致原理如下:
(2)、一般來說 fork 后的父進程先執行還是子進程先執行是不確定的。(進程運行的次序是不確定的,取決於內核對的調度算法)。可以使用getpid()和getppid()兩個函數分別獲取當前進程的進程 id 和父進程的 id。
(3)、在Linux中存在緩沖問題。當寫 printf 函,但是沒有換行的話,它是不會輸出的,而是現將要輸出的內容放入緩沖區中,當碰到換行時,將緩沖去的內容再一起輸出。所以 fork 后子進程會復制父進程的緩沖區。因此,也將待輸出內容復制到自己與父進程獨立的緩沖區中。因此,當子進程執行遇到換行,就將緩沖區的內容全都輸出來。
5、示例代碼1:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
pid = fork();
if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}
這里為什么會出現運行上面代碼,輸出會先輸出 父進程,然后輸出子進程,然后光標就在下面一行閃呢?而不是光標跟在 arrayli@ubuntu:~/sysCode/day01/fork/review$
后面呢?
主要是因為當前終端是一個進程,而程序 test_fork1 是一個當前終端下的子進程;然而該子進程有一個子進程,這三個進程之間相互爭奪 CPU 的使用權,所以會出現這種情況。父進程獲取到 CPU 的使用權后,當前終端獲取到了 CPU 的使用權,但是 終端沒有運行完畢,時間片用完了。此時 子進程獲取到了 CPU 的使用權,打印出了子進程的信息。然后當前終端獲取到了 CPU 的使用權,終端繼續向下運行,所以光標會出現這種情況。只是,輸出格式有問題,其余還是和平常一樣。大家可以看着下圖再加以理解下哦哦。
shell 終端進程
6、示例代碼2:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
pid = fork();
if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
sleep(1); // 和程序1 不同的是這里添加上了 系統休眠
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}
本程序為什么輸出格式正常呢?
因為在這里 當父進程獲取到 CPU 的使用權后,我給它休眠了 1s 。此時 子進程就會獲取 CPU 的使用權,執行程序。然后回到終端。
7、示例代碼3:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
int i;
for( i = 0; i < 2; i++ )
{
printf("-");
pid = fork();
}
return 0;
}
輸出:--------
8個中划線
分析:
在整個執行周期內,一共會產生4個進程,由於是遍歷,所以主進程會產生兩個子進程。假設 i=0 時,fork 出的子進程記為: first ; i =1 時,fork 出的子進程記為 : second
first 進程在遍歷的時候會再產生一個自己的子進程,記錄為: third;而 second 進程, i 的值已經為 1 ,不會再繼續執行下去,其他進程同理。
主進程會輸出2個中划線(總計:2個)
first 進程會輸出1個自己的中划線,此時考慮到從主進程緩沖區里面拷貝來的 1個中划線。即,printf 的緩沖區里面 還有一個 “-”的1個中划線(總計:2個)
second 進程沒有自己的中划線輸出,考慮到從主進程緩沖區里拷貝過來的2個中划線(總計:2個)
third 進程沒有自己的中划線輸出,考慮到會從 first 進程緩沖區里拷貝過來的2個中划線(總計:2個)
所以,一共會輸出8個中划線。
8、示例代碼4:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
int i;
for( i = 0; i < 2; i++ )
{
pid = fork();
printf("-");
}
return 0;
}
輸出:--------
8個中划線
解析:
雖然代碼通代碼4一樣,也是輸出8個中划線,但是內部的運行機制卻相差很遠,具體原因,見如下分析。
主進程會輸出2個中划線(總計:2個)
first 進程會輸出2個自己的中划線,無主進程緩沖區拷貝輸出(總計:2個)
second 進程會輸出1個自己的中划線,考慮到會從主進程緩沖區里拷貝過來的1個中划線(總計:2個)
third 進程會輸出1個自己的中划線,考慮到會從first 進程的緩沖區里拷貝過來的1個中划線(總計:2個)
9、示例代碼5:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
int i;
for( i = 0; i < 2; i++ )
{
printf("-\n");
pid = fork();
}
return 0;
}
輸出: -
-
-
3個中划線
解析:
主進程會輸出2個中划線(總計:2個)
first 進程會輸出1個自己的中划線,無主進程緩沖區拷貝輸出(總計:1個)
second 進程沒有自己的中划線輸出,無主進程緩沖區拷貝輸出(總計:0個)
third 進程沒有自己的中畫西安輸出,無 first 進程緩沖區拷貝輸出(總計:0個)
9、示例代碼6:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
int i;
for( i = 0; i < 2; i++ )
{
pid = fork();
printf("-\n");
}
return 0;
}
輸出: -
-
-
-
-
-
6個中划線
解析:
主進程會輸出2個中划線(總計:2個)
first 進程會輸出2個自己的中划線,無主進程緩沖區拷貝輸出(總計:2個)
second 進程會輸出1個自己的中划線,無主進程緩沖區拷貝輸出(總計:1個)
third 會輸出1個自己的中划線輸出,無 first 進程緩沖區拷貝輸出(總計:1個)
10、示例代碼7:
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
pid = fork();
if( pid == -1 )
{
perror("fork");
exit(1);
}
else if( pid == 0)
{
printf(" I am child,pid = %d, ppid = %d\n",getpid(),getppid());
}
else if( pid > 0 )
{
sleep(1);
printf(" I am parent,pid = %d, ppid = %d\n",getpid(),getppid());
}
return 0;
}
小結:
在這里和大家分享了 fork 函數的理解,有不足之處的話,歡迎大家找我交流技術哦哦。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。