第6章 嵌入式Linux進程和線程編程 LinuxC語言開發(fā)_第1頁
第6章 嵌入式Linux進程和線程編程 LinuxC語言開發(fā)_第2頁
第6章 嵌入式Linux進程和線程編程 LinuxC語言開發(fā)_第3頁
第6章 嵌入式Linux進程和線程編程 LinuxC語言開發(fā)_第4頁
第6章 嵌入式Linux進程和線程編程 LinuxC語言開發(fā)_第5頁
已閱讀5頁,還剩76頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、嵌入式Linux進程和線程編程課程目標進程相關的根本概念Linux進程的創(chuàng)立Linux進程的管理和守護進程Linux進程間通信的方式Linux中線程創(chuàng)立和退出Linux中線程間的同步和互斥 本章內容6.1 Linux進程概述 6.2 Linux進程控制相關API 6.3 ARM Linux進程間通信 6.4 ARM Linux線程相關API 6.5 Linux守護進程 本章小結6.1 Linux進程概述6.1.1 進程描述符及任務結構 6.1.2 進程的調度 6.1.3 Linux中的線程6.1.1 進程描述符及任務結構進程概念 進程的概念首先在20世紀60年代初期由MIT的Multics系統

2、和IBM的TSS/360系統中引入的。 1進程是一個獨立的可調度的活動,。2進程是一個抽象實體,當它執(zhí)行某個任務時,將要分配和釋放各種資源。3進程是可以并行執(zhí)行的計算局部,。進程和程序有本質的區(qū)別:程序是靜態(tài)的,它是一些保存在磁盤上的指令的有序集合,沒有任何執(zhí)行的概念;而進程是一個動態(tài)的概念,它是程序執(zhí)行的過程,包括了動態(tài)創(chuàng)立、調度和消亡的整個過程,它是程序執(zhí)行和資源管理的最小單位。 6.1.1 進程描述符及任務結構Linux中進程描述符進程不但包括程序的指令和數據,而且包括程序計數器和CPU的所有存放器以及存儲臨時數據的進程堆棧。內核把進程存放在任務隊列task list的雙向循環(huán)鏈表中,其

3、中鏈表的每一項都是類型為task_struct,稱為進程描述符的結構,該結構定義在中 。6.1.1 進程描述符及任務結構Linux中進程描述符Linux通過slab分配器分配task_struct結構,它實際上是一個棧,其棧頂向上增長的棧或棧底向下增長的棧中有一個thread info結構,其中的task指針指向task_struct。下面詳細講解task_struct結構中最為重要的兩個域:state和pid。6.1.1 進程描述符及任務結構Linux中進程描述符1進程狀態(tài)運行TASK_RUNNING)可中斷TASK_INTERUPTIBLE)不可中斷TASK_UNINTERUPTIBLE)

4、僵死TASK_ZOMBIE)停止TASK_STOPPED)6.1.1 進程描述符及任務結構Linux中進程描述符2任務標識Linux內核通過惟一的進程標識值PID來標識每個進程。PID是一個非負數,它實際上是一個短整型數據,也就是說它最大值為32767。讀者可以查看/proc/sys/kernel/pid_max來確定該系統的進程數上限。一般來說,32767對于很多桌面系統已經足夠,但是對于大型效勞器,就必須修改這個上限。6.1.1 進程描述符及任務結構進程的創(chuàng)立、執(zhí)行和終止 1進程的創(chuàng)立和執(zhí)行許多操作系統都提供的是產生進程的機制,也就是首先在新的地址空間里創(chuàng)立進程、讀入可執(zhí)行文件,最后再開始

5、執(zhí)行。 首先,fork()通過拷貝當前進程的內容創(chuàng)立一個子進程,子進程與父進程的區(qū)別僅僅在于不同的PID、PPID和其他一些資源。exec函數負責讀取可執(zhí)行文件并將其載入地址空間開始運行。6.1.1 進程描述符及任務結構進程的創(chuàng)立、執(zhí)行和終止 2進程的終止進程終結也需要做很多繁瑣的收尾工作,系統必須保證進程所占用的資源回收,并通知父進程。Linux首先把終止的進程設置為僵死狀態(tài),這個時候,進程無法投入運行了。它的存在只為父進程提供信息,申請死亡。父進程得到信息后,開始調用wait (),子進程占用的資源被全部釋放。6.1.2 進程的調度Linux中進程調度概述 Linux中進程調度算法 Lin

