第六章-嵌入式Linux多任務編程_第1頁
第六章-嵌入式Linux多任務編程_第2頁
第六章-嵌入式Linux多任務編程_第3頁
第六章-嵌入式Linux多任務編程_第4頁
第六章-嵌入式Linux多任務編程_第5頁
已閱讀5頁,還剩119頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、第六章 嵌入式Linux多任務編程6.2 進程控制編程6.3 進程間通信6.4 多線程編程6.5 實驗內(nèi)容6.6 小結(jié)6.1 Linux下多任務機制的介紹6.7 思考與練習26.1Linux下多任務概述36.1 Linux下多任務概述 多任務處理是指用戶可以在同一時間內(nèi)運行多個應用程序,每個應用程序被稱作一個任務。Linux就是一個支持多任務的操作系統(tǒng),比起單任務系統(tǒng)它的功能增強了許多。當多任務操作系統(tǒng)使用某種任務調(diào)度策略允許兩個或更多進程并發(fā)共享一個處理器時,事實上處理器在某一時刻只會給一件任務提供服務。因為任務調(diào)度機制保證不同任務之間的切換速度十分迅速,因此給人多個任務同時運行的錯覺。多任

2、務系統(tǒng)中有3個功能單位:任務、進程和線程。46.1.1 任務任務是一個邏輯概念,指由一個軟件完成的活動,或者是一系列共同達到某一目的的操作。通常一個任務是一個程序的一次運行,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務是進程或者是線程。 任務、進程和線程之間的關(guān)系 :56.1.2 進程進程是指一個具有獨立功能的程序在某個數(shù)據(jù)集合上的一次動態(tài)執(zhí)行過程,它是系統(tǒng)進行資源分配和調(diào)度的基本單元。一次任務的運行可以并發(fā)激活多個進程,這些進程相互合作來完成該任務的一個最終目標。進程的特性并發(fā)性動態(tài)性交互性獨立性異步性 進程和程序是有本質(zhì)區(qū)別的:程序是靜態(tài)的一段代碼,是一些保存在非易失性存儲

3、器的指令的有序集合,沒有任何執(zhí)行的概念;而進程是一個動態(tài)的概念,它是程序執(zhí)行的過程,包括了動態(tài)創(chuàng)建、調(diào)度和消亡的整個過程,它是程序執(zhí)行和資源管理的最小單位。66.1.2 進程 進程的種類:交互式進程 批處理進程 實時進程 Linux下進程結(jié)構(gòu)進程不但包括程序的指令和數(shù)據(jù),而且包括程序計數(shù)器和處理器的所有寄存器以及存儲臨時數(shù)據(jù)的進程堆棧,從而正在執(zhí)行的進程包括處理器當前的一切活動。76.1.2 進程進程狀態(tài)運行狀態(tài)可中斷的阻塞狀態(tài)不可中斷的阻塞狀態(tài)可終止的阻塞狀態(tài)暫停狀態(tài)跟蹤狀態(tài)僵尸狀態(tài)僵尸撤銷狀態(tài)86.1.2 進程 進程狀態(tài)轉(zhuǎn)換關(guān)系:96.1.2 進程Linux內(nèi)核通過惟一的進程標識符PID來

4、標識每個進程。PID存放在進程描述符的pid字段中 。在Linux中獲得當前進程的進程號(PID)和父進程號(PPID)的系統(tǒng)調(diào)用函數(shù)分別為getpid()和getppid()。 106.1.2 進程進程的創(chuàng)建和執(zhí)行:許多操作系統(tǒng)都提供的是產(chǎn)生進程的機制,也就是首先在新的地址空間里創(chuàng)建進程、讀入可執(zhí)行文件,最后再開始執(zhí)行。Linux中進程的創(chuàng)建很特別,它把上述步驟分解到兩個單獨的函數(shù)中取執(zhí)行:fork()和exec函數(shù)族。首先,fork()通過拷貝當前進程創(chuàng)建一個子進程,子進程與父進程的區(qū)別僅僅在于不同的PID、PPID和某些資源及統(tǒng)計量。exec函數(shù)族負責讀取可執(zhí)行文件并將其載入地址空間開始

5、運行。進程的終止:進程終結(jié)也需要做很多繁瑣的收尾工作,系統(tǒng)必須保證進程所占用的資源回收,并通知父進程。Linux首先把終止的進程設置為僵尸狀態(tài),這個時候,進程無法投入運行了,它的存在只為父進程提供信息,申請死亡。父進程得到信息后,開始調(diào)用wait函數(shù)族,最終賜死子進程,子進程占用的所有資源被全部釋放。116.1.2 進程進程的內(nèi)存結(jié)構(gòu)12 6.1.3 線程線程的概念它是進程內(nèi)獨立的一條運行路線,處理器調(diào)度的最小單元,也可以稱為輕量級進程。線程可以對進程的內(nèi)存空間和資源進行訪問,并與同一進程中的其他線程共享。因此,線程的上下文切換的開銷比創(chuàng)建進程小得多。 線程與進程間的關(guān)系136.1.3 線程

