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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux 系统应用编程——进程基础

發(fā)布時間:2023/12/9 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 系统应用编程——进程基础 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、Linux下多任務(wù)機制的介紹

???????? Linux有一特性是多任務(wù),多任務(wù)處理是指用戶可以在同一時間內(nèi)運行多個應(yīng)用程序,每個正在執(zhí)行的應(yīng)用程序被稱為一個任務(wù)。

?????????多任務(wù)操作系統(tǒng)使用某種調(diào)度(shedule)策略(由內(nèi)核來執(zhí)行)支持多個任務(wù)并發(fā)執(zhí)行。事實上,(單核)處理器在某一時刻只能執(zhí)行一個任務(wù)。每個任務(wù)創(chuàng)建時被分配時間片(幾十到上百毫秒),任務(wù)執(zhí)行(占用CPU)時,時間片遞減。操作系統(tǒng)會在當(dāng)前任務(wù)的時間片用完時調(diào)度執(zhí)行其他任務(wù)。由于任務(wù)會頻繁地切換執(zhí)行,因此給用戶多個任務(wù)運行的感覺。所以可以說,多任務(wù)由“時間片 + 輪換”來實現(xiàn)。多任務(wù)操作系統(tǒng)通常有三個基本概念:任務(wù)、進程和線程,現(xiàn)在,我們先學(xué)習(xí)進程:

?

進程的基本概念

????????進程是指一個具有獨立功能的程序在某個數(shù)據(jù)集合上的動態(tài)執(zhí)行過程,它是操作系統(tǒng)進行資源分配和調(diào)度的基本單元。簡單的說,進程是一個程序的一次執(zhí)行的過程。

??????? 進程具有并發(fā)性、動態(tài)性、交互性和獨立性等主要特性。

????????進程和程序是有本質(zhì)區(qū)別的:

1)程序( program )是一段靜態(tài)的代碼,是保存在非易失性存儲器(磁盤)上的指令和數(shù)據(jù)的有序集合,沒有任何執(zhí)行的概念;

2)進程( process )是一個動態(tài)的概念,它是程序的一次執(zhí)行過程(在RAM上執(zhí)行),包括了動態(tài)創(chuàng)建、調(diào)度、執(zhí)行和消亡的整個過程,它是程序執(zhí)行和資源管理的最小單位。

這里,我們可以看到,進程由兩部分組成:內(nèi)存地址空間 + task_struct ,task_struct 下面我們會講到,內(nèi)存地址空間就是我們程序在內(nèi)存中運行(進程)時所開辟的4GB虛擬地址空間,用于存放代碼段、數(shù)據(jù)段、堆、棧等;

從操作系統(tǒng)的角度看,進程是程序執(zhí)行時相關(guān)資源的總稱。當(dāng)進程結(jié)束時,所有資源被操作系統(tǒng)回收。

???????? Linux系統(tǒng)中主要包括下面幾種類型的過程:

1)交互式進程;

2)批處理進程;

3)守護進程;

?

Linux下的進程結(jié)構(gòu)

?????? 進程不但包括程序的指令和數(shù)據(jù),而且包括程序計數(shù)器和處理器的所有寄存器以及存儲臨時數(shù)據(jù)的進程堆棧。

?????? 因為Linux是一個多任務(wù)的操作系統(tǒng),所以其他的進程必須等到操作系統(tǒng)將處理器的使用權(quán)分配給自己之后才能運行。當(dāng)正在運行的進程需要等待其他的系統(tǒng)資源時,Linux內(nèi)核將取得處理器的控制權(quán),按照某種調(diào)度算法將處理器分配給某個等待執(zhí)行的進程。

