日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

进程及 fork() 系统调用详解

發(fā)布時(shí)間:2024/2/28 windows 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进程及 fork() 系统调用详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

進(jìn)程的概念及定義

定義:進(jìn)程是一個(gè)具有獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的運(yùn)行過(guò)程。它是系統(tǒng)進(jìn)行資源分配和調(diào)度的獨(dú)立單位,是一個(gè)活動(dòng)的實(shí)體。

注意:進(jìn)程的定義不唯一,這個(gè)是我覺(jué)得最準(zhǔn)確的定義。還有一點(diǎn),在沒(méi)有引入線程前,資源分配和調(diào)度的基本單位都是進(jìn)程。有了線程后,進(jìn)程仍然是資源分配的基本單位,而調(diào)度的最小單位是線程。

在多道環(huán)境下,引入進(jìn)程的概念,以便更好地描述和控制程序的并發(fā)執(zhí)行,實(shí)現(xiàn)操作系統(tǒng)的并發(fā)性和共享性(最重要的兩個(gè)特性)。為了使參與并發(fā)的程序能夠獨(dú)立地運(yùn)行,必須要有一個(gè)專(zhuān)門(mén)的數(shù)據(jù)結(jié)構(gòu)——進(jìn)程控制塊(Process Control Block,PCB),系統(tǒng)利用PCB來(lái)描述進(jìn)程的基本情況和運(yùn)行狀態(tài),進(jìn)而控制和管理進(jìn)程。相應(yīng)地,程序段、相關(guān)數(shù)據(jù)段、和PCB三部分構(gòu)成了進(jìn)程實(shí)體(依然是靜態(tài)的,因?yàn)闆](méi)有獲得處理器資源)。創(chuàng)建進(jìn)程,實(shí)質(zhì)上是創(chuàng)建進(jìn)程的PCB;而撤銷(xiāo)進(jìn)程,實(shí)質(zhì)上是撤銷(xiāo)進(jìn)程的PCB。所以PCB是進(jìn)程存在的唯一標(biāo)志。

操作系統(tǒng)中的資源:抽象的理解成時(shí)間和空間。以時(shí)間為例,分時(shí)操作系統(tǒng)中的"時(shí)間片"資源。以空間為例,內(nèi)存空間和一些寄存器空間。

?

進(jìn)程的特征

動(dòng)態(tài)性:進(jìn)程是程序在系統(tǒng)中的執(zhí)行過(guò)程,具有一定的生命周期,進(jìn)程是動(dòng)態(tài)產(chǎn)生,動(dòng)態(tài)消亡的。

并發(fā)性:多個(gè)進(jìn)程實(shí)體同事存在于內(nèi)存中,能在一段時(shí)間內(nèi)同時(shí)運(yùn)行。引入進(jìn)程的目的就是為了是程序能與其他進(jìn)程并發(fā)執(zhí)行,以提過(guò)資源利用率。

獨(dú)立性:進(jìn)程是一個(gè)能獨(dú)立運(yùn)行、獲取資源和接受調(diào)度的基本單位;

異步性由于進(jìn)程間的相互制約,使進(jìn)程具有執(zhí)行的間斷性,即進(jìn)程按各自獨(dú)立的、不可預(yù)知的速度向前推進(jìn)。異步性會(huì)導(dǎo)致執(zhí)行結(jié)果的不可再現(xiàn)性,為此在操作系統(tǒng)中必須采用相應(yīng)的進(jìn)程同步機(jī)制。

結(jié)構(gòu)性:進(jìn)程由程序段、數(shù)據(jù)段和PCB三部分組成。

?

進(jìn)程的狀態(tài)

由于系統(tǒng)中各進(jìn)程之間的相互制約關(guān)系及系統(tǒng)的運(yùn)行環(huán)境的變化,使得進(jìn)程的狀態(tài)也是在不斷地發(fā)生變化。通常進(jìn)程有以下五種狀態(tài),前三種是基本狀態(tài)。

(1)就緒狀態(tài)進(jìn)程已獲得除處理器外的所有資源,等待分配處理器資源,只要分配了處理器進(jìn)程就可執(zhí)行。就緒進(jìn)程可以按多個(gè)優(yōu)先級(jí)來(lái)劃分隊(duì)列。例如,當(dāng)一個(gè)進(jìn)程由于時(shí)間片用完而進(jìn)入就緒狀態(tài)時(shí),排入低優(yōu)先級(jí)隊(duì)列;當(dāng)進(jìn)程由I/O操作完成而進(jìn)入就緒狀態(tài)時(shí),排入高優(yōu)先級(jí)隊(duì)列。