6、線程的種類用戶級線程 輕量級進程 內(nèi)核線程 146.2進程編程基礎156.2.1 進程編程基礎fork()fork()函數(shù)用于從已存在的進程中創(chuàng)建一個新進程。新進程稱為子進程,而原進程稱為父進程。使用fork()函數(shù)得到的子進程是父進程的一個復制品,它從父進程處繼承了整個進程的地址空間,包括進程上下文、代碼段、進程堆棧、內(nèi)存信息、打開的文件描述符、信號控制設定、進程優(yōu)先級、進程組號、當前工作目錄、根目錄、資源限制和控制終端等,而子進程所獨有的只有它的進程號、資源使用和計時器等。fork()函數(shù)語法:166.2.1 進程編程基礎 exec函數(shù)族fork()函數(shù)是用于創(chuàng)建一個子進程,該子進程幾乎拷

7、貝了父進程的全部內(nèi)容,但是,這個新創(chuàng)建的進程如何執(zhí)行呢?這個exec函數(shù)族就提供了一個在進程中啟動另一個程序執(zhí)行的方法。 exec函數(shù)族成員函數(shù)語法:176.2.1 進程編程基礎 exec函數(shù)族使用區(qū)別查找方式表中的前四個函數(shù)的查找方式都是完整的文件目錄路徑,而最后兩個函數(shù)(以p結(jié)尾的函數(shù))可以只給出文件名,系統(tǒng)就會自動從環(huán)境變量“$PATH”所指出的路徑中進行查找。參數(shù)傳遞方式兩種方式:逐個列舉、將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞以函數(shù)名的第五位字母來區(qū)分的,字母為“l(fā)”(list)的表示逐個列舉的方式,其語法為char *arg;字母為“v”(vertor)的表示將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞

8、,其語法為*const argv環(huán)境變量exec函數(shù)族可以默認系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量。這里,以“e”(Enviromen)結(jié)尾的兩個函數(shù)execle、execve就可以在envp中指定當前進程所使用的環(huán)境變量18 6.2.1 進程編程基礎exec函數(shù)執(zhí)行失敗,常見原因:找不到文件或路徑,此時errno被設置為ENOENT; 數(shù)組argv和envp忘記用NULL結(jié)束,此時errno被設置為EFAULT; 沒有對應可執(zhí)行文件的運行權(quán)限,此時errno被設置為EACCES。196.2.1 進程編程基礎exit()和_exit()exit()和_exit()函數(shù)都是用來終止進程的。當

9、程序執(zhí)行到exit()或_exit()時,進程會無條件地停止剩下的所有操作,清除包括各種數(shù)據(jù)結(jié)構(gòu),并終止本進程的運行。 206.2.1 進程編程基礎exit()和_exit()的執(zhí)行過程216.2.1 進程編程基礎 exit()和_exit()的區(qū)別_exit()函數(shù)的作用是直接使進程停止運行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);exit()函數(shù)則在這些基礎上作了一些包裝,在執(zhí)行退出之前加了若干道工序。exit()函數(shù)與_exit()函數(shù)最大的區(qū)別就在于exit()函數(shù)在終止當前進程之前要檢查該進程打開過哪些文件,把文件緩沖區(qū)中的內(nèi)容寫回文件,就是圖中的“清理I/O緩沖”一項

10、。226.2.1 進程編程基礎exit()和_exit函數(shù)語法:236.2.1 進程編程基礎 wait()和waitpid()wait()函數(shù)是用于使父進程(也就是調(diào)用wait()的進程)阻塞,直到一個子進程結(jié)束或者該進程接到了一個指定的信號為止。如果該父進程沒有子進程或者他的子進程已經(jīng)結(jié)束,則wait()就會立即返回。waitpid()的作用和wait()一樣,但它并不一定要等待第一個終止的子進程,它還有若干選項,如可提供一個非阻塞版本的wait()功能,也能支持作業(yè)控制。 wait函數(shù)語法:246.2.1 進程編程基礎wait()和waitpid()waitpid函數(shù)語法:256.2.1

11、進程編程基礎 wait()和waitpid()waitpid實例:266.2.2 Linux守護進程 守護進程,也就是通常所說的Daemon進程,是Linux中的后臺服務進程。它是一個生存期較長的進程,通常獨立于控制終端并且周期性的執(zhí)行某種任務或等待處理某些發(fā)生的事件守護進程常常在系統(tǒng)引導裝入時啟動,在系統(tǒng)關(guān)閉時終止Linux系統(tǒng)有很多守護進程,大多數(shù)服務都是用守護進程實現(xiàn)的 在Linux中,每一個系統(tǒng)與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附于這個終端,這個終端就稱為這些進程的控制終端,當控制終端被關(guān)閉時,相應的進程都會被自動關(guān)閉。守護進程能夠突破這種限制,它從被執(zhí)行

12、開始運轉(zhuǎn),直到整個系統(tǒng)關(guān)閉才會退出。如果想讓某個進程不因為用戶或終端或其他的變化而受到影響,就必須把這個進程變成一個守護進程。276.2.2 Linux守護進程 編寫守護進程創(chuàng)建子進程,父進程退出在子進程中創(chuàng)建新會話改變當前目錄為根目錄重設文件權(quán)限掩碼關(guān)閉文件描述符286.2.2 Linux守護進程 創(chuàng)建子進程,父進程退出這是編寫守護進程的第一步。由于守護進程是脫離控制終端的,因此,完成第一步后就會在shell終端里造成一種程序已經(jīng)運行完畢的假象。之后的所有工作都在子進程中完成,而用戶在shell終端里則可以執(zhí)行其他的命令,從而在形式上做到了與控制終端的脫離。由于父進程已經(jīng)先于子進程退出,會造

