當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式微處理器
[導(dǎo)讀]▍進(jìn)程的概念 ·?進(jìn)程是操作系統(tǒng)的概念,每當(dāng)我們執(zhí)行一個(gè)程序時(shí),對(duì)于操作系統(tǒng)來(lái)講就創(chuàng)建了一個(gè)進(jìn)程,在這個(gè)過(guò)程中,伴隨著資源的分配和釋放??梢哉J(rèn)為進(jìn)程是一個(gè)程序的一次執(zhí)行過(guò)程。 ▍進(jìn)程通信的概念 ·?進(jìn)程用戶空間是相互獨(dú)立的,一般而言是不能相互訪問(wèn)


進(jìn)程的概念


· 進(jìn)程是操作系統(tǒng)的概念,每當(dāng)我們執(zhí)行一個(gè)程序時(shí),對(duì)于操作系統(tǒng)來(lái)講就創(chuàng)建了一個(gè)進(jìn)程,在這個(gè)過(guò)程中,伴隨著資源的分配和釋放??梢哉J(rèn)為進(jìn)程是一個(gè)程序的一次執(zhí)行過(guò)程。


▍進(jìn)程通信的概念


· 進(jìn)程用戶空間是相互獨(dú)立的,一般而言是不能相互訪問(wèn)的。但很多情況下進(jìn)程間需要互相通信,來(lái)完成系統(tǒng)的某項(xiàng)功能。進(jìn)程通過(guò)與內(nèi)核及其它進(jìn)程之間的互相通信來(lái)協(xié)調(diào)它們的行為。


進(jìn)程通信的應(yīng)用場(chǎng)景


數(shù)據(jù)傳輸:一個(gè)進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另一個(gè)進(jìn)程,發(fā)送的數(shù)據(jù)量在一個(gè)字節(jié)到幾兆字節(jié)之間。

 

共享數(shù)據(jù):多個(gè)進(jìn)程想要操作共享數(shù)據(jù),一個(gè)進(jìn)程對(duì)共享數(shù)據(jù)的修改,別的進(jìn)程應(yīng)該立刻看到。

 

通知事件:一個(gè)進(jìn)程需要向另一個(gè)或一組進(jìn)程發(fā)送消息,通知它(它們)發(fā)生了某種事件(如進(jìn)程終止時(shí)要通知父進(jìn)程)。

 

資源共享:多個(gè)進(jìn)程之間共享同樣的資源。為了作到這一點(diǎn),需要內(nèi)核提供鎖和同步機(jī)制。

 

進(jìn)程控制:有些進(jìn)程希望完全控制另一個(gè)進(jìn)程的執(zhí)行(如Debug進(jìn)程),此時(shí)控制進(jìn)程希望能夠攔截另一個(gè)進(jìn)程的所有陷入和異常,并能夠及時(shí)知道它的狀態(tài)改變。


進(jìn)程通信的方式


管道( pipe ):


管道包括三種:


· 普通管道PIPE:通常有兩種限制,一是單工,只能單向傳輸;二是只能在父子或者兄弟進(jìn)程間使用.


· 流管道s_pipe: 去除了第一種限制,為半雙工,只能在父子或兄弟進(jìn)程間使用,可以雙向傳輸.


· 命名管道:name_pipe:去除了第二種限制,可以在許多并不相關(guān)的進(jìn)程之間進(jìn)行通訊.


信號(hào)量( semophore ) :


· 信號(hào)量是一個(gè)計(jì)數(shù)器,可以用來(lái)控制多個(gè)進(jìn)程對(duì)共享資源的訪問(wèn)。它常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問(wèn)共享資源時(shí),其他進(jìn)程也訪問(wèn)該資源。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。


消息隊(duì)列( message queue ) :


· 消息隊(duì)列是由消息的鏈表,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí)。消息隊(duì)列克服了信號(hào)傳遞信息少、管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)。


信號(hào) ( sinal ) :


· 信號(hào)是一種比較復(fù)雜的通信方式,用于通知接收進(jìn)程某個(gè)事件已經(jīng)發(fā)生。


共享內(nèi)存( shared memory ) :


· 共享內(nèi)存就是映射一段能被其他進(jìn)程所訪問(wèn)的內(nèi)存,這段共享內(nèi)存由一個(gè)進(jìn)程創(chuàng)建,但多個(gè)進(jìn)程都可以訪問(wèn)。共享內(nèi)存是最快的 IPC 方式,它是針對(duì)其他進(jìn)程間通信方式運(yùn)行效率低而專(zhuān)門(mén)設(shè)計(jì)的。它往往與其他通信機(jī)制,如信號(hào)兩,配合使用,來(lái)實(shí)現(xiàn)進(jìn)程間的同步和通信。


套接字( socket ) :


· 套解口也是一種進(jìn)程間通信機(jī)制,與其他通信機(jī)制不同的是,它可用于不同機(jī)器間的進(jìn)程通信。


各進(jìn)程間通信的原理


管道


管道是如何通信的