(2)運(yùn)行狀態(tài):進(jìn)程獲得處理器資源;處于此狀態(tài)的進(jìn)程的數(shù)目小于等于處理器的數(shù)目。在沒(méi)有其他進(jìn)程可以執(zhí)行時(shí)(如所有進(jìn)程都在阻塞狀態(tài)),通常會(huì)自動(dòng)執(zhí)行系統(tǒng)的空閑進(jìn)程。

(3)阻塞狀態(tài):由于進(jìn)程等待某種條件(如資源不足、I/O操作或進(jìn)程同步),在條件滿足之前無(wú)法繼續(xù)執(zhí)行。即使處理器資源空閑,該進(jìn)程也無(wú)法運(yùn)行。

(4)創(chuàng)建狀態(tài):進(jìn)程正在被創(chuàng)建,尚未轉(zhuǎn)到就緒態(tài)。創(chuàng)建進(jìn)程通常需要多個(gè)步驟:首先申請(qǐng)一個(gè)空白的PCB,并向PCB中填寫(xiě)一些控制和管理進(jìn)程的信息;然后有系統(tǒng)為該進(jìn)程分配運(yùn)行是所需的資源;最后把該進(jìn)程轉(zhuǎn)入就緒態(tài)。

(5)終止?fàn)顟B(tài):進(jìn)程正從系統(tǒng)中消失,可能是正常結(jié)束或其他原因中斷退出運(yùn)行。進(jìn)程需要結(jié)束運(yùn)行時(shí),系統(tǒng)首先置該進(jìn)程為結(jié)束態(tài),然后再進(jìn)一步處理資源釋放和回收等工作。

?

狀態(tài)轉(zhuǎn)換圖如下:

就緒態(tài) -> 運(yùn)行態(tài):處于就緒態(tài)的進(jìn)程被調(diào)度后,獲取處理器資源,于是進(jìn)程由就緒態(tài)轉(zhuǎn)換為運(yùn)行態(tài)。

運(yùn)行態(tài) -> 就緒態(tài):處于運(yùn)行態(tài)的進(jìn)程在時(shí)間片用完后,讓出處理器,該進(jìn)程有運(yùn)行態(tài)轉(zhuǎn)換為就緒態(tài)。此外,在可剝奪的調(diào)度策略中,當(dāng)有高優(yōu)先級(jí)的進(jìn)程就緒時(shí),調(diào)度程序?qū)⒄趫?zhí)行的進(jìn)程轉(zhuǎn)換為就緒態(tài),讓更高優(yōu)先級(jí)的進(jìn)程執(zhí)行。

運(yùn)行態(tài) -> 阻塞態(tài):進(jìn)程請(qǐng)求某一資源的使用、分配或等待某一件事發(fā)生時(shí),它就從運(yùn)行態(tài)轉(zhuǎn)換為阻塞態(tài)。

阻塞態(tài) -> 就緒態(tài):進(jìn)程等待的事件到來(lái)時(shí),如I/O操作結(jié)束或中斷結(jié)束時(shí),中斷處理程序必須把相應(yīng)的狀態(tài)有阻塞態(tài)轉(zhuǎn)換為就緒態(tài)。

分辨運(yùn)行態(tài)是轉(zhuǎn)換成就緒態(tài)還是阻塞態(tài),只需要看此時(shí)該進(jìn)程所占有的資源的情況。若該進(jìn)程的資源沒(méi)有被剝奪,也沒(méi)有申請(qǐng)新的資源而沒(méi)有得到,則處于就緒態(tài)。反之處于阻塞態(tài)。

?

?

fork() 的使用

fork() 是 UNIX 或 類(lèi)UNIX 中的分叉函數(shù)。fork 系統(tǒng)調(diào)用用于創(chuàng)建一個(gè)新進(jìn)程,稱(chēng)為子進(jìn)程(子進(jìn)程拷貝了父進(jìn)程的資源),它與父進(jìn)程(調(diào)用 fork 的進(jìn)程)同時(shí)運(yùn)行。創(chuàng)建新的子進(jìn)程后,兩個(gè)進(jìn)程將執(zhí)行 fork() 系統(tǒng)調(diào)用之后的下一條指令。子進(jìn)程使用相同的 PC (程序計(jì)數(shù)器),相同的 CPU 寄存器,在父進(jìn)程中使用的相同打開(kāi)文件。fork() 函數(shù)原型如下:

pid_t fork(?void); pid_t 是一個(gè)宏定義,其實(shí)質(zhì)是 int,被定義在#include <sys/types.h> 中。

它不需要參數(shù)并返回一個(gè)整數(shù)值。下面是 fork() 返回的不同值:

負(fù)值:創(chuàng)建子進(jìn)程失敗。原因:進(jìn)程總數(shù)超出了限制。

0:返回到新創(chuàng)建的子進(jìn)程。

正值:返回父進(jìn)程或調(diào)用者。該值是新創(chuàng)建的子進(jìn)程的進(jìn)程 ID。

fork() 函數(shù)被調(diào)用一次,但返回兩次。兩次返回的區(qū)別是:子進(jìn)程的返回值是 0,而父進(jìn)程的返回值是新建子進(jìn)程的 ID。將子進(jìn)程 ID 返回給父進(jìn)程的理由是:因?yàn)橐粋€(gè)進(jìn)程的子進(jìn)程可以有多個(gè),并且沒(méi)有一個(gè)函數(shù)使一個(gè)進(jìn)程可以獲得其所有子進(jìn)程的進(jìn)程 ID。 fork 使子進(jìn)程返回值為 0 的理由是:一個(gè)進(jìn)程只會(huì)有一個(gè)父進(jìn)程,所以子進(jìn)程總是可以調(diào)用 getppid() 以獲得父進(jìn)程的進(jìn)程 ID。編程時(shí),我們可以通過(guò) fork() 返回的值來(lái)判斷當(dāng)前進(jìn)程是子進(jìn)程還是父進(jìn)程。

注意:進(jìn)程 ID 0 總是由內(nèi)核交換進(jìn)程使用,所以一個(gè)子進(jìn)程的進(jìn)程 ID 不可能為 0。

?

為什么 fork 會(huì)返回兩次?

由于在復(fù)制時(shí)復(fù)制了父進(jìn)程堆棧段,所以?xún)蓚€(gè)進(jìn)程都停留在 fork() 函數(shù)中,等待返回。因此 fork() 函數(shù)會(huì)返回兩次,一次是在父進(jìn)程中返回,另一次是在子進(jìn)程中返回,所以這兩次的返回值是不一樣的。

?

利用 fork() 函數(shù)創(chuàng)建子進(jìn)程:

#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h>int main() {pid_t pid;int count = 0;pid = fork();if(pid < 0)printf("error in fork!\n");else if(pid == 0){count++;printf("I am the child process,ID is:[%d] , count is:[%d]\n", getpid(), count);} // getpid返回當(dāng)前進(jìn)程標(biāo)識(shí),getppid返回父進(jìn)程標(biāo)識(shí)。else{ count++;printf("I am the parent process,ID is:[%d], count is:[%d]\n", getpid(), count);}return 0; }

從上述代碼中可以看出,父子進(jìn)程的 count 是相互獨(dú)立的變量,這是因?yàn)樽舆M(jìn)程拷貝了 count,而不是和父進(jìn)程共用同一個(gè) count 。接下來(lái)聲明不同的變量或常量,用于驗(yàn)證 fork() 資源拷貝的問(wèn)題(棧區(qū)變量、堆區(qū)變量及數(shù)據(jù)區(qū)的常量、變量)。

代碼如下:

#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h>int a = 1; //初始化的全局變量 int asd[5] = {1,2,3,4,5}; int main() {pid_t pid;pid = fork();int count1 = 1; //棧區(qū)變量count1static int count2 = 1; //靜態(tài)變量count2char s1[] = "abc"; //棧區(qū)char *s2 = "qwe"; //s2在棧區(qū),而內(nèi)容在常量區(qū)char *p1 = (char*)malloc(10); //堆區(qū)if(pid < 0)printf("error in fork!\n");else if(pid == 0){a++; count1++; count2++;printf("I am the child process,ID is:[%d]\n", getpid());printf("a is:[%d] address:[%p], count1 is:[%d] address:[%p], count2 is:[%d] address:[%p]\n", a, &a, count1, &count1, count2, &count2);printf("asd's address:[%p], s1's address:[%p], s2's address:[%p], p1's address[%p]\n", asd, s1, s2, p1);s1[0] = 'A';printf("%s\n", s1);int childasd = 0;printf("childasd's address:[%p]\n", &childasd);printf("\n");}else{ a -= 2; count1 -=2 ; count2 -= 2;printf("I am the parent process,ID is:[%d]\n", getpid());printf("a is:[%d] address:[%p], count1 is:[%d] address:[%p], count2 is:[%d] address:[%p]\n", a, &a, count1, &count1, count2, &count2);printf("asd's address:[%p], s1's address:[%p], s2's address:[%p], p1's address[%p]\n", asd, s1, s2, p1);s1[0] = 'B';printf("%s\n", s1);int parentasd = 0;printf("childasd's address:[%p]\n", &parentasd);printf("\n");}free(p1);return 0; }