13、成子進程沒有父進程,從而變成一個孤兒進程。在Linux中,每當系統(tǒng)發(fā)現(xiàn)一個孤兒進程,就會自動由1號進程(也就是init進程)收養(yǎng)它,這樣,原先的子進程就會變成init進程的子進程了。 實例:pid = fork();if (pid 0)exit(0); /*父進程退出*/ 296.2.2 Linux守護進程 在子進程中創(chuàng)建新會話進程組進程組是一個或多個進程的集合。進程組由進程組ID來惟一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。每個進程組都有一個組長進程,其組長進程的進程號等于進程組ID。且該進程ID不會因組長進程的退出而受到影響。 會話期會話組是一個或多個進程組的集合

14、。通常,一個會話開始于用戶登錄,終止于用戶退出,在此期間該用戶運行的所有進程都屬于這個會話期 306.2.2 Linux守護進程 在子進程中創(chuàng)建新會話setsid()函數(shù)作用:setsid()函數(shù)用于創(chuàng)建一個新的會話,并擔任該會話組的組長。調(diào)用setsid()有下面的3個作用。讓進程擺脫原會話的控制。讓進程擺脫原進程組的控制。讓進程擺脫原控制終端的控制。setsid()函數(shù)格式:316.2.2 Linux守護進程 改變當前目錄為根目錄通常的做法是讓“/”作為守護進程的當前工作目錄 。使用fork創(chuàng)建的子進程繼承了父進程的當前工作目錄。由于在進程運行過程中,當前目錄所在的文件系統(tǒng)是不能卸載的,這

15、對以后的使用會造成諸多的麻煩(比如進入單用戶模式)。326.2.2 Linux守護進程 重設文件權(quán)限掩碼文件權(quán)限掩碼是指屏蔽掉文件權(quán)限中的對應位。由于使用fork新建的子進程繼承了父進程的文件權(quán)限掩碼,這就給該子進程使用文件帶來了諸多的麻煩。因此,把文件權(quán)限掩碼設置為0,可以大大增加該守護進程的靈活性。設置文件權(quán)限掩碼的函數(shù)是umask通常的使用方法為umask(0)336.2.2 Linux守護進程 關(guān)閉文件描述符同文件權(quán)限掩碼一樣,用fork新建的子進程會從父進程那里繼承一些已經(jīng)打開了的文件。這些被打開的文件可能永遠不會被守護進程讀或?qū)?,但它們一樣消耗系統(tǒng)資源,而且可能導致所在的文件系統(tǒng)無

16、法卸下 在上面的第二步后,守護進程已經(jīng)與所屬的控制終端失去了聯(lián)系。因此從終端輸入的字符不可能達到守護進程,守護進程中用常規(guī)的方法(如printf)輸出的字符也不可能在終端上顯示出來。所以,文件描述符為0、1和2的三個文件(常說的輸入、輸出和報錯這三個文件)已經(jīng)失去了存在的價值,也應被關(guān)閉。實例: for(i=0;iMAXFILE;i+)close(i);346.2.2 Linux守護進程 Linux守護進程創(chuàng)建流程356.2.2 Linux守護進程 守護進程出錯處理syslog是Linux中的系統(tǒng)日志管理服務,通過守護進程syslogd來維護。該守護進程在啟動時會讀一個配置文件“/etc/sy

17、slog.conf”。該文件決定了不同種類的消息會發(fā)送向何處。例如,緊急消息可被送向系統(tǒng)管理員并在控制臺上顯示,而警告消息則可被記錄到一個文件中。該機制提供了3個syslog相關(guān)函數(shù),分別為openlog()、syslog()和closelog()。openlog()函數(shù)用于打開系統(tǒng)日志服務的一個連接;syslog()函數(shù)是用于向日志文件中寫入消息,在這里可以規(guī)定消息的優(yōu)先級、消息輸出格式等;closelog()函數(shù)是用于關(guān)閉系統(tǒng)日志服務的連接。 366.2.2 Linux守護進程 syslog相關(guān)函數(shù)格式 37所需頭文件#include 函數(shù)原型void openlog (char *ide

18、nt, int option , int facility)函數(shù)傳入值ident要向每個消息加入的字符串,通常為程序的名稱optionLOG_CONS:如果消息無法送到系統(tǒng)日志服務,則直接輸出到系統(tǒng)控制終端LOG_NDELAY:立即打開系統(tǒng)日志服務的連接。在正常情況下,直接發(fā)送到第一條消息時才打開連接LOG_PERROR:將消息也同時送到stderr上LOG_PID:在每條消息中包含進程的PID函數(shù)傳入值facility:指定程序發(fā)送的消息類型LOG_AUTHPRIV:安全/授權(quán)訊息LOG_CRON:時間守護進程(cron及at)LOG_DAEMON:其他系統(tǒng)守護進程LOG_KERN:內(nèi)核信息

19、LOG_LOCAL07:保留LOG_LPR:行打印機子系統(tǒng)LOG_MAIL:郵件子系統(tǒng)LOG_NEWS:新聞子系統(tǒng)LOG_SYSLOG:syslogd內(nèi)部所產(chǎn)生的信息LOG_USER:一般使用者等級訊息LOG_UUCP:UUCP子系統(tǒng)6.2.2 Linux守護進程syslog相關(guān)函數(shù)格式 38所需頭文件#include 函數(shù)原型void syslog(int priority, char *format, .)函數(shù)傳入值priority:指定消息的重要性LOG_EMERG:系統(tǒng)無法使用LOG_ALERT:需要立即采取措施LOG_CRIT:有重要情況發(fā)生LOG_ERR:有錯誤發(fā)生LOG_WARN