??????? 在上面介紹程序和進程的區(qū)別時,我們看到進程除了內(nèi)存地址空間以外,還有個結(jié)構(gòu)體task_struct內(nèi)核將所有進程存放在雙向循環(huán)鏈表(進程鏈表)中,鏈表的每一項就是這個結(jié)構(gòu)體task_struct,稱為進程控制塊的結(jié)構(gòu)。該結(jié)構(gòu)包含了與一個進程相關(guān)的所有信息,在linux內(nèi)核目錄下<include / Linux / sched.h>文件中定義。task_struct內(nèi)核結(jié)構(gòu)比較大,它能完整地描述一個進程,如進程的狀態(tài)、進程的基本信息、進程標(biāo)示符、內(nèi)存的相關(guān)信息、父進程相關(guān)信息、與進程相關(guān)的終端信息、當(dāng)前工作目錄、打開的文件信息,所接收的信號信息等。

???????

下面詳細講解task_struct結(jié)構(gòu)中最為重要的兩個域:stat (進程狀態(tài)) 和 pid (進程標(biāo)示符)。

1、進程狀態(tài)

Linux中的進程有以下幾種主要狀態(tài):運行狀態(tài)、可中斷的阻塞狀態(tài)、不可中斷的阻塞狀態(tài)、暫停狀態(tài)、僵死狀態(tài)、消亡狀態(tài),它們之間的轉(zhuǎn)換關(guān)系如下:


1)運行態(tài)(TASK_RUNNING):進程當(dāng)前正在運行,或者正在運行隊列中等待調(diào)度(排隊中);

2)等待態(tài)_可中斷(TASK_INTERRUPTIBLE):進程處于阻塞(睡眠sleep,主動放棄CPU)狀態(tài),正在等待某些事件發(fā)生或能夠占用某些資源。處于這種狀態(tài)下的進程可以被信號中斷。接收到信號或被顯式地喚醒呼叫(如調(diào)用wake_up系列宏:wake_up、wake_up_interruptible等)喚醒之后,進程將轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;

3)登臺態(tài)_不可中斷(TASK_UNINTERRUPTIBLE):此進程狀態(tài)類似于可中斷的阻塞狀態(tài)(TASK_INTERRUPTIBLE),只是他不會處理信號,把信號傳遞到這種狀態(tài)下的進程不能改變它的狀態(tài),即不可被信號所中斷,不能被隨便調(diào)度。在一些特定的情況下(進程必須等待,知道某些不能被中斷的事件發(fā)生),這種狀態(tài)是很有用的。只有在它等待的事件發(fā)生時,進程才被顯示地喚醒呼叫喚醒,進程將轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;

4)停止態(tài)(TASK_STOPPED),即暫停狀態(tài),進程的執(zhí)行被暫停,當(dāng)進程受到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信號,就會進入暫停狀態(tài),知道收到繼續(xù)執(zhí)行信號,轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;

5)僵尸態(tài)(EXIT_ZOMBIE):子進程運行結(jié)束,父進程尚未使用wait 函數(shù)族(如使用wait()函數(shù))等系統(tǒng)調(diào)用來回收退出狀態(tài)。處于該狀態(tài)下的子進程已經(jīng)放棄了幾乎所有的內(nèi)存空間,沒有任何可執(zhí)行代碼,也不能被調(diào)度,僅僅在進程列表中(即task_struct)保留一個位置,記載該進程的退出信息供其父進程收集。即進程結(jié)束后,內(nèi)存地址空間被釋放、task_struct 成員被釋放,但task_struct 這個空殼還存在,它就是僵尸,這個僵尸我們用kill 是殺不掉的。所以,一般在子進程結(jié)束后,我們會對其進行回收?;厥盏姆椒ㄓ腥N:

1)誰創(chuàng)建誰回收,即用父進程來回收;

2)父進程不回收,通知內(nèi)核來回收;

3)由init 進程來回收,當(dāng)父進程先死掉,子進程成為孤兒進程,由 init 進程來認養(yǎng);

當(dāng)這三種條件都不滿足時,比如父進程不去回收子進程,自己卻未死掉,僵尸便會出現(xiàn),這是非常棘手的,可以通過殺死父進程來殺死僵尸(不推薦使用);?


2、進程標(biāo)識符