當(dāng)看到父子進(jìn)程的變量的地址都是一樣的時(shí)候,你是否感到迷惑?其實(shí)不用迷惑,這是因?yàn)樽舆M(jìn)程和父進(jìn)程執(zhí)行 fork 調(diào)用之后的指令。子進(jìn)程是父進(jìn)程的副本。例如,子進(jìn)程獲得父進(jìn)程數(shù)據(jù)空間、堆和棧的副本。注意,這是子進(jìn)程所擁有的副本。父進(jìn)程和子進(jìn)程并不共享這些存儲(chǔ)空間,父子進(jìn)程共享的是代碼段。

還有一點(diǎn):這里打印出來(lái)的地址是邏輯地址而不是物理地址,子進(jìn)程拷貝父進(jìn)程的變量,其地址總是一樣的,因?yàn)樵?fork 時(shí)整個(gè)虛擬地址空間被復(fù)制,而物理內(nèi)存沒(méi)有復(fù)制。上面的基本概念已經(jīng)提到過(guò)?PCB 是進(jìn)程存在的唯一標(biāo)志,父子進(jìn)程擁有各自的進(jìn)程 ID (若不考慮誰(shuí)創(chuàng)建了誰(shuí),它們就是兩個(gè)進(jìn)程,而進(jìn)程間的邏輯地址相同是互不影響的)。當(dāng)它們運(yùn)行的時(shí)候,會(huì)將邏輯地址轉(zhuǎn)換成物理地址。這就是為什么父子進(jìn)程打印出來(lái)的地址是一樣的,但還是互不影響的完成了對(duì)數(shù)據(jù)的操作。

現(xiàn)在很多實(shí)現(xiàn)并不是執(zhí)行一個(gè)父進(jìn)程數(shù)據(jù)段、棧和堆的完全副本。取而代之的是使用寫(xiě)時(shí)復(fù)制(Copy-On-Write,COW)技術(shù)。這些區(qū)域由父進(jìn)程和子進(jìn)程共享,而且內(nèi)核將它們的訪問(wèn)權(quán)限改變?yōu)橹蛔x。如果父進(jìn)程和子進(jìn)程中的任一個(gè)試圖修改這些區(qū)域,則內(nèi)核只為修改區(qū)域的那塊內(nèi)存制作一個(gè)副本,通常是虛擬存儲(chǔ)系統(tǒng)中的一"頁(yè)"

?

父子進(jìn)程之間的區(qū)別具體如下:

  • (1) fork 的返回值不同。
  • (2) 進(jìn)程 ID 不同。
  • (3) 子進(jìn)程的 tms_utime 、tms_stime 、tms_cutime 和 tms_ustime 的值設(shè)置為 0。
  • (4) 子進(jìn)程不繼承父進(jìn)程設(shè)置的文件鎖。
  • (5) 子進(jìn)程的未處理鬧鐘被清除。
  • (6) 子進(jìn)程的未處理信號(hào)集設(shè)置為空。

?

fork() 的用法:

(1)一個(gè)父進(jìn)程希望復(fù)制自己,是父進(jìn)程的子進(jìn)程同時(shí)執(zhí)行不同的代碼段。這在網(wǎng)絡(luò)服務(wù)進(jìn)程中是最常見(jiàn)的——父進(jìn)程等待客戶端的服務(wù)請(qǐng)求。當(dāng)這種請(qǐng)求到達(dá)時(shí),父進(jìn)程調(diào)用fork,使子進(jìn)程處理此請(qǐng)求,父進(jìn)程則繼續(xù)等待下一個(gè)服務(wù)請(qǐng)求。

(2)一個(gè)進(jìn)程要執(zhí)行一個(gè)不同的程序。這對(duì)shell是最常見(jiàn)的情況。在這種情況下,子進(jìn)程從fork返回后立即調(diào)用exec。

?

參考:百度百科、《UNIX環(huán)境高級(jí)編程》

總結(jié)

以上是生活随笔為你收集整理的进程及 fork() 系统调用详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。