6、ux中進程調度概述進程調度是指確定CPU當前執(zhí)行哪個進程。Linux進程調度策略是以優(yōu)先級調度為根底的,即優(yōu)先運行優(yōu)先級最高的進程。根據優(yōu)先級的范圍,可以把進程分為實時進程這里的實時是軟實時和普通進程。實時進程優(yōu)先級高于普通進程,并由特定的調度策略來保證它們的軟實時性。內核中的默認配置是:進程優(yōu)先級在0139,其中實時進程占用099,一般進程占用100139。在調度時,系統總是首先選取具有最高優(yōu)先級的并且擁有活潑進程的進程組,然后進行相同優(yōu)先級下的進程調度。 Linux中進程調度算法內核中實現了一個O(1)的調度算法,也就是說每一次調度所需要的時間與該CPU內的總進程數無關 。Linux中每個

7、運行隊列都有兩個優(yōu)先級數組,一個活潑的和一個過期的。優(yōu)先級數組在中被定義,它是prio_array類型的結構體。struct prio_array unsigned int nr_active; /* 當前活潑的進程總數 */ unsigned long bitmapBITMAP_SIZE;/* 活潑進程的位圖 */ struct list_head queueMAX_PRIO; /* 各個優(yōu)先級隊列的頭指針組成的數組*/;6.1.3 Linux中的線程線程機制是現代編程技術中常用的一種抽象,該機制提供了在同一程序內共享內存地址空間運行的一組線程。這些線程可以共享翻開的文件和其他資源等。Lin

8、ux中實現線程的機制非常獨特。從內核的角度來說,它并沒有線程這個概念。Linux把線程都當作進程來實現,僅僅將其視為使用某些共享資源的進程。每個線程都用有惟一隸屬于自己的task_struct,所以在內核中,它看起來就像一個普通的進程 。6.2 Linux進程控制相關API進程的創(chuàng) 建fork1fork函數說明執(zhí)行一次卻返回兩個值,父進程的返回值是子進程的進程號,而子進程那么返回0 。 2fork函數語法fork函數的語法格式如下所示。頭文件#include / 提供類型pid_t的定義#include 函數原型pid_t fork(void);函數返回值0:子進程子進程ID大于0的整數:父進

9、程-1:出錯6.2 Linux進程控制相關API進程的創(chuàng) 建3fork函數調用實例/*調用fork函數,其返回值賦給result*/int result = fork();/*通過result的值來判斷fork函數的返回情況,首先進行出錯處理*/if(result = -1) perror(fork); exit(-1);/*返回值為0代表子進程*/else if(result = 0) /*子進程相關語句*/*返回值大于0代表父進程*/else /*父進程相關語句*/6.2 Linux進程控制相關API進程的創(chuàng) 建exec函數族1exec函數族說明fork函數是用于創(chuàng)立一個子進程,該子進程幾

10、乎拷貝了父進程的全部內容。 exec函數族就提供了一個在進程中啟動另一個程序執(zhí)行的方法。它可以根據指定的路徑和文件名找到可執(zhí)行文件,并用它來取代原調用進程的數據段、代碼段和堆棧段,在執(zhí)行完之后,原調用進程的內容除了進程號外,其他全部被新的內容替換了。另外,這里的可執(zhí)行文件既可以是二進制文件,也可以是Linux下任何可執(zhí)行的腳本文件。6.2 Linux進程控制相關API進程的創(chuàng) 建exec函數族2exec函數族語法實際上,在Linux中并沒有exec函數,而是以exec開頭的函數族,它們之間語法有細微差異。exec函數族語法格式如下所示。 頭文件#include 函數原型int execl(co

11、nst 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 *co