管道是由內(nèi)核管理的一個(gè)緩沖區(qū),相當(dāng)于我們放入內(nèi)存中的一個(gè)紙條。管道的一端連接一個(gè)進(jìn)程的輸出。這個(gè)進(jìn)程會(huì)向管道中放入信息。管道的另一端連接一個(gè)進(jìn)程的輸入,這個(gè)進(jìn)程取出被放入管道的信息。一個(gè)緩沖區(qū)不需要很大,它被設(shè)計(jì)成為環(huán)形的數(shù)據(jù)結(jié)構(gòu),以便管道可以被循環(huán)利用。當(dāng)管道中沒(méi)有信息的話,從管道中讀取的進(jìn)程會(huì)等待,直到另一端的進(jìn)程放入信息。當(dāng)管道被放滿信息的時(shí)候,嘗試放入信息的進(jìn)程會(huì)等待,直到另一端的進(jìn)程取出信息。當(dāng)兩個(gè)進(jìn)程都終結(jié)的時(shí)候,管道也自動(dòng)消失。

 


管道是如何創(chuàng)建的


從原理上,管道利用fork機(jī)制建立,從而讓兩個(gè)進(jìn)程可以連接到同一個(gè)PIPE上。最開(kāi)始的時(shí)候,上面的兩個(gè)箭頭都連接在同一個(gè)進(jìn)程Process 1上(連接在Process 1上的兩個(gè)箭頭)。當(dāng)fork復(fù)制進(jìn)程的時(shí)候,會(huì)將這兩個(gè)連接也復(fù)制到新的進(jìn)程(Process 2)。隨后,每個(gè)進(jìn)程關(guān)閉自己不需要的一個(gè)連接 (兩個(gè)黑色的箭頭被關(guān)閉; Process 1關(guān)閉從PIPE來(lái)的輸入連接,Process 2關(guān)閉輸出到PIPE的連接),這樣,剩下的紅色連接就構(gòu)成了如上圖的PIPE。

 


· 管道通信的實(shí)現(xiàn)細(xì)節(jié) 


在 Linux 中,管道的實(shí)現(xiàn)并沒(méi)有使用專(zhuān)門(mén)的數(shù)據(jù)結(jié)構(gòu),而是借助了文件系統(tǒng)的file結(jié)構(gòu)和VFS的索引節(jié)點(diǎn)inode。通過(guò)將兩個(gè) file 結(jié)構(gòu)指向同一個(gè)臨時(shí)的 VFS 索引節(jié)點(diǎn),而這個(gè) VFS 索引節(jié)點(diǎn)又指向一個(gè)物理頁(yè)面而實(shí)現(xiàn)的。如下圖

 


有兩個(gè) file 數(shù)據(jù)結(jié)構(gòu),但它們定義文件操作進(jìn)程地址是不同的,其中一個(gè)是向管道中寫(xiě)入數(shù)據(jù)的進(jìn)程地址,而另一個(gè)是從管道中讀出數(shù)據(jù)的進(jìn)程地址。這樣,用戶程序的系統(tǒng)調(diào)用仍然是通常的文件操作,而內(nèi)核卻利用這種抽象機(jī)制實(shí)現(xiàn)了管道這一特殊操作。


關(guān)于管道的讀寫(xiě)


管道實(shí)現(xiàn)的源代碼在fs/pipe.c中,在pipe.c中有很多函數(shù),其中有兩個(gè)函數(shù)比較重要,即管道讀函數(shù)pipe_read()和管道寫(xiě)函數(shù)pipe_wrtie()。管道寫(xiě)函數(shù)通過(guò)將字節(jié)復(fù)制到 VFS 索引節(jié)點(diǎn)指向的物理內(nèi)存而寫(xiě)入數(shù)據(jù),而管道讀函數(shù)則通過(guò)復(fù)制物理內(nèi)存中的字節(jié)而讀出數(shù)據(jù)。當(dāng)然,內(nèi)核必須利用一定的機(jī)制同步對(duì)管道的訪問(wèn),為此,內(nèi)核使用了鎖、等待隊(duì)列和信號(hào)。


當(dāng)寫(xiě)進(jìn)程向管道中寫(xiě)入時(shí),它利用標(biāo)準(zhǔn)的庫(kù)函數(shù)write(),系統(tǒng)根據(jù)庫(kù)函數(shù)傳遞的文件描述符,可找到該文件的 file 結(jié)構(gòu)。file 結(jié)構(gòu)中指定了用來(lái)進(jìn)行寫(xiě)操作的函數(shù)(即寫(xiě)入函數(shù))地址,于是,內(nèi)核調(diào)用該函數(shù)完成寫(xiě)操作。寫(xiě)入函數(shù)在向內(nèi)存中寫(xiě)入數(shù)據(jù)之前,必須首先檢查 VFS 索引節(jié)點(diǎn)中的信息,同時(shí)滿足如下條件時(shí),才能進(jìn)行實(shí)際的內(nèi)存復(fù)制工作:


· 內(nèi)存中有足夠的空間可容納所有要寫(xiě)入的數(shù)據(jù);

· 內(nèi)存沒(méi)有被讀程序鎖定。