?????? Linux內(nèi)核通過唯一的進程標(biāo)示符PID來標(biāo)識每個進程。PID存放在task_strcut 的pid字段中。當(dāng)系統(tǒng)啟動后,內(nèi)核通常作為某一個進程的代表。一個指向task_struct 的宏 current 用來記錄正在運行的進程。current 進程作為進程描述符結(jié)構(gòu)指針的形式出現(xiàn)在內(nèi)核代碼中,例如,current->pid 表示處理器正在執(zhí)行的進程的PID。當(dāng)系統(tǒng)需要查看所有的進程時,則調(diào)用 for_each_process(宏),這將比系統(tǒng)搜索數(shù)組的速度要快的多。

???????在Linux 中獲得當(dāng)前進程的進程號(PID)和父進程號 (PPID) 的系統(tǒng)調(diào)用函數(shù)分別為 getpid() 和 getppid() 。


?3、進程的模式

? ? ? ? 進程的執(zhí)行模式分別為用戶模式內(nèi)核模式

? ? ? ? 在CPU的所有指令中,有一些指令是非常危險的,如果錯用,將導(dǎo)致整個系統(tǒng)崩潰。比如:清內(nèi)存、設(shè)置時鐘等。如果所有的程序都能使用這些指令,那么你的系統(tǒng)一天死機n回就不足為奇了。所以,CPU將指令分為特權(quán)指令和非特權(quán)指令,對于那些危險的指令,只允許操作系統(tǒng)及其相關(guān)模塊使用,普通的應(yīng)用程序只能使用那些不會造成災(zāi)難的指令。Intel的CPU將特權(quán)級別分為4個級別:RING0,RING1,RING2,RING3。

? ? ? ???linux的內(nèi)核是一個有機的整體。每一個用戶進程運行時都有一份內(nèi)核的拷貝,每當(dāng)用戶進程使用系統(tǒng)調(diào)用時,都自動地將運行模式從用戶態(tài)轉(zhuǎn)為內(nèi)核態(tài),此時進程在內(nèi)核的地址空間中運行。

? ? ? ? ?當(dāng)一個任務(wù)(進程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時,我們就稱進程處于內(nèi)核運行態(tài)(或簡稱為內(nèi)核態(tài))。此時處理器處于特權(quán)級最高的(0級)內(nèi)核代碼中執(zhí)行。當(dāng)進程處于內(nèi)核態(tài)時,執(zhí)行的內(nèi)核代碼會使用當(dāng)前進程的內(nèi)核棧。每個進程都有自己的內(nèi)核棧。

? ? ? ? ?當(dāng)進程在執(zhí)行用戶自己的代碼時,則稱其處于用戶運行態(tài)(用戶態(tài))。即此時處理器在特權(quán)級最低的(3級)用戶代碼中運行。當(dāng)正在執(zhí)行用戶程序而突然被中斷程序中斷時,此時用戶程序也可以象征性地稱為處于進程的內(nèi)核態(tài)。因為中斷處理程序?qū)⑹褂卯?dāng)前進程的內(nèi)核棧。這與處于內(nèi)核態(tài)的進程的狀態(tài)有些類似。

? ? ? ? ?內(nèi)核態(tài)與用戶態(tài)是操作系統(tǒng)的兩種運行級別,跟intel cpu沒有必然的聯(lián)系, 如上所提到的intel cpu提供Ring0-Ring3四種級別的運行模式,Ring0級別最高,Ring3最低。Linux使用了Ring3級別運行用戶態(tài),Ring0作為 內(nèi)核態(tài),沒有使用Ring1和Ring2。Ring3狀態(tài)不能訪問Ring0的地址空間,包括代碼和數(shù)據(jù)。Linux進程的4GB地址空間,3G-4G部 分大家是共享的,是內(nèi)核態(tài)的地址空間,這里存放在整個內(nèi)核的代碼和所有的內(nèi)核模塊,以及內(nèi)核所維護的數(shù)據(jù)。用戶運行一個程序,該程序所創(chuàng)建的進程開始是運行在用戶態(tài)的,如果要執(zhí)行文件操作,網(wǎng)絡(luò)數(shù)據(jù)發(fā)送等操作,必須通過write,send等系統(tǒng)調(diào)用,這些系統(tǒng)調(diào)用會調(diào)用內(nèi)核中的代碼來完成操作,這時,必須切換到Ring0,然后進入3GB-4GB中的內(nèi)核地址空間去執(zhí)行這些代碼完成操作,完成后,切換回Ring3,回到用戶態(tài)。這樣,用戶態(tài)的程序就不能 隨意操作內(nèi)核地址空間,具有一定的安全保護作用。