12、nst argv);函數返回值出錯1:-16.2 Linux進程控制相關API進程的創(chuàng) 建exec函數族2exec函數族語法6.2 Linux進程控制相關API進程的創(chuàng) 建exec函數族3exec函數組調用實例使用execlp函數,采用逐個列舉方式,并且使用系統默認的環(huán)境變量。使用execl函數時需要給出完整的文件路徑來查找對應的可執(zhí)行文件。if(fork()=0)/*調用execlp函數,這里相當于調用了“l(fā)s -l命令*/ if(execlp(ls,ls,-l,NULL)0) perror(execlp error!);/*調用execl函數,注意這里要給出ls程序所在的完整路徑*/if(

13、execl(/bin/ls,ls,-l,NULL)0) perror(execl error!);6.2 Linux進程控制相關API進程的創(chuàng) 建exec函數族3exec函數組調用實例使用execle時可以將環(huán)境變量添加到新建的子進程中去 使用execve函數時,通過構造指針數組的方式來傳遞參數,注意參數列表一定要以NULL作為結尾標識符。/*命令參數列表,必須以NULL結尾*/char *envp=PATH=/tmp,USER=sunq,NULL;/*調用execle函數,注意這里也要指出env的完整路徑*/if(execle(/bin/env,env,NULL,envp)0) perror

14、(execle error!);/*命令參數列表,必須以NULL結尾*/char *arg=env,NULL;char *envp=PATH=/tmp,USER=sunq,NULL;if(execve(/bin/env,arg,envp)0) perror(execve error!);6.2 Linux進程控制相關API進程的創(chuàng) 建exit和_exit1exit和_exit函數說明exit和_exit函數都是用來終止進程的。當程序執(zhí)行到exit或_exit時,進程會無條件地停止剩下的所有操作,去除包括PCB在內的各種數據結構,并終止本進程的運行。exit()函數與_exit()函數的區(qū)別就在

15、于exit()函數在調用exit系統調用之前要檢查進程中文件的翻開情況,把文件緩沖區(qū)中的內容寫回文件。如果用_exit()函數直接將進程關閉,緩沖區(qū)中的數據就會喪失。因此,假設想保證數據的完整性,就要使用exit()函數。6.2 Linux進程控制相關API進程的創(chuàng) 建exit和_exit2exit和_exit函數語法exit和_exit函數的語法如下所示。頭文件exit:#include _exit:#include 函數原型void exit/_exit(int status); /*利用該參數傳遞進程結束時的狀態(tài)。一般來說,0表示正常結束;其他的數值表示出現了錯誤,進程非正常結束*/3e

16、xit和_exit使用實例exit和_exit函數的調用很簡單,輸入狀態(tài)參數即可,如下所示:exit(0);_exit(-1);6.2 Linux進程控制相關API進程的創(chuàng) 建wait和waitpid 1wait和waitpid函數說明wait函數可以使父進程也就是調用wait的進程阻塞,直到任意一個子進程結束或者父進程接到了一個指定的信號為止。如果該父進程沒有子進程或者他的子進程已經結束,那么wait函數會立即返回。waitpid的作用和wait一樣,但它并不一定要等待第一個終止的子進程。它還有假設干選項,如可提供一個非阻塞版本的wait功能,也能支持作業(yè)控制。實際上wait函數只是wait

17、pid函數的一個特例,在Linux內部實現wait函數時直接調用的就是waitpid函數。6.2 Linux進程控制相關API進程的創(chuàng) 建wait和waitpid 2wait和waitpid函數格式說明wait函數的語法標準如下所示。 頭文件#include #include 函數原型pid_t wait(int *status) /*等待子進程結束,同時接受子進程退出時的狀態(tài)*/pid_t waitpid(pid_t pid, /*等待結束的進程類型*/ int *status, /*同wait*/ int options) /*選項*/這里的status假設為空,那么代表任意狀態(tài)結束的子進