20、ING:有警告發(fā)生LOG_NOTICE:正常情況,但也是重要情況LOG_INFO:信息消息LOG_DEBUG:調(diào)試信息format以字符串指針的形式表示輸出的格式,類似printf中的格式所需頭文件#include 函數(shù)原型void closelog(void)6.3進程間通信396.3.1 Linux下進程間通信概述 Linux下的進程通信手段基本上是從Unix平臺上的進程通信手段繼承而來的集合System V IPC(貝爾實驗室)和socket的進程間通信機制(BSD)的優(yōu)勢40 Unix進程間通信(IPC)方式包括管道、FIFO以及信號。 System V進程間通信(IPC)包括Syst

21、em V消息隊列、System V信號量以及System V共享內(nèi)存區(qū)。 Posix 進程間通信(IPC)包括Posix消息隊列、Posix信號量以及Posix共享內(nèi)存區(qū)。 6.3.1 Linux下進程間通信概述常用的進程間通信機制(1)管道(Pipe)及有名管道(named pipe):管道可用于具有親緣關(guān)系進程間的通信,有名管道,除具有管道所具有的功能外,它還允許無親緣關(guān)系進程間的通信。(2)信號(Signal):信號是在軟件層次上對中斷機制的一種模擬,它是比較復雜的通信方式,用于通知進程有某事件發(fā)生,一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一樣的。(3)消息隊列(Mes

22、sge Queue):消息隊列是消息的鏈接表,包括Posix消息隊列SystemV消息隊列。它克服了前兩種通信方式中信息量有限的缺點,具有寫權(quán)限的進程可以按照一定的規(guī)則向消息隊列中添加新消息;對消息隊列有讀權(quán)限的進程則可以從消息隊列中讀取消息。(4)共享內(nèi)存(Shared memory):可以說這是最有效的進程間通信方式。它使得多個進程可以訪問同一塊內(nèi)存空間,不同進程可以及時看到對方進程中對共享內(nèi)存中數(shù)據(jù)的更新。這種通信方式需要依靠某種同步機制,如互斥鎖和信號量等。(5)信號量(Semaphore):主要作為進程之間以及同一進程的不同線程之間的同步和互斥手段。(6)套接字(Socket):這是

23、一種更為一般的進程間通信機制,它可用于網(wǎng)絡中不同機器之間的進程間通信,應用非常廣泛。416.3.2 管道通信 管道簡介Linux的管道主要包括兩種:無名管道和有名管道。無名管道特點它只能用于具有親緣關(guān)系的進程之間的通信(也就是父子進程或者兄弟進程之間)。它是一個半雙工的通信模式,具有固定的讀端和寫端。管道也可以看成是一種特殊的文件,對于它的讀寫也可以使用普通的read()、write()等函數(shù)。但是它不是普通的文件,并不屬于其他任何文件系統(tǒng),并且只存在于內(nèi)存中 有名管道特點它可以使互不相關(guān)的兩個進程實現(xiàn)彼此通信。該管道可以通過路徑名來指出,并且在文件系統(tǒng)中是可見的。在建立了管道之后,兩個進程就

24、可以把它當作普通文件一樣進行讀寫操作,使用非常方便。FIFO嚴格地遵循先進先出規(guī)則,對管道及FIFO的讀總是從開始處返回數(shù)據(jù),對它們的寫則把數(shù)據(jù)添加到末尾,它們不支持如lseek()等文件定位操作。 426.3.2 管道通信 無名管道系統(tǒng)調(diào)用管道創(chuàng)建和關(guān)閉管道是基于文件描述符的通信方式,當一個管道建立時,它會創(chuàng)建兩個文件描述符fds0和fds1,其中fds0固定用于讀管道,而fd1固定用于寫管道,這樣就構(gòu)成了一個半雙工的通道。 管道關(guān)閉時只需將這兩個文件描述符關(guān)閉即可,可使用普通的close()函數(shù)逐個關(guān)閉各個文件描述符。 436.3.2 管道通信 無名管道系統(tǒng)調(diào)用pipe()創(chuàng)建管道可以通過

25、調(diào)用pipe()來實現(xiàn).pipe()語法:446.3.2 管道通信 無名管道系統(tǒng)調(diào)用父子進程管道的文件描述符對應關(guān)系456.3.2 管道通信 無名管道系統(tǒng)調(diào)用管道讀寫注意點:只有在管道的讀端存在時,向管道寫入數(shù)據(jù)才有意義。否則,向管道寫入數(shù)據(jù)的進程將收到內(nèi)核傳來的SIGPIPE信號(通常為Broken pipe錯誤)。向管道寫入數(shù)據(jù)時,Linux將不保證寫入的原子性,管道緩沖區(qū)一有空閑區(qū)域,寫進程就會試圖向管道寫入數(shù)據(jù)。如果讀進程不讀取管道緩沖區(qū)中的數(shù)據(jù),那么寫操作將會一直阻塞。父子進程在運行時,它們的先后次序并不能保證,因此,在為了保證父子進程已經(jīng)關(guān)閉了相應的文件描述符,可在兩個進程中調(diào)用s