? ? ?處理器總處于以下狀態(tài)中的一種:

1、內(nèi)核態(tài),運行于進程上下文,內(nèi)核代表進程運行于內(nèi)核空間;
2、內(nèi)核態(tài),運行于中斷上下文,內(nèi)核代表硬件運行于內(nèi)核空間;
3、用戶態(tài),運行于用戶空間。


從用戶空間到內(nèi)核空間有兩種觸發(fā)手段:

1、用戶空間的應(yīng)用程序,通過系統(tǒng)調(diào)用,進入內(nèi)核空間。這個時候用戶空間的進程要傳遞很多變量、參數(shù)的值給內(nèi)核,內(nèi)核態(tài)運行的時候也要保存用戶進程的一些寄存器值、變量等。所謂的“進程上下文”,可以看作是用戶進程傳遞給內(nèi)核的這些參數(shù)以及內(nèi)核要保存的那一整套的變量和寄存器值和當(dāng)時的環(huán)境等。

2、硬件通過觸發(fā)信號,導(dǎo)致內(nèi)核調(diào)用中斷處理程序,進入內(nèi)核空間。這個過程中,硬件的一些變量和參數(shù)也要傳遞給內(nèi)核,內(nèi)核通過這些參數(shù)進行中斷處理。所謂的“中斷上下文”,其實也可以看作就是硬件傳遞過來的這些參數(shù)和內(nèi)核需要保存的一些其他環(huán)境(主要是當(dāng)前被打斷執(zhí)行的進程環(huán)境)。

? ?一個程序我們可以從兩種角度去分析。其一就是它的靜態(tài)結(jié)構(gòu),其二就是動態(tài)過程。下圖表示了用戶態(tài)和內(nèi)核態(tài)直接的關(guān)系(靜態(tài)的角度來觀察程序)



二、進程編程

1、fork() 函數(shù)

??????? 在Linux 中創(chuàng)建一個新進程的方法是使用fork() 函數(shù)。fork() 函數(shù)最大的特性就是執(zhí)行一次返回兩個值。

函數(shù)原型如下:

所需頭文件

#include <sys/types.h> //提供類型pid_t定義

#include <unistd.h>

函數(shù)原型pid_t? fork(void)
函數(shù)返回值

0 :子進程

子進程PID(大于0的整數(shù)):父進程

-1 :出錯

fork() 函數(shù)用于從已存在的進程中創(chuàng)建一個新進程。新進程稱為子進程,而原進程稱為父進程。具體fork()函數(shù)究竟做了什么,我們先看這張圖:

?

這里我們可以看到,使用fork () 函數(shù)得到的子進程是父進程的一個復(fù)制品,它從父進程處繼承了整個進程的地址空間(注意:子進程有其獨立的地址空間,只是復(fù)制了父進程地址空間里的內(nèi)容),包括進程上下文、代碼段、進程堆棧、內(nèi)存信息、打開的文件描述符、信號處理函數(shù)、進程優(yōu)先級等。而子進程所獨有的只是它的進程號、資源使用和計時器等。