18、程;status假設不為空,那么代表指定狀態(tài)結束的子進程。6.2 Linux進程控制相關API進程的創(chuàng) 建wait和waitpid 2wait和waitpid函數格式說明 函數返回值成功:子進程的進程號或0調用成功子但進程還未退出失?。?16.2 Linux進程控制相關API進程的創(chuàng) 建wait和waitpid 3waitpid使用實例wait函數的使用非常簡單,只需要在父進程處調用即可。調用wait后父進程就會阻塞自己,直到有相應的子進程退出為止。waitpid函數支持不同的參數,可以通過指定WNOHANG使父進程以非阻塞的方式檢查子進程是否結束,其調用過程如下所示:/*調用waitpid,

19、且父進程不阻塞*/pr=waitpid(pid,NULL,WNOHANG);6.2 Linux進程控制相關API進程的創(chuàng) 建防止僵死進程實例當一個進程已經終止,但是其父進程尚未對其進行回收獲得終止子進程的有關信息,釋放它占用的資源的進程被稱為僵死進程。為了防止僵死進程的出現,一種方法是父進程調用wait/waitpid等待子進程結束,但這樣做有一個弊端就是在子進程結束前父進程會一直阻塞,不能做任何事情。另外一種更好的方法就是調用兩次fork函數。 int main() pid_t pid; if(pid = fork() 0) / 創(chuàng)立子進程1 perror(fork); else if(pi

20、d = 0) / 子進程1 if(pid = fork() 0) exit(0); / 子進程1結束 else / 子進程2 sleep(2); /*打印子進程2的父進程號*/ printf(second child, parent pid = %dn, getppid(); exit(0); else / 父進程 / do something else 6.3 ARM Linux進程間通信Linux下的進程通信方式根本上是從Unix平臺上繼承而來的。而對Unix開展做出重大奉獻的兩大主力AT&T的貝爾實驗室及BSD在進程間的通信方面的側重點有所不同。前者是對Unix早期的進程間通信手段進行了

21、系統的改進和擴充,形成了“System V IPC,其通信進程主要局限在單個計算機內;后者那么跳過了該限制,形成了基于套接口socket的進程間通信機制。6.3 ARM Linux進程間通信 Unix 進程間通信IPC方式包括管道、FIFO、信號。 System V進程間通信IPC包括System V消息隊列、System V信號燈、System V共享內存區(qū)。 Posix 進程間通信IPC包括Posix消息隊列、Posix信號燈、Posix共享內存區(qū)。 現在在Linux中使用較多的進程間通信方式主要有以下幾種。1管道Pipe及有名管道named pipe2信號Signal3消息隊列4共享內存

22、5信號量6套接字Socket6.3.1 管道通信管道概述 管道是Linux中進程間通信的一種方式,它把一個程序的輸出直接連接到另一個程序的輸入。Linux的管道主要包括兩種:無名管道有名管道。無名管道 無名管道是Linux中管道通信的一種原始方法,如下圖,它具有如下特點。它只能用于具有親緣關系的進程之間的通信也就是父子進程或者兄弟進程之間。它是一個半雙工的通信模式,具有固定的讀端和寫端。管道也可以看成是一種特殊的文件,對于它的讀寫也可以使用普通的read、write等函數。但是它不是普通的文件,并不屬于其他任何文件系統,并且只存在于內存中。有名管道FIFO 有名管道是對無名管道的一種改進,如下

23、圖,它具有如下特點:它可以使互不相關的兩個進程實現彼此通信。該管道可以通過路徑名來指出,并且在文件系統中是可見的。在建立了管道之后,兩個進程就可以把它當作普通文件一樣進行讀寫操作,使用非常方便。FIFO嚴格地遵循先進先出規(guī)那么,對管道及FIFO的讀總是從開始處返回數據,對它們的寫那么把數據添加到末尾,它們不支持如lseek()等文件定位操作。6.3.1 管道通信有名管道的創(chuàng)立1函數說明有名管道的創(chuàng)立可以使用函數mkfifo,該函數類似文件中的open操作,可以指定管道的路徑和讀寫權限。普通文件在讀寫時不會出現阻塞問題,而在管道的讀寫中卻有阻塞的可能。這里的非阻塞標志可以在open函數中設定為O

24、_NONBLOCK。 對于讀進程 假設該管道是以阻塞方式翻開,假設當前FIFO內沒有數據,那么讀進程將一直阻塞,直到有數據寫入。 假設該管道是以非阻塞方式翻開,那么不管FIFO內是否有數據,讀進程都會立即執(zhí)行讀操作。 對于寫進程 假設該管道是以阻塞方式翻開,那么寫進程將一直阻塞直到有讀進程讀出數據。 假設該管道是以非阻塞方式翻開,那么無論當前FIFO內有沒有讀操作,寫進程都會立即執(zhí)行寫操作 6.3.1 管道通信有名管道的創(chuàng)立2函數格式定義mkfifo函數格式如下所示: 頭文件#include #include 函數原型int mkfifo(const char *filename, /* 要創(chuàng)

25、立的管道*/ mode_t mode) /*管道文件的讀寫權限*/ 函數返回值成功:0出錯:-16.3.1 管道通信有名管道的創(chuàng)立3函數調用實例假設要使用有名管道的方式來進行進程間通信,那么必須首先調用mkfifo函數創(chuàng)立管道,創(chuàng)立后用戶可以調用函數open、read、write來實現對管道的讀寫,如下所示:/*創(chuàng)立有名管道,并設置相應的權限*/mkfifo(FIFO,0666);/*翻開有名管道,并設置非阻塞標志*/fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);6.3.2 信號通信信號概述信號是進程間通信機制中惟一的異步通信方式,可以看作是異步通知,通知接收信號的進