26、leep()函數(shù),當然這種調(diào)用不是很好的解決方法,在后面學到進程之間的同步與互斥機制之后,請讀者自行修改本小節(jié)的實例程序。 466.3.2 管道通信 標準流管道標準流管道函數(shù)說明與Linux的文件操作中有基于文件流的標準I/O操作一樣,管道的操作也支持基于文件流的模式。這種基于文件流的管道主要是用來創(chuàng)建一個連接到另一個進程的管道,這里的“另一個進程”也就是一個可以進行一定操作的可執(zhí)行文件 。標準流管道就將一系列的創(chuàng)建過程合并到一個函數(shù)popen()中完成。它所完成的工作有以下幾步。 創(chuàng)建一個管道。 fork()一個子進程。 在父子進程中關(guān)閉不需要的文件描述符。 執(zhí)行exec函數(shù)族調(diào)用。 執(zhí)行函

27、數(shù)中所指定的命令。 476.3.2 管道通信 標準流管道popen函數(shù)格式:pclose函數(shù)格式:486.3.2 管道通信 有名管道(FIFO)通過mkfifo()創(chuàng)建有名管道,可以指定管道的路徑和打開的模式。 mkfifo()函數(shù)語法:496.3.2 管道通信 有名管道(FIFO)FIFO相關(guān)出錯信息506.3.2 管道通信 有名管道(FIFO)FIFO讀寫對于讀進程若該管道是阻塞打開,且當前FIFO內(nèi)沒有數(shù)據(jù),則對讀進程而言將一直阻塞到有數(shù)據(jù)寫入。若該管道是非阻塞打開,則不論FIFO內(nèi)是否有數(shù)據(jù),讀進程都會立即執(zhí)行讀操作。即如果FIFO內(nèi)沒有數(shù)據(jù),則讀函數(shù)將立刻返回0。對于寫進程若該管道是

28、阻塞打開,則寫操作將一直阻塞到數(shù)據(jù)可以被寫入。若該管道是非阻塞打開而不能寫入全部數(shù)據(jù),則讀操作進行部分寫入或者調(diào)用失敗。516.3.3 信號通信 信號概述信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式信號可以直接進行用戶空間進程和內(nèi)核進程之間的交互,內(nèi)核進程也可以利用它來通知用戶空間進程發(fā)生了哪些系統(tǒng)事件。它可以在任何時候發(fā)給某一進程,而無需知道該進程的狀態(tài)。如果該進程當前并未處于執(zhí)行態(tài),則該信號就由內(nèi)核保存起來,直到該進程恢復執(zhí)行再傳遞給它;如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消時才被傳遞給進程。 526.3.3 信號通信信號概述信號是進程間通信機制

29、中惟一的異步通信機制,可以看作是異步通知,通知接收信號的進程有哪些事情發(fā)生了。信號機制經(jīng)過POSIX實時擴展后,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。信號事件的發(fā)生有兩個來源:硬件來源(比如我們按下了鍵盤或者其他硬件故障);軟件來源,最常用發(fā)送信號的系統(tǒng)函數(shù)有kill()、raise()、alarm()、setitimer()和sigqueue()等,軟件來源還包括一些非法運算等操作。進程可以通過3種方式來響應一個信號。忽略信號,即對信號不做任何處理,其中,有兩個信號不能忽略:SIGKILL及SIGSTOP。捕捉信號,定義信號處理函數(shù),當信號發(fā)生時,執(zhí)行相應的處理函數(shù)。執(zhí)行缺省

30、操作,Linux對每種信號都規(guī)定了默認操作 。536.3.3 信號通信 信號概述信號缺省操作546.3.3 信號通信 信號發(fā)送和捕捉信號發(fā)送:kill()和raise()kill()函數(shù)同讀者熟知的kill系統(tǒng)命令一樣,可以發(fā)送信號給進程或進程組,它不僅可以中止進程(實際上發(fā)出SIGKILL信號),也可以向進程發(fā)送其他信號。kill()函數(shù)語法:556.3.3 信號通信 信號發(fā)送和捕捉信號捕捉:alarm()、pause()alarm()也稱為鬧鐘函數(shù),它可以在進程中設置一個定時器,當定時器指定的時間到時,它就向進程發(fā)送SIGALARM信號 。pause()函數(shù)是用于將調(diào)用進程掛起直至捕捉到信

31、號為止。這個函數(shù)很常用,通??梢杂糜谂袛嘈盘柺欠褚训?。566.3.3 信號通信 信號發(fā)送和捕捉信號的處理:signal、sigaction使用signal()處理信號時,只需要指出要處理的信號和處理函數(shù)即可。它主要是用于前32種非實時信號的處理,不支持信號傳遞信息,但是由于使用簡單、易于理解,因此也受到很多程序員的歡迎 。signal()函數(shù)的語法:576.3.3 信號通信 信號發(fā)送和捕捉信號的處理:signal、sigactionsigaction() 函數(shù)相對于signal()更加健壯,推薦使用這個586.3.3 信號通信 信號發(fā)送和捕捉信號的處理:signal、sigactionsiga

32、ction()函數(shù)中第2個和第3個參數(shù)用到的sigaction結(jié)構(gòu) struct sigaction void (*sa_handler)(int signo); sigset_t sa_mask; int sa_flags; void (*sa_restore)(void); sa_handler是一個函數(shù)指針,指定信號處理函數(shù),這里除可以是用戶自定義的處理函數(shù)外,還可以為SIG_DFL(采用缺省的處理方式)或SIG_IGN(忽略信號)。它的處理函數(shù)只有一個參數(shù),即信號值。sa_mask是一個信號集,它可以指定在信號處理程序執(zhí)行過程中哪些信號應當被屏蔽,在調(diào)用信號捕獲函數(shù)之前,該信號集要加入