如果同時(shí)滿足上述條件,寫(xiě)入函數(shù)首先鎖定內(nèi)存,然后從寫(xiě)進(jìn)程的地址空間中復(fù)制數(shù)據(jù)到內(nèi)存。否則,寫(xiě)入進(jìn)程就休眠在 VFS 索引節(jié)點(diǎn)的等待隊(duì)列中,接下來(lái),內(nèi)核將調(diào)用調(diào)度程序,而調(diào)度程序會(huì)選擇其他進(jìn)程運(yùn)行。寫(xiě)入進(jìn)程實(shí)際處于可中斷的等待狀態(tài),當(dāng)內(nèi)存中有足夠的空間可以容納寫(xiě)入數(shù)據(jù),或內(nèi)存被解鎖時(shí),讀取進(jìn)程會(huì)喚醒寫(xiě)入進(jìn)程,這時(shí),寫(xiě)入進(jìn)程將接收到信號(hào)。當(dāng)數(shù)據(jù)寫(xiě)入內(nèi)存之后,內(nèi)存被解鎖,而所有休眠在索引節(jié)點(diǎn)的讀取進(jìn)程會(huì)被喚醒。


管道的讀取過(guò)程和寫(xiě)入過(guò)程類(lèi)似。但是,進(jìn)程可以在沒(méi)有數(shù)據(jù)或內(nèi)存被鎖定時(shí)立即返回錯(cuò)誤信息,而不是阻塞該進(jìn)程,這依賴(lài)于文件或管道的打開(kāi)模式。反之,進(jìn)程可以休眠在索引節(jié)點(diǎn)的等待隊(duì)列中等待寫(xiě)入進(jìn)程寫(xiě)入數(shù)據(jù)。當(dāng)所有的進(jìn)程完成了管道操作之后,管道的索引節(jié)點(diǎn)被丟棄,而共享數(shù)據(jù)頁(yè)也被釋放。


Linux函數(shù)原型


#include <unistd.h>int pipe(int filedes[2]);


filedes[0]用于讀出數(shù)據(jù),讀取時(shí)必須關(guān)閉寫(xiě)入端,即close(filedes[1]);

filedes[1]用于寫(xiě)入數(shù)據(jù),寫(xiě)入時(shí)必須關(guān)閉讀取端,即close(filedes[0])。


程序?qū)嵗?/span>


int main(void){    int n;    int fd[2];    pid_t pid;    char line[MAXLINE];    if(pipe(fd)  0){                 /* 先建立管道得到一對(duì)文件描述符 */        exit(0); }
    if((pid = fork())  0)            /* 父進(jìn)程把文件描述符復(fù)制給子進(jìn)程 */        exit(1);    else if(pid > 0){                /* 父進(jìn)程寫(xiě) */        close(fd[0]);                /* 關(guān)閉讀描述符 */        write(fd[1], "\nhello world\n", 14); }
    else{                            /* 子進(jìn)程讀 */        close(fd[1]);                /* 關(guān)閉寫(xiě)端 */        n = read(fd[0], line, MAXLINE);        write(STDOUT_FILENO, line, n); }
    exit(0);}


命名管道


由于基于fork機(jī)制,所以管道只能用于父進(jìn)程和子進(jìn)程之間,或者擁有相同祖先的兩個(gè)子進(jìn)程之間 (有親緣關(guān)系的進(jìn)程之間)。為了解決這一問(wèn)題,Linux提供了FIFO方式連接進(jìn)程。FIFO又叫做命名管道(named PIPE)。


實(shí)現(xiàn)原理


FIFO (First in, First out)為一種特殊的文件類(lèi)型,它在文件系統(tǒng)中有對(duì)應(yīng)的路徑。當(dāng)一個(gè)進(jìn)程以讀(r)的方式打開(kāi)該文件,而另一個(gè)進(jìn)程以寫(xiě)(w)的方式打開(kāi)該文件,那么內(nèi)核就會(huì)在這兩個(gè)進(jìn)程之間建立管道,所以FIFO實(shí)際上也由內(nèi)核管理,不與硬盤(pán)打交道。之所以叫FIFO,是因?yàn)楣艿辣举|(zhì)上是一個(gè)先進(jìn)先出的隊(duì)列數(shù)據(jù)結(jié)構(gòu),最早放入的數(shù)據(jù)被最先讀出來(lái),從而保證信息交流的順序。FIFO只是借用了文件系統(tǒng)(file system,命名管道是一種特殊類(lèi)型的文件,因?yàn)長(zhǎng)inux中所有事物都是文件,它在文件系統(tǒng)中以文件名的形式存在。)來(lái)為管道命名。寫(xiě)模式的進(jìn)程向FIFO文件中寫(xiě)入,而讀模式的進(jìn)程從FIFO文件中讀出。當(dāng)刪除FIFO文件時(shí),管道連接也隨之消失。FIFO的好處在于我們可以通過(guò)文件的路徑來(lái)識(shí)別管道,從而讓沒(méi)有親緣關(guān)系的進(jìn)程之間建立連接


函數(shù)原型:


#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *filename, mode_t mode);int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );


其中filename是被創(chuàng)建的文件名稱(chēng),mode表示將在該文件上設(shè)置的權(quán)限位和將被創(chuàng)建的文件類(lèi)型(在此情況下為S_IFIFO),dev是當(dāng)創(chuàng)建設(shè)備特殊文件時(shí)使用的一個(gè)值。因此,對(duì)于先進(jìn)先出文件它的值為0。


程序?qū)嵗?br>


#include <stdio.h>  #include <stdlib.h>  #include <sys/types.h>  #include <sys/stat.h>  int main() {      int res = mkfifo("/tmp/my_fifo", 0777);      if (res == 0)      {          printf("FIFO created/n");      }       exit(EXIT_SUCCESS);  }


▍信號(hào)量


什么是信號(hào)量


為了防止出現(xiàn)因多個(gè)程序同時(shí)訪問(wèn)一個(gè)共享資源而引發(fā)的一系列問(wèn)題,我們需要一種方法。比如在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問(wèn)代碼的臨界區(qū)域。臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號(hào)量就可以提供這樣的一種訪問(wèn)機(jī)制,讓一個(gè)臨界區(qū)同一時(shí)間只有一個(gè)線程在訪問(wèn)它,也就是說(shuō)信號(hào)量是用來(lái)調(diào)協(xié)進(jìn)程對(duì)共享資源的訪問(wèn)的。


信號(hào)量是一個(gè)特殊的變量,程序?qū)ζ湓L問(wèn)都是原子操作,且只允許對(duì)它進(jìn)行等待(即P(信號(hào)變量))和發(fā)送(即V(信號(hào)變量))信息操作。最簡(jiǎn)單的信號(hào)量是只能取0和1的變量,這也是信號(hào)量最常見(jiàn)的一種形式,叫做二進(jìn)制信號(hào)量。而可以取多個(gè)正整數(shù)的信號(hào)量被稱(chēng)為通用信號(hào)量。


信號(hào)量的工作原理


由于信號(hào)量只能進(jìn)行兩種操作等待和發(fā)送信號(hào),即P(sv)和V(sv),他們的行為是這樣的:


· P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進(jìn)程的執(zhí)行

· V(sv):如果有其他進(jìn)程因等待sv而被掛起,就讓它恢復(fù)運(yùn)行,如果沒(méi)有進(jìn)程因等待sv而掛起,就給它加1.


舉個(gè)例子,就是兩個(gè)進(jìn)程共享信號(hào)量sv,一旦其中一個(gè)進(jìn)程執(zhí)行了P(sv)操作,它將得到信號(hào)量,并可以進(jìn)入臨界區(qū),使sv減1。而第二個(gè)進(jìn)程將被阻止進(jìn)入臨界區(qū),因?yàn)楫?dāng)它試圖執(zhí)行P(sv)時(shí),sv為0,它會(huì)被掛起以等待第一個(gè)進(jìn)程離開(kāi)臨界區(qū)域并執(zhí)行V(sv)釋放信號(hào)量,這時(shí)第二個(gè)進(jìn)程就可以恢復(fù)執(zhí)行。


Linux的信號(hào)量機(jī)制


Linux提供了一組精心設(shè)計(jì)的信號(hào)量接口來(lái)對(duì)信號(hào)進(jìn)行操作,它們不只是針對(duì)二進(jìn)制信號(hào)量,下面將會(huì)對(duì)這些函數(shù)進(jìn)行介紹,但請(qǐng)注意,這些函數(shù)都是用來(lái)對(duì)成組的信號(hào)量值進(jìn)行操作的。它們聲明在頭文件sys/sem.h中。


semget函數(shù)


它的作用是創(chuàng)建一個(gè)新信號(hào)量或取得一個(gè)已有信號(hào)量,原型為:


int semget(key_t key, int num_sems, int sem_flags); 


第一個(gè)參數(shù)key是整數(shù)值(唯一非零),不相關(guān)的進(jìn)程可以通過(guò)它訪問(wèn)一個(gè)信號(hào)量,它代表程序可能要使用的某個(gè)資源,程序?qū)λ行盘?hào)量的訪問(wèn)都是間接的,程序先通過(guò)調(diào)用semget函數(shù)并提供一個(gè)鍵,再由系統(tǒng)生成一個(gè)相應(yīng)的信號(hào)標(biāo)識(shí)符(semget函數(shù)的返回值),只有semget函數(shù)才直接使用信號(hào)量鍵,所有其他的信號(hào)量函數(shù)使用由semget函數(shù)返回的信號(hào)量標(biāo)識(shí)符。如果多個(gè)程序使用相同的key值,key將負(fù)責(zé)協(xié)調(diào)工作。

 

第二個(gè)參數(shù)num_sems指定需要的信號(hào)量數(shù)目,它的值幾乎總是1。 

 