26、程有哪些事情發(fā)生了。信號事件的發(fā)生有兩個來源:硬件來源比方我們按下了鍵盤或者其他硬件故障;軟件來源,最常用發(fā)送信號的系統函數是kill、raise、alarm、setitimer和sigqueue函數,軟件來源還包括一些非法運算等操作。進程可以通過3種方式來響應一個信號。1忽略信號2捕捉信號3執(zhí)行缺省操作6.3.2 信號通信信號概述一個完整的信號生命周期可以分為3個階段。這3個階段由4個重要事件來刻畫的:信號產生、信號在進程中注冊、信號在進程中注銷、執(zhí)行信號處理函數。 6.3.2 信號通信信號概述信號的處理包括信號的發(fā)送、捕獲以及信號的處理,它們各自相對應的常見函數有發(fā)送信號的函數:kill(

27、)、raise()。捕獲信號的函數:alarm()、pause()。設置信號處理的函數:signal()。6.3.2 信號通信kill()和raise() 1函數說明kill函數可以發(fā)送信號給進程或進程組。它不僅可以中止進程,也可以向進程發(fā)送其他信號。與kill函數所不同的是,raise函數只允許進程向自身發(fā)送信號。2函數格式kill和raise函數的語法要點如下所示。 頭文件#include #include 函數原型int kill(pid_t pid, /*指明要接收信號的進程號*/ int sig) /*信號,表中的數值*/int raise(int sig) /*信號,表中的數值*/

28、6.3.2 信號通信kill()和raise() 函數返回值成功:0出錯:-13函數調用實例這兩個函數的調用都比較簡單,如下所示:raise(SIGSTOP);kill(pid,SIGKILL);6.3.2 信號通信alarm()和pause() 1函數說明alarm也稱為鬧鐘函數,它可以在進程中設置一個定時器,當定時器指定的時間到時,它就向進程發(fā)送SIGALARM信號。 pause函數是用于將調用進程掛起直至捕捉到信號為止。 2函數格式alarm和pause函數的語法要點如下所示。 頭文件#include 函數原型unsigned int alarm(unsigned int seconds

29、) /*指定秒數*/int pause(void)6.3.2 信號通信alarm()和pause() 函數返回值成功:如果調用此alarm前,進程中已經設置了鬧鐘時間,那么返回上一個鬧鐘時間的剩余時間,否那么返回0。出錯:-1,并且把error值設為EINTR3函數調用實例這兩個函數的調用很簡單,如下所示:ret=alarm(5);pause();由于SIGALARM默認的系統動作為終止該進程,因此在收到該信號之后程序就終止了。6.3.2 信號通信signal() 1函數說明一個進程可以決定在該進程中需要對哪些信號進行什么樣的處理。使用signal函數時,只需把要處理的信號和處理函數列出即可。

30、它主要是用于前32種非實時信號的處理,不支持信號傳遞信息。 2函數格式signal函數的語法要點如下所示。 頭文件#include 函數原型void (*signal(int signum, /*指定信號*/ void (*handler)(int)(int) /*對信號的處理*/6.3.2 信號通信signal() 函數返回值成功:以前的信號處理配置 出錯:-13使用實例使用signal函數時通常用于自定義信號處理函數handler的第3種情況。首先自定義了信號處理函數,接著再使用signal函數處理相應的信號。/*這里的my_func是自定義信號處理函數*/signal(SIGINT, m