33、到信號的信號屏蔽字中。sa_flags中包含了許多標志位,是對信號進行處理的各個選擇項 596.3.3 信號通信信號發(fā)送和捕捉使用信號集函數(shù)組處理信號時涉及一系列的函數(shù),這些函數(shù)按照調(diào)用的先后次序可分為以下幾大功能模塊:創(chuàng)建信號集合、注冊信號處理函數(shù)以及檢測信號。其中,創(chuàng)建信號集合主要用于處理用戶感興趣的一些信號,其函數(shù)包括以下幾個。 sigemptyset():將信號集合初始化為空。 sigfillset():將信號集合初始化為包含所有已定義的信號的集合。 sigaddset():將指定信號加入到信號集合中去。 sigdelset():將指定信號從信號集合中刪去。 sigismember()

34、:查詢指定信號是否在信號集合之中。606.3.3 信號通信信號發(fā)送和捕捉總之,在處理信號時,一般遵循如圖6.13所示的操作流程。616.3.4 信號量 信號量概述信號量是用來解決進程之間的同步與互斥問題的一種進程之間通信機制,包括一個稱為信號量的變量和在該信號量下等待資源的進程等待隊列,以及對信號量進行的兩個原子操作(PV操作)。其中信號量對應于某一種資源,取一個非負的整型值。信號量值指的是當前可用的該資源的數(shù)量,若它等于0則意味著目前沒有可用的資源。PV原子操作的具體定義為:P操作:如果有可用的資源(信號量值0),則占用一個資源(給信號量值減去一,進入臨界區(qū)代碼);如果沒有可用的資源(信號量

35、值等于0),則被阻塞到,直到系統(tǒng)將資源分配給該進程(進入等待隊列,一直等到資源輪到該進程)。V操作:如果在該信號量的等待隊列中有進程在等待資源,則喚醒一個阻塞進程。如果沒有進程等待它,則釋放一個資源(給信號量值加一)。626.3.4 信號量 信號量概述常見的使用信號量訪問臨界區(qū)的偽代碼所下所示:/* 設R為某種資源,S為資源R的信號量*/INIT_VAL(S);/* 對信號量S進行初始化 */非臨界區(qū);P(S);/* 進行P操作 */臨界區(qū)(使用資源R);/* 只有有限個(通常只有一個)進程被允許進入該區(qū)*/V(S);/* 進行V操作 */非臨界區(qū);最簡單的信號量是只能取0和1兩種值,這種信號

36、量被叫做二維信號量。在本節(jié)中,主要討論二維信號量。二維信號量的應用比較容易地擴展到使用多維信號量的情況。636.3.4 信號量 信號量編程使用信號量的步驟:第一步:創(chuàng)建信號量或獲得在系統(tǒng)已存在的信號量,此時需要調(diào)用semget()函數(shù)。不同進程通過使用同一個信號量鍵值來獲得同一個信號量。第二步:初始化信號量,此時使用semctl()函數(shù)的SETVAL操作。當使用二維信號量時,通常將信號量初始化為1。第三步:進行信號量的PV操作,此時調(diào)用semop()函數(shù)。這一步是實現(xiàn)進程之間的同步和互斥的核心工作部分。第四步:如果不需要信號量,則從系統(tǒng)中刪除它,此時使用semclt()函數(shù)的IPC_RMID操

37、作。此時需要注意,在程序中不應該出現(xiàn)對已經(jīng)被刪除的信號量的操作。646.3.4 信號量信號量編程semget()函數(shù)語法:656.3.4 信號量 信號量編程semctl()函數(shù)語法:666.3.4 信號量 信號量編程semop()函數(shù)語法:676.3.5 共享內(nèi)存共享內(nèi)存是一種最為高效的進程間通信方式,進程可以直接讀寫內(nèi)存,而不需要任何數(shù)據(jù)的拷貝為了在多個進程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū),可以由需要訪問的進程將其映射到自己的私有地址空間進程就可以直接讀寫這一內(nèi)存區(qū)而不需要進行數(shù)據(jù)的拷貝,從而大大提高的效率。由于多個進程共享一段內(nèi)存,因此也需要依靠某種同步機制,如互斥鎖和信號量等686.

38、3.5 共享內(nèi)存共享內(nèi)存原理示意圖696.3.5 共享內(nèi)存共享內(nèi)存實現(xiàn)的步驟:創(chuàng)建共享內(nèi)存,這里用到的函數(shù)是shmget,也就是從內(nèi)存中獲得一段共享內(nèi)存區(qū)域映射共享內(nèi)存,也就是把這段創(chuàng)建的共享內(nèi)存映射到具體的進程空間中去,這里使用的函數(shù)是shmat到這里,就可以使用這段共享內(nèi)存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進行操作撤銷映射的操作,其函數(shù)為shmdt 706.3.5 共享內(nèi)存shmget函數(shù)語法:716.3.5 共享內(nèi)存shmat函數(shù)語法:726.3.5 共享內(nèi)存shmdt函數(shù)語法:736.3.6 消息隊列 消息隊列就是一些消息的列表。用戶可以在消息隊列中添加消息和讀取消息等。從

