C 語言系統編程:進程的原理介紹和代碼實現

前言

本文我們介紹 C 語言系統編程中關於進程的創建、執行程序和進程結束。我們的進程都會有一個唯一的標識 PID,和我們的身份證一樣是唯一的數字,我們可以根據這個 PID 對進程進行控制,結束、開始、掛起、運行等操作。接下來我們先看一下進程實現需要用到的幾個基本函數。

fork 函數

一個運行的進程可以通過調用 fork 函數創建一個新的進程,由 fork 創建出來的進程我們叫子進程,我, fork 函數被調用一次會返回兩次,子進程返回的值爲 0,父進程返回的是子進程的 PID,爲什麼呢?因爲一個進程可以有很多的子進程但是沒有函數可以獲取子進程的 PID。爲了方便管理子進程所以 fork 會返回給父進程自己創建出來子進程的 PID。那我們回想一下爲什麼我們子進程返回的是 0,理由是我們子進程只會有一個父進程,而父進程的 PID 是可以通過函數 getppid 來獲取的,對於每一個進程想知道自己的 pid 可以使用函數 getpid 來獲取。

子進程與父進程的關係

我們在這裏說一個知識點,虛擬內存和物理內存的關係,我們 32 位系統進程的產生 0-4G(可用)的虛擬內存空間,1-3G 是我們的用戶區,3-4G 是我們的內核區,但是我們有些電腦的內存沒有那麼大,那這時候怎麼辦?我們 CPU 裏面有一個 MMU 模塊可用幫我們把 0-4G 的虛擬內存映射到我們的真實物理內存上,下圖就是把兩個進程 0-4G 的虛擬內存映射到只有 512M 的物理內存條上,映射的時候我們要注意的是,內核段的虛擬內存是共用的,兩個進程的內核區映射到物理內存上是用一個地址,但是我們的用戶區虛擬內存映射到物理內存上是獨立的。

圖片

我們看下邊的圖就會更好的去理解,兩個進的內核段是共用的,那你可能就會問了那我們不會相互影響麼?我們的進程由我們的進程控制模塊 PCB 來控制,每一個進程都有自己的 PCB 控制模塊,裏面存放着自己進程的一些相關的信息,我們的 PCB 實際上是一個結構體,每一個進程一個結構體,那經過 MMU 的映射把這些結構體存放相同的物理內存上也是可以的並不會相互干擾,而且也只有這樣,兩個進程之間才能實現通信。

圖片

執行順序

一般來說我們進程執行是沒有先後順序的,換言之就是子進程和父進程誰先執行並不清楚,這取決於內核使用的跳讀算法,如果我們需要按照我們自己的想法去實現進程順序,那我們可以使用休眠,舉個例子:現在我們不知道父進程還是子進程先執行,我們想讓子進程先執行完了後在執行父進程,那我們的父進程加一個 sleep(2)休眠 2 秒鐘等待,休眠的時候 CPU 就把子進程執行了,等休眠時間到纔開始執行父進程。

實現代碼

#include<unistd.h>
 #include<pthread.h>
 #include<stdio.h>
 #include<stdlib.h>
   int main ()
{
   printf("進程前代碼打印!!!!\n");
   printf("進程前代碼打印!!!!\n");
  printf("進程前代碼打印!!!!\n");
  printf("進程前代碼打印!!!!\n");
  printf("進程前代碼打印!!!!\n");
  //開始創建進程
  pid_t pid=fork();
  if(pid==-1)
  {
  perror("fork error");
  exit(-1);
  }
  else if(pid==0)
  {
    sleep(1);
  printf("子進程創建成功!\n");
  printf("父進程pid:%d\n",getppid());   
  printf("子進程pid:%d\n",getpid());
  }
  else if(pid>0)
 {
   printf("我是父進程!!!\n");
  printf("我創建的子進程pid:%d\n",getpid());
  }
  printf("******進程後的代碼*****\n");
  sleep(2);
  return 0;
  }

結果

圖片

我們看一下沒加 sleep 之前的打印情況,明顯進程執行的順序是雜亂的,然後加上休眠後我們再來看看結果

圖片

很明顯是先執行完父進程然後在執行子進程,我們看一下圈起來的兩個紅圈,我們看一下代碼,代碼 printf("****** 進程後的代碼 *****\n"); 只有一次,但是爲什麼會打印出兩次,子進程打印一次父進程打印一次?

在這裏我們講一個進程的知識點,在父進程 fork 一個子進程後,創建出來的子進程其實是以父進程複製出來的一份,父進程裏面的代碼子進程都有,所以這就爲什麼一個打印語句執行兩次了,那有些讀者可能就會疑問了,那你不是說代碼一模一樣嘛,那爲什麼子進程不打印下面的代碼?

圖片

這是因爲在父進程執行這些語句的時候子進程還沒有被創建,程序是從上往下執行的,創建出的子進程也是往下走,而不是返回去從頭執行,你可以理解爲子進程共享父進程 fork 之後的代碼。

現在你對 C 語言系統編程的進程有一定的瞭解了麼?我們設想一下,如果我父進程聲明一個變量,然後按理說我們 fork 創建的子進程也會拿到這個變量,那如果我們在子進程改變了這個值後會影響到父進程的變量值麼?

如果你不知道我建議你在往上翻一下看一下我剛剛說的內存映射部分再好好理解進程之間的關係。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/TKM8Tygrxb4JGTywzvdU5w