第三個(gè)參數(shù)sem_flags是一組標(biāo)志,當(dāng)想要當(dāng)信號(hào)量不存在時(shí)創(chuàng)建一個(gè)新的信號(hào)量,可以和值IPC_CREAT做按位或操作。設(shè)置了IPC_CREAT標(biāo)志后,即使給出的鍵是一個(gè)已有信號(hào)量的鍵,也不會(huì)產(chǎn)生錯(cuò)誤。而IPC_CREAT | IPC_EXCL則可以創(chuàng)建一個(gè)新的,唯一的信號(hào)量,如果信號(hào)量已存在,返回一個(gè)錯(cuò)誤。

 

semget函數(shù)成功返回一個(gè)相應(yīng)信號(hào)標(biāo)識(shí)符(非零),失敗返回-1.


semop函數(shù)


它的作用是改變信號(hào)量的值,原型為:


int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  · 1


sem_id是由semget返回的信號(hào)量標(biāo)識(shí)符,sembuf結(jié)構(gòu)的定義如下:


struct sembuf{      short sem_num;//除非使用一組信號(hào)量,否則它為0      short sem_op;//信號(hào)量在一次操作中需要改變的數(shù)據(jù),通常是兩個(gè)數(shù),一個(gè)是-1,即P(等待)操作,                      //一個(gè)是+1,即V(發(fā)送信號(hào))操作。     short sem_flg;//通常為SEM_UNDO,使操作系統(tǒng)跟蹤信號(hào),                      //并在進(jìn)程沒(méi)有釋放該信號(hào)量而終止時(shí),操作系統(tǒng)釋放信號(hào)量  }; 


semctl函數(shù)


int semctl(int sem_id, int sem_num, int command, ...);  · 1


如果有第四個(gè)參數(shù),它通常是一個(gè)union semum結(jié)構(gòu),定義如下:


union semun{      int val;      struct semid_ds *buf;      unsigned short *arry;  }; 


前兩個(gè)參數(shù)與前面一個(gè)函數(shù)中的一樣,command通常是下面兩個(gè)值中的其中一個(gè) 
SETVAL:用來(lái)把信號(hào)量初始化為一個(gè)已知的值。p 這個(gè)值通過(guò)union semun中的val成員設(shè)置,其作用是在信號(hào)量第一次使用前對(duì)它進(jìn)行設(shè)置。 


IPC_RMID:用于刪除一個(gè)已經(jīng)無(wú)需繼續(xù)使用的信號(hào)量標(biāo)識(shí)符。

 

消息隊(duì)列


什么是消息隊(duì)列


消息隊(duì)列是消息的鏈接表,包括Posix消息隊(duì)列system V消息隊(duì)列。有足夠權(quán)限的進(jìn)程可以向隊(duì)列中添加消息,被賦予讀權(quán)限的進(jìn)程則可以讀走隊(duì)列中的消息。消息隊(duì)列克服了信號(hào)承載信息量少,管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)。消息隊(duì)列是隨內(nèi)核持續(xù)的。


每個(gè)消息隊(duì)列都有一個(gè)隊(duì)列頭,用結(jié)構(gòu)struct msg_queue來(lái)描述。隊(duì)列頭中包含了該消息隊(duì)列的大量信息,包括消息隊(duì)列鍵值、用戶ID、組ID、消息隊(duì)列中消息數(shù)目等等,甚至記錄了最近對(duì)消息隊(duì)列讀寫(xiě)進(jìn)程的ID。讀者可以訪問(wèn)這些信息,也可以設(shè)置其中的某些信息。


結(jié)構(gòu)msg_queue用來(lái)描述消息隊(duì)列頭,存在于系統(tǒng)空間:


struct msg_queue {    struct kern_ipc_perm q_perm;    time_t q_stime;        /* last msgsnd time */    time_t q_rtime;        /* last msgrcv time */    time_t q_ctime;        /* last change time */    unsigned long q_cbytes;    /* current number of bytes on queue */    unsigned long q_qnum;      /* number of messages in queue */    unsigned long q_qbytes;    /* max number of bytes on queue */    pid_t q_lspid;          /* pid of last msgsnd */    pid_t q_lrpid;          /* last receive pid */    struct list_head q_messages;    struct list_head q_receivers;    struct list_head q_senders;};


結(jié)構(gòu)msqid_ds用來(lái)設(shè)置或返回消息隊(duì)列的信息,存在于用戶空間:


struct msqid_ds {    struct ipc_perm msg_perm;    struct msg *msg_first;      /* first message on queue,unused  */    struct msg *msg_last;      /* last message in queue,unused */    __kernel_time_t msg_stime;  /* last msgsnd time */    __kernel_time_t msg_rtime;  /* last msgrcv time */    __kernel_time_t msg_ctime;  /* last change time */    unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */    unsigned long  msg_lqbytes; /* ditto */    unsigned short msg_cbytes;  /* current number of bytes on queue */    unsigned short msg_qnum;    /* number of messages in queue */    unsigned short msg_qbytes;  /* max number of bytes on queue */    __kernel_ipc_pid_t msg_lspid;  /* pid of last msgsnd */    __kernel_ipc_pid_t msg_lrpid;  /* last receive pid */};


▍消息隊(duì)列與內(nèi)核的聯(lián)系