39、這點上看,消息隊列具有一定的FIFO特性,但是它可以實現(xiàn)消息的隨機查詢,比FIFO具有更大的優(yōu)勢。同時,這些消息又是存在于內(nèi)核中的,由“隊列ID”來標識。 746.3.6 消息隊列 消息隊列的實現(xiàn)包括創(chuàng)建或打開消息隊列、添加消息、讀取消息和控制消息隊列這四種操作創(chuàng)建或打開消息隊列使用的函數(shù)是msgget,這里創(chuàng)建的消息隊列的數(shù)量會受到系統(tǒng)消息隊列數(shù)量的限制添加消息使用的函數(shù)是msgsnd函數(shù),它把消息添加到已打開的消息隊列末尾讀取消息使用的函數(shù)是msgrcv,它把消息從消息隊列中取走,與FIFO不同的是,這里可以指定取走某一條消息控制消息隊列使用的函數(shù)是msgctl,它可以完成多項功能。756

40、.3.6 消息隊列 msgget函數(shù)語法:766.3.6 消息隊列 msgsnd函數(shù)語法:77 6.3.6 消息隊列msgrcv函數(shù)語法:786.3.6 消息隊列 msgctl函數(shù)語法:796.4多線程編程806.4.1 線程基本編程 創(chuàng)建線程實際上就是確定調(diào)用該線程函數(shù)的入口點,這里通常使用的函數(shù)是pthread_create()。 在線程創(chuàng)建之后,就開始運行相關(guān)的線程函數(shù),在該函數(shù)運行完之后,該線程也就退出了,這也是線程退出的一種方法。另一種退出線程的方法是使用函數(shù)pthread_exit(),這是線程的主動行為。 pthread_join()可以用于將當前線程掛起來等待線程的結(jié)束。這個函

41、數(shù)是一個線程阻塞的函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當函數(shù)返回時,被等待線程的資源就被收回。 pthread_cancel() 向其他線程發(fā)送終止信號,但在被取消的線程的內(nèi)部需要調(diào)用pthread_setcancel()函數(shù)和pthread_setcanceltype()函數(shù)設置自己的取消狀態(tài)。816.4.1 線程基本編程 phtread_create()函數(shù)語法: phtread_exit()函數(shù)語法:826.4.1 線程基本編程 phtread_join()函數(shù)語法:phtread_cancel()函數(shù)語法:836.4.2 線程之間的同步和互斥互斥鎖線程控制互斥鎖是用一種

42、簡單的加鎖方法來控制對共享資源的原子操作。 互斥鎖只有兩種狀態(tài),也就是上鎖和解鎖,可以把互斥鎖看作某種意義上的全局變量。 在同一時刻只能有一個線程掌握某個互斥鎖,擁有上鎖狀態(tài)的線程能夠?qū)蚕碣Y源進行操作。若其他線程希望上鎖一個已經(jīng)被上鎖的互斥鎖,則該線程就會掛起,直到上鎖的線程釋放掉互斥鎖為止。846.4.2 線程之間的同步和互斥互斥鎖機制主要包括下面的基本函數(shù)。 互斥鎖初始化:pthread_mutex_init() 互斥鎖上鎖:pthread_mutex_lock() 互斥鎖判斷上鎖:pthread_mutex_trylock() 互斥鎖接鎖:pthread_mutex_unlock()

43、消除互斥鎖:pthread_mutex_destroy()互斥鎖可以分為快速互斥鎖、遞歸互斥鎖和檢錯互斥鎖。這三種鎖的區(qū)別主要在于其他未占有互斥鎖的線程在希望得到互斥鎖時是否需要阻塞等待。快速鎖是指調(diào)用線程會阻塞直至擁有互斥鎖的線程解鎖為止。遞歸互斥鎖能夠成功地返回,并且增加調(diào)用線程在互斥上加鎖的次數(shù),而檢錯互斥鎖則為快速互斥鎖的非阻塞版本,它會立即返回并返回一個錯誤信息。默認屬性為快速互斥鎖。 856.4.2 線程之間的同步和互斥互斥鎖線程控制pthread_mutex_init()函數(shù)語法:866.4.2 線程之間的同步和互斥互斥鎖線程控制pthread_mutex_lock()函數(shù)語法:

44、876.4.2 線程之間的同步和互斥可通過條件變量和互斥鎖組合使用來形成線程的一種同步機制。條件變量主要包括如下基本函數(shù):(1)條件變量初始化:pthread_cond_init()。(2)條件等待:pthread_cond_wait()。(3)發(fā)送條件:pthread_cond_signal()。(4)廣播條件:pthread_cond_broadcast()。(5)消除條件變量:pthread_cond_destroy()。886.4.2 線程之間的同步和互斥89pthread_cond_init()函數(shù)語法6.4.2 線程之間的同步和互斥90pthread_cond_wait()函數(shù)語法

45、6.4.2 線程之間的同步和互斥91pthread_cond_broadcast()pthread_cond_signal()pthread_cond_destroy()函數(shù)語法6.4.2 線程之間的同步和互斥信號量線程控制信號量也就是操作系統(tǒng)中所用到的PV原子操作,它廣泛用于進程或線程間的同步與互斥。信號量本質(zhì)上是一個非負的整數(shù)計數(shù)器,它被用來控制對公共資源的訪問。 PV原子操作是對整數(shù)計數(shù)器信號量sem的操作。一次P操作使sem減一,而一次V操作使sem加一。進程(或線程)根據(jù)信號量的值來判斷是否對公共資源具有訪問權(quán)限。當信號量sem的值大于等于零時,該進程(或線程)具有公共資源的訪問權(quán)限