31、y_func);signal(SIGQUIT, my_func);6.3.2 信號通信具有超時限制的read調用 通常的read函數并沒有超時限制的功能。如果讀取的設備是一個低速設備,可能需要等待一段時間才會讀取成功。這里通過使用alarm定時函數來給read函數設置超時時限10s。假設定時器到時,就會向進程發(fā)送SIGALRM信號,從而調用函數sig_alrm。int main(void) int n; char lineMAXLINE; /*設定超時時限*/ alarm(10); /*信號注冊函數*/ if(signal(SIGALRM, sig_alrm) = SIG_ERR) perro

32、r(signal); exit(-1); 6.3.2 信號通信if(n = read(STDIN_FILENO, line, MAXLINE) 0) perror(read); write(STDOUT_FILENO, line, n); exit(0);static void sig_alrm(int signo) printf(in here alarmn); exit(0); 6.3.3 共享內存共享內存概述共享內存允許兩個或更多進程共享一給定的內存區(qū)域。因為數據不需要在各個進程之間復制,所以這是最高效的一種進程間通信方式。使用共享內存的關鍵在于如何在多個進程之間對一給定的存儲區(qū)進行同步

33、訪問。 6.3.3 共享內存函數說明 共享內存的實現分為3個步驟。第一步是創(chuàng)立共享內存,這里用到的函數是shmget,也就是從內存中獲得一段共享內存區(qū)域。第二步映射共享內存,也就是把這段創(chuàng)立的共享內存映射到具體的進程空間去,這里使用的函數是shmat。接著就可以使用這段共享內存了,即可以使用不帶緩沖的I/O讀寫命令對其進行操作。第三步是撤銷映射的操作,其函數為shmdt。6.3.3 共享內存函數格式 函數的頭文件如下所示。#include #include #include shmget函數的語法要點如下所示。 函數原型int shmget(key_t key, /*IPC_PRIVATE *

34、/ int size, /*共享內存區(qū)大小*/ int shmflg) /*同open函數的權限位,也可以用8進制表示法*/ 函數返回值成功:共享內存段標識符出錯:-16.3.3 共享內存函數格式 shmat函數的語法要點如下所示。 函數原型char *shmat(int shmid, /*要映射的共享內存區(qū)標識符*/ const void *shmaddr, /*將共享內存映射到指定位置假設為0那么表示把該段共享內存映射到調用進程的地址空間*/ int shmflg) /*SHM_RDONLY:共享內存只讀。默認0,共享內存可讀寫*/ 函數返回值成功:被映射的段地址出錯:-1shmdt函數的

35、語法如下所示。 函數原型int shmdt(const void *shmaddr) /*被映射的共享內存段地址*/ 函數返回值成功:0出錯:-16.3.3 共享內存使用實例 這里要介紹的一個命令是ipcs,這是用于顯示進程間通信狀態(tài)的命令。它可以查看共享內存、消息隊列等各種進程間通信機制對象的情況,這里使用了system函數來調用命令ipcs,函數,如下所示:/*創(chuàng)立共享內存*/shmget(IPC_PRIVATE,BUFSZ,0666);/*映射共享內存*/shmat(shmid,0,0);/*刪除共享內存*/shmdt(shmadd);6.3.4 消息隊列消息隊列概述 消息隊列就是一個消

36、息的鏈表。用戶可以把消息看作一個記錄,具有特定的格式以及特定的優(yōu)先級。對消息隊列有寫權限的進程可以向中按照一定的規(guī)那么添加新消息;對消息隊列有讀權限的進程那么可以從消息隊列中讀走消息,消息隊列是隨內核持續(xù)的。6.3.4 消息隊列消息隊列實現說明 消息隊列的實現包括創(chuàng)立或翻開消息隊列、添加消息、讀取消息和控制消息隊列這4種操作。 創(chuàng)立或翻開消息隊列使用函數msgget,這里創(chuàng)立的消息隊列的數量會受到系統消息隊列數量的限制。 添加到消息隊列使用函數msgsnd,它把消息添加到已翻開的消息隊列末尾。 讀取消息隊列內容使用函數msgrcv,它把消息從消息隊列中取走,與FIFO不同的是,這里可以指定取走

