0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

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

科技綠洲 ? 來(lái)源:Linux開(kāi)發(fā)架構(gòu)之路 ? 作者:Linux開(kāi)發(fā)架構(gòu)之路 ? 2023-11-11 14:42 ? 次閱讀

進(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ū)嵗?/p>

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 worldn", 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ū)嵗?/p>

#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);

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, ...);

如果有第四個(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通信基本流程圖

圖片

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 通信
    +關(guān)注

    關(guān)注

    18

    文章

    5878

    瀏覽量

    135310
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207898
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4235

    瀏覽量

    61965
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    NanoEdge AI的技術(shù)原理、應(yīng)用場(chǎng)景及優(yōu)勢(shì)

    能耗并提高數(shù)據(jù)安全性。本文將對(duì) NanoEdge AI 的技術(shù)原理、應(yīng)用場(chǎng)景以及優(yōu)勢(shì)進(jìn)行綜述。 1、技術(shù)原理 NanoEdge AI 的核心技術(shù)包括邊緣計(jì)算、神經(jīng)網(wǎng)絡(luò)壓縮和低功耗硬件設(shè)計(jì)。邊緣計(jì)算
    發(fā)表于 03-12 08:09

    MOS管的應(yīng)用場(chǎng)景

    mos管的應(yīng)用場(chǎng)景,你了解么?低壓MOS管可稱(chēng)為金屬氧化物半導(dǎo)體場(chǎng)效應(yīng)管,因?yàn)榈蛪篗OS管具有良好的開(kāi)關(guān)特性,廣泛應(yīng)用在電子開(kāi)關(guān)的電路中。如開(kāi)關(guān)電源,電動(dòng)馬達(dá)、照明調(diào)光等!下面銀聯(lián)寶科技就跟大家一起
    發(fā)表于 11-14 09:24

    this的使用場(chǎng)景及與C,Java中的this的區(qū)別

    【JS】this有哪些使用場(chǎng)景?跟C,Java中的this有什么區(qū)別?如何改變this的值?
    發(fā)表于 03-11 10:17

    CP-OFMD調(diào)制波形應(yīng)用場(chǎng)景

    圖1、5G的應(yīng)用場(chǎng)景5G使用5G多載波波形來(lái)為智能手機(jī),辦公室,工廠自動(dòng)化,智能電網(wǎng),智慧城市,物聯(lián)網(wǎng),M2M,M2X等多種設(shè)備提供應(yīng)用平臺(tái)。5G新無(wú)線電(5G NR)根據(jù)應(yīng)用場(chǎng)景可分為三大類(lèi)服務(wù)
    發(fā)表于 06-18 06:51

    =>的使用場(chǎng)景有哪些

    使用場(chǎng)景
    發(fā)表于 10-27 13:25

    藍(lán)牙低功耗常見(jiàn)的應(yīng)用場(chǎng)景及架構(gòu)

    淺談藍(lán)牙低功耗(BLE)的幾種常見(jiàn)的應(yīng)用場(chǎng)景及架構(gòu)
    發(fā)表于 06-15 09:51

    uwb定位技術(shù)原理及應(yīng)用場(chǎng)景

    說(shuō)到定位我們并不陌生,定位技術(shù)一直與我們的生活密不可分,比如最常見(jiàn)的車(chē)輛導(dǎo)航。 根據(jù)使用場(chǎng)景,定位技術(shù)分為室內(nèi)定位和室外定位。 室外定位主要依靠GPS,北斗,GLONASS,伽利略等全球衛(wèi)星定位
    發(fā)表于 06-30 07:15

    ARM的技術(shù)特征是什么?應(yīng)用場(chǎng)景有哪些?

    ARM的技術(shù)特征是什么?應(yīng)用場(chǎng)景有哪些?
    發(fā)表于 11-05 07:32

    MS9331的應(yīng)用場(chǎng)景是什么?

    MS9331的應(yīng)用場(chǎng)景是什么?
    發(fā)表于 02-11 06:41

    RK3308的特點(diǎn)及應(yīng)用場(chǎng)景是什么?

    RK3308的特點(diǎn)及應(yīng)用場(chǎng)景是什么?
    發(fā)表于 03-09 08:04

    逐一介紹Linux各個(gè)系統(tǒng)的特點(diǎn)以及應(yīng)用場(chǎng)景

    rootfs 與一些需要的應(yīng)用軟件。**Firefly Buildroot 特點(diǎn)如下:**Firefly Buildroot 應(yīng)用場(chǎng)景:如果您的產(chǎn)品 CPU 性能比較弱、硬件資源比較緊張可以首選
    發(fā)表于 08-02 16:42

    藍(lán)牙多連接應(yīng)用場(chǎng)景舉例

    藍(lán)牙多連接應(yīng)用場(chǎng)景舉例 一、藍(lán)牙多連接的通信方式: 1-1、藍(lán)牙MESH組網(wǎng)圖: 1-2、藍(lán)牙星型組網(wǎng)圖; 二、兩種方案的優(yōu)劣勢(shì): 2-1、 MESH方式網(wǎng)絡(luò)中的節(jié)點(diǎn)數(shù)量多,能夠?qū)崿F(xiàn)單播、組播
    發(fā)表于 05-09 09:09

    SMT組裝工藝流程的應(yīng)用場(chǎng)景

    工藝流程的應(yīng)用場(chǎng)景。 01單面純貼片工藝 應(yīng)用場(chǎng)景: 僅在一面有需要焊接的貼片器件。 02雙面純貼片工藝 應(yīng)用場(chǎng)景: A/B面均為貼片元件。 03單面混裝工藝 應(yīng)用場(chǎng)景: A面有貼
    發(fā)表于 10-20 10:31

    labview 和 wincc 的區(qū)別 使用場(chǎng)景

    labview 和 wincc 的區(qū)別 使用場(chǎng)景 都是上位機(jī)軟件,都可以做監(jiān)控軟件 wincc的名氣也比較大 對(duì)比的資料較少 寫(xiě)這些文章的人,從自己的從事的行業(yè)出發(fā),帶有自己的思維 使用的場(chǎng)景 肯定
    發(fā)表于 10-27 18:01

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

    管道是由內(nèi)核管理的一個(gè)緩沖區(qū),相當(dāng)于我們放入內(nèi)存中的一個(gè)紙條。管道的一端連接一個(gè)進(jìn)程的輸出。這個(gè)進(jìn)程會(huì)向管道中放入信息。管道的另一端連接一個(gè)進(jìn)程的輸入,這個(gè)進(jìn)程取出被放入管道的信息。一
    的頭像 發(fā)表于 06-05 16:44 ?6630次閱讀
    Linux<b class='flag-5'>進(jìn)程</b>的概念及<b class='flag-5'>進(jìn)程</b><b class='flag-5'>通信</b>的應(yīng)<b class='flag-5'>用場(chǎng)景</b>