46、;相反,當信號量sem的值小于零時,該進程(或線程)就將阻塞直到信號量sem的值大于等于0為止。926.4.2 線程之間的同步和互斥信號量線程控制PV原子操作主要用于進程或線程間的同步和互斥這兩種典型情況。 93互斥同步6.4.2 線程之間的同步和互斥信號量線程控制Linux實現(xiàn)了POSIX的無名信號量,主要用于線程間的互斥與同步。這里主要介紹幾個常見函數(shù)。sem_init()用于創(chuàng)建一個信號量,并初始化它的值。sem_wait()和sem_trywait()都相當于P操作,在信號量大于零時它們都能將信號量的值減一,兩者的區(qū)別在于若信號量小于零時,sem_wait()將會阻塞進程,而sem_t

47、rywait()則會立即返回。sem_post()相當于V操作,它將信號量的值加一同時發(fā)出信號來喚醒等待的進程。sem_getvalue()用于得到信號量的值。sem_destroy()用于刪除信號量。946.4.2 線程之間的同步和互斥信號量線程控制sem_init()函數(shù)語法:956.4.2 線程之間的同步和互斥信號量線程控制sem_wait()函數(shù)語法:966.4.3 線程屬性pthread_create()函數(shù)的第二個參數(shù)(pthread_attr_t *attr)表示線程的屬性。如果該值設為NULL,就是采用默認屬性,線程的多項屬性都是可以更改的。這些屬性主要包括綁定屬性、分離屬性、

48、堆棧地址、堆棧大小以及優(yōu)先級。其中系統(tǒng)默認的屬性為非綁定、非分離、缺省1M的堆棧以及與父進程同樣級別的優(yōu)先級。 976.4.3 線程屬性綁定屬性綁定屬性就是指一個用戶線程固定地分配給一個內(nèi)核線程,因為CPU時間片的調(diào)度是面向內(nèi)核線程(也就是輕量級進程)的,因此具有綁定屬性的線程可以保證在需要的時候總有一個內(nèi)核線程與之對應。而與之對應的非綁定屬性就是指用戶線程和內(nèi)核線程的關(guān)系不是始終固定的,而是由系統(tǒng)來控制分配的。 分離屬性 分離屬性是用來決定一個線程以什么樣的方式來終止自己。 986.4.3 線程屬性pthread_attr_init()pthread_attr_init()函數(shù)對屬性進行初始

49、化pthread_attr_init()函數(shù)語法: 996.4.3 線程屬性pthread_attr_setscope()pthread_attr_setscope()函數(shù)設置線程綁定屬性pthread_attr_setscope()函數(shù)語法: 1006.4.3 線程屬性pthread_attr_setdetachstate() pthread_attr_setdetachstate()函數(shù)設置線程分離屬性pthread_attr_setdetachstate()函數(shù)語法: 1016.4.3 線程屬性pthread_attr_getschedparam() pthread_attr_getsc

50、hedparam()函數(shù)獲得線程優(yōu)先級pthread_attr_getschedparam()函數(shù)語法: 1026.4.3 線程屬性pthread_attr_setschedparam()pthread_attr_setschedparam()函數(shù)設置線程優(yōu)先級pthread_attr_setschedparam()函數(shù)語法: 1036.4.4 線程私有數(shù)據(jù)所謂線程私有數(shù)據(jù)是除局部數(shù)據(jù)和全局數(shù)據(jù)的第三類數(shù)據(jù),這個概念是隨著線程的誕生而提出來的。假設你的一個線程里面嵌套調(diào)用了很多函數(shù),而你又需要在這些函數(shù)之間使用一個公共的變量,如果在單線程環(huán)境中,我們是不是聲明一個全局變量就解決問題了呢?但是我

51、們想使這個“公共變量”只屬于我們當前這個實例線程,其他線程訪問不到,所以引入了線程私有數(shù)據(jù)。1046.4.4 線程私有數(shù)據(jù)1051.創(chuàng)建鍵值:用于將每個線程的私有數(shù)據(jù)都和進程中的鍵值數(shù)組成員一一對應。pthread_key_create()語法要點:6.4.4 線程私有數(shù)據(jù)1062.確保同一個私有數(shù)據(jù)對應的鍵值只被創(chuàng)建一次且唯一pthread_once()語法要點:6.4.4 線程私有數(shù)據(jù)1073.每個線程通過鍵值獲取鍵值相對應的私有數(shù)據(jù)pthread_getspecific()語法要點6.4.4 線程私有數(shù)據(jù)1084.將鍵值與私有數(shù)據(jù)綁定pthread_setspecific()語法要點6.5實驗內(nèi)容1096.5.1 編寫多進程程序?qū)嶒災康耐ㄟ^編寫多進程程序,使讀者熟練掌握fork()、exec()、wait()和waitpid()等函數(shù)的使用,進一步理解在Linux中多進程編程的步驟。實驗內(nèi)容該實驗有3個進程,其中一個為父進程,其余兩個是該父進程創(chuàng)建的子進程,其中一個子進程運行“l(fā)s -l”指令,另一個子進程在暫停5s之后異常退出,父進程先用阻塞方式等待第一個子進程的結(jié)束,然后

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論