37、某一類型的消息。 控制消息隊列使用函數msgctl,它可以完成多項功能。6.3.4 消息隊列函數格式 用到的頭文件如下所示:#include #include #include msgget函數的語法要點如下所示 函數原型int msgget(key_t key, /*返回新的或已有隊列的隊列ID、IPC_PRIVATE */ int flag) 函數返回值成功:消息隊列ID出錯:-1msgsnd函數的語法要點如下所示。 函數原型int msgsnd(int msqid, /*消息隊列的隊列ID */ const void *prt, /*指向消息結構的指針*/ size_t size, /*

38、消息的字節(jié)數*/ int flag)/*有兩種取值情況:IPC_NOWAIT假設消息并沒有立即發(fā)送而調用進程會立即返回。0:msgsnd調用阻塞直到條件滿足為止*/6.3.4 消息隊列這里prt消息的結構如下所示:struct msgbuflong mtype;/消息類型char mtextLEN;/消息正文, LEN由用戶指定 函數返回值成功:0出錯:-1msgrcv函數的語法要點如下所示。 函數原型int msgrcv(int msgid, /*消息隊列的隊列ID */ struct msgbuf *msgp,/* 消息緩沖區(qū)*/ int size, /*消息的字節(jié)數*/ long msg

39、type, /*接收的消息類型*/ int flag) /*類型符*/ 函數返回值成功:0出錯:-1flag含 義MSG_NOERROR若返回的消息比size字節(jié)多,則消息就會截短到size字節(jié),且不通知消息發(fā)送進程IPC_NOWAIT若消息并沒有立即發(fā)送而調用進程會立即返回0msgsnd調用阻塞直到條件滿足為止6.3.4 消息隊列msgctl函數的語法要點如下所示。 函數原型int msgctl(int msgqid, /*消息隊列的隊列ID*/ int cmd, /*消息隊列控制選項*/ struct msqid_ds *buf ) /*消息隊列緩沖區(qū)*/ 函數返回值成功:0出錯:-1cm

40、d含 義IPC_STAT讀取消息隊列的數據結構msqid_ds,并將其存儲在buf指定的地址中IPC_SET設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值,這個值取自buf參數IPC_RMID從系統內核中移走消息隊列6.3.4 消息隊列使用實例在使用消息隊列前,可以先使用函數ftok,根據不同的路徑和關鍵表示產生標準的key。之后使用msgget等函數對消息隊列進行操作,如下所示:/*自定義消息格式*/struct message msg;/*創(chuàng)立消息隊列*/msgget(key,IPC_CREAT|0666);/*添加消息到消息隊列*/msgsnd(qid,&msg,le

41、n,0);/*讀取消息隊列*/msgrcv(qid,&msg,BUFSZ,0,0);/*從系統內核中移走消息隊列。*/msgctl(qid,IPC_RMID,NULL); 6.4 ARM Linux線程相關API 線程創(chuàng)立和退出 1函數說明使用線程主要包括以下幾個步驟。 創(chuàng)立線程 調用相關線程函數 線程退出 線程資源回收6.4 ARM Linux線程相關API 線程創(chuàng)立和退出 2函數格式#include pthread_create函數的語法要點如下所示。 函數原型int pthread_create( pthread_t *thread, /*線程標識符*/ pthread_attr_t *

42、attr, /*線程屬性設置,默認為NULL,可以使用其他函數來設置*/ void *(*start_routine)(void *),/*線程函數的起始地址*/ void *arg) /*傳遞給start_routine的參數*/ 函數返回值成功:0出錯:-16.4 ARM Linux線程相關API線程創(chuàng)立和退出 2函數格式pthread_exit函數的語法要點如下所示。 函數原型void pthread_exit(void *retval) /*線程的返回值,可由其他函數如pthread_join來檢索獲取*/pthread_join函數的語法要點如下所示。 函數原型int pthread

43、_join(pthread_t thread, /*等待線程的標識符*/ void *thread_return) /*用戶定義的指針,用來存儲被等待線程的返回值不為NULL時*/ 函數返回值成功:0出錯:-16.4 ARM Linux線程相關API線程創(chuàng)立和退出 3函數調用實例在使用pthread_create函數時,通常可以將所要傳遞給線程函數的參數寫成一個結構體,傳入到該函數中。pthread_join函數那么使用pthread_create函數的id等待線程退出,該函數調用源碼如下所示:void thread(void)/*具體線程函數*/*主函數中創(chuàng)立線程*/ret=pthread_