????? 因為子進程幾乎是父進程的完全復(fù)制,所以父子進程會運行同一個程序,這里,兩個進程都會從PC位置往下執(zhí)行;如何區(qū)分它們呢?父子進程一個很重要的區(qū)別是, fork()返回值不同。父進程中返回值是子進程的進程號,而子進程中返回0;所以在上圖中,兩個進程會通過判斷PID來選擇執(zhí)行的語句。

?????? 注意:子進程沒有執(zhí)行fork() 函數(shù),而是從fork() 函數(shù)調(diào)用的下一條語句開始執(zhí)行。

下面,寫一個fork()程序,來加深對fork()的理解:

[cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<unistd.h>??
  • #include?<sys/types.h>??
  • ??
  • int?global?=?22;??
  • ??
  • int?main(void)??
  • {??
  • ????int?test?=?0,stat;??
  • ????pid_t?pid;??
  • ??
  • ????pid?=?fork();??
  • ??
  • ????if(pid?<?0)??
  • ????{??
  • ????????perror("fork");??
  • ????????return?-1;??
  • ????}??
  • ????else?if(pid?==?0)??
  • ????????{??
  • ????????????global++;??
  • ????????????test++;??
  • ????????????printf("global?=?%d?test?=?%d?Child,my?PID?is?%d\n",global,test,getpid());??
  • ????????????exit(0);??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????global?+=?2;??
  • ????????????test?+=?2;??
  • ????????????printf("global?=?%d?test?=?%d?Parent,my?PID?is?%d\n",global,test,getpid());??
  • ????????????exit(0);??
  • ????????}??
  • }??
  • 執(zhí)行結(jié)果如下:



    從結(jié)果我們可以發(fā)現(xiàn)幾個問題:

    1)最后一行光標(biāo)在閃,是程序沒執(zhí)行完嗎?第三行中子進程打印前是bash,這是什么原因呢?

    其實我們這里執(zhí)行的程序中有三個進程在執(zhí)行:父進程、子進程、bash。從打印結(jié)果中我們可以看到父進程先執(zhí)行完,然后是bash ,最后子進程執(zhí)行完,這里的光標(biāo)其實是bash的。所以,我們可以發(fā)現(xiàn):父進程、子進程誰先運行時不可知的,誰先運行有內(nèi)核調(diào)度來確定;

    2)從打印結(jié)果中,可以看出父子進程打印出了各自的進程號和對應(yīng)變量的值,顯然global和test在父子進程間是獨立的,其各自的操作不會對對方的值有影響;

    ?

    2、exec 函數(shù)族

    ??????? fork() 函數(shù)用于創(chuàng)建一個子進程,該子進程幾乎賦值了父進程的全部內(nèi)容。我們能否讓子進程執(zhí)行一個新的程序呢?exec 函數(shù)族就提供了一個在進程中執(zhí)行里一個程序的辦法。它可以根據(jù)指定的文件名或目錄找到可執(zhí)行文件,并用它來取代當(dāng)前進程的數(shù)據(jù)段、代碼段和堆棧段。在執(zhí)行完之后,當(dāng)前進程除了進程號外,其他的內(nèi)容都被替換掉了。所以,如果一個進程想執(zhí)行另一個程序,那么它就可以調(diào)用fork() 函數(shù)創(chuàng)建一個進程,然后調(diào)用exec家族中的任意一個函數(shù),這樣看起來就像執(zhí)行應(yīng)用程序而產(chǎn)生了一個新進程。

    ??????Linux 中并沒有exec() 函數(shù),而是有6個以 exec 開頭的函數(shù),下面是函數(shù)語法:

    所需頭文件#include <stdio.h>
    函數(shù)原型

    int execl (const char *path,const char *arg,...);

    int execv (const char *path, char *const argv[]);

    int execle (const char *path,const char *arg,....,char *const envp[]);

    int execve(const char *path, char? const *argv[],char *const envp[]);

    int execlp (const char *file,const char *arg,...);

    int execvp (const char *file, char *const argv[]);

    函數(shù)返回值-1;出錯

    exec 函數(shù)族使用區(qū)別

    1)可執(zhí)行文件查找方式

    表中的前四個函數(shù)的查找方式都是指定完整的文件目錄路徑,而最后兩個函數(shù)(以p 結(jié)尾的函數(shù))可以只給出文件名,系統(tǒng)會自動從環(huán)境變量“$PATH”所包含的路徑中進行查找。

    2)參數(shù)表傳遞方式

    兩種方式:逐個列舉或是將所喲參數(shù)通過指針數(shù)組傳遞

    以函數(shù)名的第五位字母來區(qū)分,字母為" l ”(list) 的表示逐個列舉的方式;字母為"?v "(vertor) 的表示將所有參數(shù)構(gòu)成指針數(shù)組傳遞,其語法為 char *const argv[]

    3)環(huán)境變量的使用

    exec 函數(shù)族可以默認使用系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量,這里,以"e" (Enviromen) 結(jié)尾的兩個函數(shù)execle 、execve 就可以在 envp[] 中傳遞當(dāng)前進程所使用的環(huán)境變量;

    exev使用示例:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<unistd.h>??
  • ??
  • int?main()??
  • {??
  • //調(diào)用execlp?函數(shù),相當(dāng)于調(diào)用了?"ps?-ef"命令??
  • ????if(execlp("ps","ps","-ef",NULL)?<?0)?//這里"ps"為filename?"ps"?為argv[0]?"-ef"為argv[1],NULL為參數(shù)結(jié)束標(biāo)志??
  • ????{??
  • ????????perror("execlp?error");??
  • ????????return?-1;??
  • ????}??
  • ??
  • ????return?0;??
  • }??
  • 執(zhí)行結(jié)果如下:

    [cpp]?view plaincopy
  • fs@ubuntu:~/qiang/process/exec$?./execlp???
  • UID????????PID??PPID??C?STIME?TTY??????????TIME?CMD??
  • root?????????1?????0??0?13:48??????????00:00:01?/sbin/init??
  • root?????????2?????0??0?13:48??????????00:00:00?[kthreadd]??
  • ??
  • ...??
  • ??
  • root??????5300?????2??0?20:49??????????00:00:00?[kworker/0:2]??
  • root??????5351?????2??0?20:54??????????00:00:00?[kworker/0:0]??
  • fs????????5371??2797??0?20:56?pts/0????00:00:00?ps?-ef??
  • fs@ubuntu:~/qiang/process/exec$???
  • 如果我們使用execvp,則

    [cpp]?view plaincopy
  • char?*argv[]?=?{"ps","-ef",NULL};??
  • execvp("ps",argv);??

  • 3、exit() 和_exit()
    1) exit()和_exit()函數(shù)說明

    ?? exit()和_exit() 都是用來終止進程的。當(dāng)程序執(zhí)行到exit()或_exit() 時,進程會無條件的停止剩下的所有操作,清除各種數(shù)據(jù)結(jié)構(gòu),并終止本進程的運行。但是,這兩個函數(shù)是有區(qū)別的:

    可以看出exit() 是庫函數(shù),而_exit() 是系統(tǒng)調(diào)用;

    _exit() 函數(shù)的作用最為簡單:直接使進程終止運行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);exit() 函數(shù)則在這些基礎(chǔ)上作了一些包裝,在執(zhí)行退出之前加了若干道工序。

    二者函數(shù)描述如下:

    所需頭文件

    exit()? :#include <stdlib.h>

    _exit():#include <unistd.h>

    函數(shù)原型

    exit()? :void exit(int status);

    _exit():void _exit(int status);

    函數(shù)傳入值

    status 是一個整形的參數(shù),可以利用這個參數(shù)傳遞進程結(jié)束時的狀態(tài)。

    通常0表示正常結(jié)束;其他的數(shù)值表示出現(xiàn)了錯誤,進程非正常結(jié)束。在

    實際編程時,可以用wait 系統(tǒng)調(diào)用接收子進程的返回值,進行相應(yīng)的處理。

    其實,在main函數(shù)內(nèi)執(zhí)行return 語句,也會使進程正常終止;

    exit(status) 執(zhí)行完會將終止?fàn)顟B(tài)(status)傳給內(nèi)核,內(nèi)核會將status傳給父進程的wait(&status),wait()會提取status,并分析;

    ?

    4、wait 和waitpid()

    wait() 函數(shù)???

    調(diào)用該函數(shù)使進程阻塞,直到任一個子進程結(jié)束或者是該進程接受到了一個信號為止。如果該進程沒有子進程或其子進程已經(jīng)結(jié)束。wait 函數(shù)會立即返回。函數(shù)描述如下:

    所需頭文件

    #include <sys/types.h>

    #includ <sys/wait.h>

    函數(shù)原型pid_t wait(int *status)
    函數(shù)參數(shù)

    status是一個整型指針,指向的對象用來保存子進程退出時的狀態(tài)

    status 若為空,表示忽略子進程退出時的狀態(tài);

    status 若不為空,表示保存子進程退出時的狀態(tài);

    另外,子進程的結(jié)束狀態(tài)可有Linux 中的一些特定的宏來測宏。

    函數(shù)返回值

    成功:子進程的進程號

    失敗: -1

    附:檢查wait 所返回的終止?fàn)顟B(tài)的宏

    WIFEXTED (status)?:若為正常終止子進程返回的狀態(tài),則為真。對于這種情況可執(zhí)行 WEXITSTATUS (status) ,取子進程傳送給exit 參數(shù)的低八位;

    (首先判斷子進程是否正常死亡,異常死亡是不會運行到exit()的,這時分析status 是無意義的;)

    wait() 會回收任一一個先死亡的子進程;

    下面看一個程序,wait() 與exit()的使用

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<sys/types.h>??
  • #include?<sys/wait.h>??
  • #include?<stdlib.h>??
  • #include?<unistd.h>??
  • ?????????
  • int?main(int?argc,?char?**argv)??
  • {??
  • ????pid_t?pid;??
  • ??
  • ????printf("parent[pid=%d]?is?born\n",?getpid());??
  • ???????????
  • ????if?(-1?==?(pid?=?fork()))?{??
  • ????????perror("fork?error");??
  • ????????return?-1;??
  • ????}??
  • ??????
  • ????if?(pid?==?0){??
  • ????????printf("child[pid=%d]?is?born\n",?getpid());??
  • ????????sleep(20);??
  • ????????printf("child?is?over\n");??
  • ??????????
  • ????????exit(123);?//return?123;???
  • ????}??
  • ????else{??
  • ????????pid_t?pid_w;??
  • ????????int?status;??
  • ??????????
  • ????????printf("parent?started?to?wait...\n");??
  • ????????pid_w?=?wait(&status);??
  • ????????printf("parent?wait?returned\n");??
  • ??????????
  • ????????if?(pid_w?<?0)?{??
  • ????????????perror("wait?error");??
  • ????????????return?1;??
  • ????????}??
  • ??????????
  • ????????if?(WIFEXITED(status))?{???
  • ????????????status?=?WEXITSTATUS(status);??
  • ????????????printf("wait?returns?with?pid?=?%d.?return?status?is?%d\n",?pid_w,?status);??
  • ????????}?else?{??
  • ????????????printf("wait?returns?with?pid?=?%d.?the?child?is?terminated?abnormally\n",?pid_w);??
  • ????????}??
  • ??????????
  • //??????while(1);??
  • ????????printf("father?is?over\n");??
  • ??????????
  • ????????return?0;??
  • ????}??
  • }??
  • 執(zhí)行結(jié)果如下:

    [cpp]?view plaincopy
  • fs@ubuntu:~/qiang/process/wait$?./wait??
  • parent[pid=5603]?is?born??
  • parent?started?to?wait...??
  • child[pid=5604]?is?born??
  • child?is?over??
  • parent?wait?returned??
  • wait?returns?with?pid?=?5604.?return?status?is?123??
  • father?is?over??
  • fs@ubuntu:~/qiang/process/wait$???

  • waitpid() 函數(shù)

    ? ? ? ?waitpid() 函數(shù)和wait() 的作用是完全相同的,但waitpid 多出了兩個可有用戶控制的參數(shù)pid 和 options,從而為用戶編程提供了一種更為靈活的方式。waitpid 可以用來等待指定的進程,可以使進程不掛起而立刻返回。

    wait(&status);等價于waitpid(-1, &status, 0);

    其函數(shù)類型如下:

    所需頭文件#include <sys/types.h> /* 提供類型pid_t的定義 */
    #include <sys/wait.h>
    函數(shù)原型pid_t waitpid(pid_t pid,int *status,int options)
    函數(shù)傳入值pid > 0 時,只等待進程ID等于pid的子進程,不管其它已經(jīng)有多少子進程運行結(jié)束退出了,
    ? ? ? ? ? ? ? ? ? ? ? 只要指定的子進程還沒有結(jié)束,waitpid就會一直等下去。
    pid = -1時,等待任何一個子進程退出,沒有任何限制,
    ? ? ? ? ? ? ? ? ? ? ?此時waitpid和wait的作用一模一樣。
    pid = 0 時,等待同一個進程組中的任何子進程,如果子進程已經(jīng)加入了別的進程組,
    ? ? ? ? ? ? ? ? ? ? ? waitpid不會對它做任何理睬。
    pid < -1時,等待一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值。
    函數(shù)傳入值WNOHANG ? ? ?如果沒有任何已經(jīng)結(jié)束的子進程則馬上返回, 不予以等待;
    WUNTRACED 如果子進程進入暫停執(zhí)行情況則馬上返回,但結(jié)束狀態(tài)不予以理會;
    0 ? : 同wait() ,阻塞父進程,直到指定的子進程退出;
    函數(shù)返回值> 0 :已經(jīng)結(jié)束運行的子進程的進程號;
    0 ? ?: 使用選項WNOHANG 且沒有子進程退出
    -1 ? : 出錯

    示例如下:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<sys/types.h>??
  • #include?<sys/wait.h>??
  • #include?<unistd.h>??
  • #include?<stdlib.h>??
  • ??
  • int?main()??
  • {??
  • ????pid_t?pc,?pr;??
  • ??
  • ????pc?=?fork();??
  • ????if(pc?<?0)?/*?如果fork出錯?*/??
  • ????????printf("Error?occured?on?forking\n");??
  • ????else?if(pc?==?0)?/*?如果是子進程?*/??
  • ????{??
  • ????????sleep(10);?/*?睡眠10秒?*/??
  • ????????exit(0);??
  • ????}??
  • ????/*?如果是父進程?*/??
  • ????do{??
  • ????????pr?=?waitpid(pc,?NULL,?WNOHANG);?/*?使用了WNOHANG參數(shù),waitpid不會在這里等待?*/??
  • ????????if(pr?==?0)?/*?如果沒有收集到子進程?*/??
  • ????????{??
  • ????????????printf("No?child?exited\n");??
  • ????????????sleep(1);??
  • ????????}??
  • ????}while(pr?==?0);?/*?沒有收集到子進程,就回去繼續(xù)嘗試?*/??
  • ??
  • ????if(pr?==?pc)??
  • ????????printf("successfully?get?child?%d\n",?pr);??
  • ????else??
  • ????????printf("some?error?occured\n");??
  • }??
  • 執(zhí)行結(jié)果如下:

    [cpp]?view plaincopy
  • fs@ubuntu:~/qiang/wait$?./waitpid???
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • No?child?exited??
  • successfully?get?child?17144??
  • fs@ubuntu:~/qiang/wait$??
  • 父進程經(jīng)過10次失敗的嘗試之后,終于收集到了退出的子進程。

    總結(jié)

    以上是生活随笔為你收集整理的Linux 系统应用编程——进程基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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