下圖說(shuō)明了內(nèi)核與消息隊(duì)列是怎樣建立起聯(lián)系的:

 


從上圖可以看出,全局?jǐn)?shù)據(jù)結(jié)構(gòu) struct ipc_ids msg_ids 可以訪問(wèn)到每個(gè)消息隊(duì)列頭的第一個(gè)成員:struct kern_ipc_perm;而每個(gè)struct kern_ipc_perm能夠與具體的消息隊(duì)列對(duì)應(yīng)起來(lái)是因?yàn)樵谠摻Y(jié)構(gòu)中,有一個(gè)key_t類(lèi)型成員key,而key則唯一確定一個(gè)消息隊(duì)列。kern_ipc_perm結(jié)構(gòu)如下:


struct kern_ipc_perm{  //內(nèi)核中記錄消息隊(duì)列的全局?jǐn)?shù)據(jù)結(jié)構(gòu)msg_ids能夠訪問(wèn)到該結(jié)構(gòu);key_t  key;    //該鍵值則唯一對(duì)應(yīng)一個(gè)消息隊(duì)列uid_t  uid;gid_t  gid;uid_t  cuid;gid_t  cgid;mode_t  mode;unsigned long seq;}


消息隊(duì)列的操作

 

打開(kāi)或創(chuàng)建消息隊(duì)列息隊(duì)列的內(nèi)核持續(xù)性要求每個(gè)消息隊(duì)列都在系統(tǒng)范圍內(nèi)對(duì)應(yīng)唯一的鍵值,所以,要獲得一個(gè)消息隊(duì)列的描述字,只需提供該消息隊(duì)列的鍵值即可;

 

注:消息隊(duì)列描述字是由在系統(tǒng)范圍內(nèi)唯一的鍵值生成的,而鍵值可以看作對(duì)應(yīng)系統(tǒng)內(nèi)的一條路經(jīng)。

  

讀寫(xiě)的操作

 

消息讀寫(xiě)操作非常簡(jiǎn)單,對(duì)開(kāi)發(fā)人員來(lái)說(shuō),每個(gè)消息都類(lèi)似如下的數(shù)據(jù)結(jié)構(gòu):


struct msgbuf{long mtype;char mtext[1];};


mtype成員代表消息類(lèi)型,從消息隊(duì)列中讀取消息的一個(gè)重要依據(jù)就是消息的類(lèi)型;mtext是消息內(nèi)容,當(dāng)然長(zhǎng)度不一定為1。因此,對(duì)于發(fā)送消息來(lái)說(shuō), 首先預(yù)置一個(gè)msgbuf緩沖區(qū)并寫(xiě)入消息類(lèi)型和內(nèi)容,調(diào)用相應(yīng)的發(fā)送函數(shù)即可;對(duì)讀取消息來(lái)說(shuō),首先分配這樣一個(gè)msgbuf緩沖區(qū),然后把消息讀入該緩沖區(qū)即可。

· 獲得或設(shè)置消息隊(duì)列屬性:


消息隊(duì)列的信息基本上都保存在消息隊(duì)列頭中,因此,可以分配一個(gè)類(lèi)似于消息隊(duì)列頭的結(jié)構(gòu),來(lái)返回消息隊(duì)列的屬性;同樣可以設(shè)置該數(shù)據(jù)結(jié)構(gòu)。

 


▍信號(hào)


信號(hào)本質(zhì)


· 信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬,在原理上,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一個(gè)中斷請(qǐng)求可以說(shuō)是一樣的。信號(hào)是異步的,一個(gè)進(jìn)程不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá),事實(shí)上,進(jìn)程也不知道信號(hào)到底什么時(shí)候到達(dá)。


· 信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號(hào)的進(jìn)程有哪些事情發(fā)生了。信號(hào)機(jī)制經(jīng)過(guò)POSIX實(shí)時(shí)擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。


信號(hào)來(lái)源


信號(hào)事件的發(fā)生有兩個(gè)來(lái)源:硬件來(lái)源(比如我們按下了鍵盤(pán)或者其它硬件故障);軟件來(lái)源,最常用發(fā)送信號(hào)的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer以及sigqueue函數(shù),軟件來(lái)源還包括一些非法運(yùn)算等操作。


信號(hào)的種類(lèi)


· 可以從兩個(gè)不同的分類(lèi)角度對(duì)信號(hào)進(jìn)行分類(lèi):(1)可靠性方面:可靠信號(hào)與不可靠信號(hào);(2)與時(shí)間的關(guān)系上:實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)。


可靠信號(hào)和不可靠信號(hào)


不可靠信號(hào)


Linux信號(hào)機(jī)制基本上是從Unix系統(tǒng)中繼承過(guò)來(lái)的。早期Unix系統(tǒng)中的信號(hào)機(jī)制比較簡(jiǎn)單和原始,后來(lái)在實(shí)踐中暴露出一些問(wèn)題,因此,把那些建立在早期機(jī)制上的信號(hào)叫做”不可靠信號(hào)”,信號(hào)值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號(hào)都是不可靠信號(hào)。這就是”不可靠信號(hào)”的來(lái)源。它的主要問(wèn)題是:


· 進(jìn)程每次處理信號(hào)后,就將對(duì)信號(hào)的響應(yīng)設(shè)置為默認(rèn)動(dòng)作。在某些情況下,將導(dǎo)致對(duì)信號(hào)的錯(cuò)誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號(hào)處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號(hào)。


· 信號(hào)可能丟失。因此,早期unix下的不可靠信號(hào)主要指的是進(jìn)程可能對(duì)信號(hào)做出錯(cuò)誤的反應(yīng)以及信號(hào)可能丟失。 

 

Linux支持不可靠信號(hào),但是對(duì)不可靠信號(hào)機(jī)制做了改進(jìn):在調(diào)用完信號(hào)處理函數(shù)后,不必重新調(diào)用該信號(hào)的安裝函數(shù)(信號(hào)安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此,Linux下的不可靠信號(hào)問(wèn)題主要指的是信號(hào)可能丟失。

 

可靠信號(hào)


· 隨著時(shí)間的發(fā)展,實(shí)踐證明了有必要對(duì)信號(hào)的原始機(jī)制加以改進(jìn)和擴(kuò)充,力圖實(shí)現(xiàn)”可靠信號(hào)”。由于原來(lái)定義的信號(hào)已有許多應(yīng)用,不好再做改動(dòng),最終只好又新增加了一些信號(hào),并在一開(kāi)始就把它們定義為可靠信號(hào),這些信號(hào)支持排隊(duì),不會(huì)丟失。


· 信號(hào)值位于SIGRTMIN和SIGRTMAX之間的信號(hào)都是可靠信號(hào),可靠信號(hào)克服了信號(hào)可能丟失的問(wèn)題。Linux在支持新版本的信號(hào)安裝函數(shù)sigation()以及信號(hào)發(fā)送函數(shù)sigqueue()的同時(shí),仍然支持早期的signal()信號(hào)安裝函數(shù),支持信號(hào)發(fā)送函數(shù)kill()


注意:可靠信號(hào)是指后來(lái)添加的新信號(hào)(信號(hào)值位于SIGRTMIN及SIGRTMAX之間);不可靠信號(hào)是信號(hào)值小于SIGRTMIN的信號(hào)。信號(hào)的可靠與不可靠只與信號(hào)值有關(guān),與信號(hào)的發(fā)送及安裝函數(shù)無(wú)關(guān)。


實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)


· 非實(shí)時(shí)信號(hào)都不支持排隊(duì),都是不可靠信號(hào),編號(hào)是1-31,0是空信號(hào);實(shí)時(shí)信號(hào)都支持排隊(duì),都是可靠信號(hào)。


進(jìn)程對(duì)信號(hào)的響應(yīng)


· 忽略信號(hào),即對(duì)信號(hào)不做任何處理,其中,有兩個(gè)信號(hào)不能忽略:SIGKILL及SIGSTOP;


· 捕捉信號(hào)。定義信號(hào)處理函數(shù),當(dāng)信號(hào)發(fā)生時(shí),執(zhí)行相應(yīng)的處理函數(shù);


· 執(zhí)行缺省操作,Linux對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作


注意:進(jìn)程對(duì)實(shí)時(shí)信號(hào)的缺省反應(yīng)是進(jìn)程終止。


信號(hào)的發(fā)送和安裝


· 發(fā)送信號(hào)的主要函數(shù)有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。


· 如果進(jìn)程要處理某一信號(hào),那么就要在進(jìn)程中安裝該信號(hào)。安裝信號(hào)主要用來(lái)確定信號(hào)值及進(jìn)程針對(duì)該信號(hào)值的動(dòng)作之間的映射關(guān)系,即進(jìn)程將要處理哪個(gè)信號(hào);該信號(hào)被傳遞給進(jìn)程時(shí),將執(zhí)行何種操作。


注意:inux主要有兩個(gè)函數(shù)實(shí)現(xiàn)信號(hào)的安裝:signal()、sigaction()。其中signal()在可靠信號(hào)系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn), 是庫(kù)函數(shù)。它只有兩個(gè)參數(shù),不支持信號(hào)傳遞信息,主要是用于前32種非實(shí)時(shí)信號(hào)的安裝;而sigaction()是較新的函數(shù)(由兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn):sys_signal以及sys_rt_sigaction),有三個(gè)參數(shù),支持信號(hào)傳遞信息,主要用來(lái)與 sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時(shí)信號(hào)的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)在支持信號(hào)帶有參數(shù)。


共享內(nèi)存


共享內(nèi)存可以說(shuō)是最有用的進(jìn)程間通信方式,也是最快的IPC形式。是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的。兩個(gè)不同進(jìn)程A、B共享內(nèi)存的意思是,同一塊物理內(nèi)存被映射到進(jìn)程A、B各自的進(jìn)程地址空間。進(jìn)程A可以即時(shí)看到進(jìn)程B對(duì)共享內(nèi)存中數(shù)據(jù)的更新,反之亦然。由于多個(gè)進(jìn)程共享同一塊內(nèi)存區(qū)域,必然需要某種同步機(jī)制,互斥鎖和信號(hào)量都可以。