44、create(&id,NULL,(void *) thread,NULL);/*等待線程結束*/pthread_join(id,NULL); / 不處理子線程退出時返回的值6.4 ARM Linux線程相關API mutex線程訪問控制 1mutex互斥鎖函數說明mutex是一種簡單的加鎖的方法來控制對共享資源的存取。這個互斥鎖只有兩種狀態(tài),也就是上鎖和解鎖,可以把互斥鎖看作某種意義上的全局變量?;コ怄i可以分為快速互斥鎖、遞歸互斥鎖和檢錯互斥鎖,這3種鎖的區(qū)別主要在于其他未占有互斥鎖的線程在希望得到互斥鎖時的是否需要阻塞等待。快速鎖是指調用線程會阻塞直至擁有互斥鎖的線程解鎖為止。遞歸互斥鎖能夠

45、成功地返回并且增加調用線程在互斥上加鎖的次數。檢錯互斥鎖那么為快速互斥鎖的非阻塞版本,它會立即返回并返回一個錯誤信息。6.4 ARM Linux線程相關APImutex線程訪問控制 1mutex互斥鎖函數說明互斥鎖的操作主要包括以下幾個步驟。 互斥鎖初始化:pthread_mutex_init。 互斥鎖上鎖:pthread_mutex_lock。 互斥鎖判斷上鎖:pthread_mutex_trylock。 互斥鎖接鎖:pthread_mutex_unlock。 消除互斥鎖:pthread_mutex_destroy。6.4 ARM Linux線程相關APImutex線程訪問控制 2函數格式這

46、幾個函數都需要包含同樣的頭文件,如下所示:#include pthread_mutex_init函數的語法要點如下所示。 函數原型int pthread_mutex_init(pthread_mutex_t *mutex,/*互斥鎖*/ const pthread_mutexattr_t*mutexattr) /*創(chuàng)立互斥鎖的方法*/int pthread_mutex_lock(pthread_mutex_t *mutex)/*互斥鎖*/int pthread_mutex_trylock(pthread_mutex_t *mutex)/*互斥鎖*/int pthread_mutex_unloc

47、k(pthread_mutex_t *mutex)/*互斥鎖*/int pthread_mutex_destroy(pthread_mutex_t *mutex)/*互斥鎖*/6.4 ARM Linux線程相關APImutex線程訪問控制 2函數格式 函數返回值成功:0出錯:-13使用實例在使用互斥鎖時,通常首先pthread_mutex_lock上鎖,然后執(zhí)行需要原子操作的代碼,最后再使用pthread_mutex_unlock解鎖。/*互斥鎖上鎖*/pthread_mutex_lock(&mutex);/*需原子操作的代碼*/*互斥鎖接鎖*/pthread_mutex_unlock(&mu

48、tex);6.4 ARM Linux線程相關API 信號量線程控制 1信號量說明信號量也就是操作系統中所用到的PV原語,它廣泛用于進程或線程間的同步與互斥。 PV原語是對整數計數器信號量sem的操作。一次P操作使sem減一,而一次V操作使sem加一。進程或線程根據信號量的值來判斷是否對公共資源具有訪問權限。當信號量sem的值大于等于0時,該進程或線程具有公共資源的訪問權限;相反,當信號量sem的值小于0時,該進程或線程就將阻塞直到信號量sem的值大于等于0為止。6.4 ARM Linux線程相關API信號量線程控制 1信號量說明PV原語主要用于進程或線程間的同步和互斥這兩種典型情況。假設用于互

49、斥,幾個進程或線程往往只設置一個信號量sem,它們的操作流程如下圖。 6.4 ARM Linux線程相關API信號量線程控制 1信號量說明當信號量用于同步操作時,往往會設置多個信號量,并安排不同的初始值來實現它們之間的順序執(zhí)行,它們的操作流程如下圖。6.4 ARM Linux線程相關API信號量線程控制2函數說明Linux實現了POSIX的無名信號量,主要用于線程間的互斥同步。這里主要介紹幾個常見函數。 sem_init用于創(chuàng)立一個信號量,并能初始化它的值。 sem_wait和sem_trywait相當于P操作,它們都能將信號量的值減一,兩者的區(qū)別在于假設信號量小于0時,sem_wait將會阻塞進程,而sem_trywait那么會立即返回。

溫馨提示

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

評論

0/150

提交評論