系統(tǒng)V共享內(nèi)存原理


進(jìn)程間需要共享的數(shù)據(jù)被放在一個(gè)叫做IPC共享內(nèi)存區(qū)域的地方,所有需要訪問(wèn)該共享區(qū)域的進(jìn)程都要把該共享區(qū)域映射到本進(jìn)程的地址空間中去。系統(tǒng)V共享內(nèi)存通過(guò)shmget獲得或創(chuàng)建一個(gè)IPC共享內(nèi)存區(qū)域,并返回相應(yīng)的標(biāo)識(shí)符。內(nèi)核在保證shmget獲得或創(chuàng)建一個(gè)共享內(nèi)存區(qū),初始化該共享內(nèi)存區(qū)相應(yīng)的shmid_kernel結(jié)構(gòu)體的同時(shí),還將在特殊文件系統(tǒng)shm中,創(chuàng)建并打開(kāi)一個(gè)同名文件,并在內(nèi)存中建立起該文件的相應(yīng)dentry及inode結(jié)構(gòu),新打開(kāi)的文件不屬于任何一個(gè)進(jìn)程(任何進(jìn)程都可以訪問(wèn)該共享內(nèi)存區(qū))。所有這一切都是系統(tǒng)調(diào)用shmget完成的。


系統(tǒng)V共享內(nèi)存API


shmget()用來(lái)獲得共享內(nèi)存區(qū)域的ID,如果不存在指定的共享區(qū)域就創(chuàng)建相應(yīng)的區(qū)域。shmat()把共享內(nèi)存區(qū)域映射到調(diào)用進(jìn)程的地址空間中去,這樣,進(jìn)程就可以方便地對(duì)共享區(qū)域進(jìn)行訪問(wèn)操作。shmdt()調(diào)用用來(lái)解除進(jìn)程對(duì)共享內(nèi)存區(qū)域的映射。shmctl實(shí)現(xiàn)對(duì)共享內(nèi)存區(qū)域的控制操作。


▍套接字(socket)


最早出現(xiàn)在UNIX系統(tǒng)中,是UNIX系統(tǒng)主要的信息傳遞方式。


Socket相關(guān)概念


兩個(gè)基本概念:客戶方和服務(wù)方。當(dāng)兩個(gè)應(yīng)用之間需要采用SOCKET通信時(shí),首先需要在兩個(gè)應(yīng)用之間(可能位于同一臺(tái)機(jī)器,也可能位于不同的機(jī)器)建立SOCKET連接。

 

發(fā)起呼叫連接請(qǐng)求的一方為客戶方 


在客戶方呼叫連接請(qǐng)求之前,它必須知道服務(wù)方在哪里。所以需要知道服務(wù)方所在機(jī)器的IP地址或機(jī)器名稱(chēng),如果客戶方和服務(wù)方事前有一個(gè)約定就好了,這個(gè)約定就是PORT(端口號(hào))。也就是說(shuō),客戶方可以通過(guò)服務(wù)方所在機(jī)器的IP地址或機(jī)器名稱(chēng)和端口號(hào)唯一的確定方式來(lái)呼叫服務(wù)方。

 

接受呼叫連接請(qǐng)求的一方成為服務(wù)方。 


在客戶方呼叫之前,服務(wù)方必須處于偵聽(tīng)狀態(tài),偵聽(tīng)是否有客戶要求建立連接。一旦接到連接請(qǐng)求,服務(wù)方可以根據(jù)情況建立或拒絕連接。當(dāng)客戶方的消息到達(dá)服務(wù)方端口時(shí),會(huì)自動(dòng)觸發(fā)一個(gè)事件(event),服務(wù)方只要接管該事件,就可以接受來(lái)自客戶方的消息了。

 

Socket類(lèi)型


· 流式Socket(STREAM):是一種面向連接的Socekt,針對(duì)面向連接的TCP服務(wù)應(yīng)用,安全,但是效率低;

· 數(shù)據(jù)報(bào)式Socket(DATAGAM):是一種無(wú)連接的Socket,對(duì)應(yīng)于無(wú)連接的UDP服務(wù)應(yīng)用。不安(丟失,順序混亂,在接受端要分析重排及要求重發(fā)),但效率高。


Socket一般應(yīng)用模式(服務(wù)端和客戶端)

 


Socket通信基本流程圖

 


-END-




推薦閱讀



【01】為什么Linux CFS調(diào)度器沒(méi)有帶來(lái)驚艷的碾壓效果?
【02】Linux是否能在 8 位 MCU 上運(yùn)行?
【03】求職要做足功課!看看嵌入式linux牛人是如何做的
【04】看漫畫(huà),學(xué)Linux內(nèi)核!看完明白小企鵝們?cè)诟缮读税桑?/a>
【05】若想成為一名Linux下編程高手,必須能對(duì)各種系統(tǒng)調(diào)用有透徹的了解



免責(zé)聲明:整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系刪除

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