1. 程式人生 > >Linux程序間的通訊方式和原理【轉】

Linux程序間的通訊方式和原理【轉】

程序的概念

  • 程序是作業系統的概念,每當我們執行一個程式時,對於作業系統來講就建立了一個程序,在這個過程中,伴隨著資源的分配和釋放。可以認為程序是一個程式的一次執行過程。

程序通訊的概念

  • 程序使用者空間是相互獨立的,一般而言是不能相互訪問的。但很多情況下程序間需要互相通訊,來完成系統的某項功能。程序通過與核心及其它程序之間的互相通訊來協調它們的行為。

程序通訊的應用場景

  • 資料傳輸:一個程序需要將它的資料傳送給另一個程序,傳送的資料量在一個位元組到幾兆位元組之間。

  • 共享資料:多個程序想要操作共享資料,一個程序對共享資料的修改,別的程序應該立刻看到。

  • 通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它(它們)發生了某種事件(如程序終止時要通知父程序)。

  • 資源共享:多個程序之間共享同樣的資源。為了作到這一點,需要核心提供鎖和同步機制。

  • 程序控制:有些程序希望完全控制另一個程序的執行(如Debug程序),此時控制程序希望能夠攔截另一個程序的所有陷入和異常,並能夠及時知道它的狀態改變。

程序通訊的方式

管道( pipe ):

管道包括三種:

  • 普通管道PIPE: 通常有兩種限制,一是單工,只能單向傳輸;二是隻能在父子或者兄弟程序間使用.
  • 流管道s_pipe: 去除了第一種限制,為半雙工,只能在父子或兄弟程序間使用,可以雙向傳輸.
  • 命名管道:name_pipe:去除了第二種限制,可以在許多並不相關的程序之間進行通訊.

訊號量( semophore ) :

  • 訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。

訊息佇列( message queue ) :

  • 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。

訊號 ( sinal ) :

  • 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。

共享記憶體( shared memory ) :

  • 共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現程序間的同步和通訊。

套接字( socket ) :

  • 套解口也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同機器間的程序通訊。

各程序間通訊的原理

管道

管道是如何通訊的

管道是由核心管理的一個緩衝區,相當於我們放入記憶體中的一個紙條。管道的一端連線一個程序的輸出。這個程序會向管道中放入資訊。管道的另一端連線一個程序的輸入,這個程序取出被放入管道的資訊。一個緩衝區不需要很大,它被設計成為環形的資料結構,以便管道可以被迴圈利用。當管道中沒有資訊的話,從管道中讀取的程序會等待,直到另一端的程序放入資訊。當管道被放滿資訊的時候,嘗試放入資訊的程序會等待,直到另一端的程序取出資訊。當兩個程序都終結的時候,管道也自動消失。

這裡寫圖片描述

管道是如何建立的

從原理上,管道利用fork機制建立,從而讓兩個程序可以連線到同一個PIPE上。最開始的時候,上面的兩個箭頭都連線在同一個程序Process 1上(連線在Process 1上的兩個箭頭)。當fork複製程序的時候,會將這兩個連線也複製到新的程序(Process 2)。隨後,每個程序關閉自己不需要的一個連線 (兩個黑色的箭頭被關閉; Process 1關閉從PIPE來的輸入連線,Process 2關閉輸出到PIPE的連線),這樣,剩下的紅色連線就構成了如上圖的PIPE。

這裡寫圖片描述

  • 管道通訊的實現細節 
    在 Linux 中,管道的實現並沒有使用專門的資料結構,而是藉助了檔案系統的file結構和VFS的索引節點inode。通過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。如下圖

這裡寫圖片描述

有兩個 file 資料結構,但它們定義檔案操作程序地址是不同的,其中一個是向管道中寫入資料的程序地址,而另一個是從管道中讀出資料的程序地址。這樣,使用者程式的系統呼叫仍然是通常的檔案操作,而核心卻利用這種抽象機制實現了管道這一特殊操作。

關於管道的讀寫

管道實現的原始碼在fs/pipe.c中,在pipe.c中有很多函式,其中有兩個函式比較重要,即管道讀函式pipe_read()和管道寫函式pipe_wrtie()。管道寫函式通過將位元組複製到 VFS 索引節點指向的實體記憶體而寫入資料,而管道讀函式則通過複製實體記憶體中的位元組而讀出資料。當然,核心必須利用一定的機制同步對管道的訪問,為此,核心使用了鎖、等待佇列和訊號。

當寫程序向管道中寫入時,它利用標準的庫函式write(),系統根據庫函式傳遞的檔案描述符,可找到該檔案的 file 結構。file 結構中指定了用來進行寫操作的函式(即寫入函式)地址,於是,核心呼叫該函式完成寫操作。寫入函式在向記憶體中寫入資料之前,必須首先檢查 VFS 索引節點中的資訊,同時滿足如下條件時,才能進行實際的記憶體複製工作:

  • 記憶體中有足夠的空間可容納所有要寫入的資料;
  • 記憶體沒有被讀程式鎖定。

如果同時滿足上述條件,寫入函式首先鎖定記憶體,然後從寫程序的地址空間中複製資料到記憶體。否則,寫入程序就休眠在 VFS 索引節點的等待佇列中,接下來,核心將呼叫排程程式,而排程程式會選擇其他程序執行。寫入程序實際處於可中斷的等待狀態,當記憶體中有足夠的空間可以容納寫入資料,或記憶體被解鎖時,讀取程序會喚醒寫入程序,這時,寫入程序將接收到訊號。當資料寫入記憶體之後,記憶體被解鎖,而所有休眠在索引節點的讀取程序會被喚醒。

管道的讀取過程和寫入過程類似。但是,程序可以在沒有資料或記憶體被鎖定時立即返回錯誤資訊,而不是阻塞該程序,這依賴於檔案或管道的開啟模式。反之,程序可以休眠在索引節點的等待佇列中等待寫入程序寫入資料。當所有的程序完成了管道操作之後,管道的索引節點被丟棄,而共享資料頁也被釋放。

Linux函式原型

#include <unistd.h>

int pipe(int filedes[2]);

filedes[0]用於讀出資料,讀取時必須關閉寫入端,即close(filedes[1]);

filedes[1]用於寫入資料,寫入時必須關閉讀取端,即close(filedes[0])。

程式例項:

int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];

    if(pipe(fd)  0){                 /* 先建立管道得到一對檔案描述符 */
        exit(0);
    }

    if((pid = fork())  0)            /* 父程序把檔案描述符複製給子程序 */
        exit(1);
    else if(pid > 0){                /* 父程序寫 */
        close(fd[0]);                /* 關閉讀描述符 */
        write(fd[1], "\nhello world\n", 14);
    }
    else{                            /* 子程序讀 */
        close(fd[1]);                /* 關閉寫端 */
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }

    exit(0);
}

命名管道

由於基於fork機制,所以管道只能用於父程序和子程序之間,或者擁有相同祖先的兩個子程序之間 (有親緣關係的程序之間)。為了解決這一問題,Linux提供了FIFO方式連線程序。FIFO又叫做命名管道(named PIPE)。

實現原理

FIFO (First in, First out)為一種特殊的檔案型別,它在檔案系統中有對應的路徑。當一個程序以讀(r)的方式開啟該檔案,而另一個程序以寫(w)的方式開啟該檔案,那麼核心就會在這兩個程序之間建立管道,所以FIFO實際上也由核心管理,不與硬碟打交道。之所以叫FIFO,是因為管道本質上是一個先進先出的佇列資料結構,最早放入的資料被最先讀出來,從而保證資訊交流的順序。FIFO只是借用了檔案系統(file system,命名管道是一種特殊型別的檔案,因為Linux中所有事物都是檔案,它在檔案系統中以檔名的形式存在。)來為管道命名。寫模式的程序向FIFO檔案中寫入,而讀模式的程序從FIFO檔案中讀出。當刪除FIFO檔案時,管道連線也隨之消失。FIFO的好處在於我們可以通過檔案的路徑來識別管道,從而讓沒有親緣關係的程序之間建立連線

函式原型:

#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是被建立的檔名稱,mode表示將在該檔案上設定的許可權位和將被建立的檔案型別(在此情況下為S_IFIFO),dev是當建立裝置特殊檔案時使用的一個值。因此,對於先進先出檔案它的值為0。

程式例項:

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

參考文獻

訊號量

什麼是訊號量

為了防止出現因多個程式同時訪問一個共享資源而引發的一系列問題,我們需要一種方法。比如在任一時刻只能有一個執行執行緒訪問程式碼的臨界區域。臨界區域是指執行資料更新的程式碼需要獨佔式地執行。而訊號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個執行緒在訪問它,也就是說訊號量是用來調協程序對共享資源的訪問的。

訊號量是一個特殊的變數,程式對其訪問都是原子操作,且只允許對它進行等待(即P(訊號變數))和傳送(即V(訊號變數))資訊操作。最簡單的訊號量是隻能取0和1的變數,這也是訊號量最常見的一種形式,叫做二進位制訊號量。而可以取多個正整數的訊號量被稱為通用訊號量。

訊號量的工作原理

由於訊號量只能進行兩種操作等待和傳送訊號,即P(sv)和V(sv),他們的行為是這樣的:

  • P(sv):如果sv的值大於零,就給它減1;如果它的值為零,就掛起該程序的執行
  • V(sv):如果有其他程序因等待sv而被掛起,就讓它恢復執行,如果沒有程序因等待sv而掛起,就給它加1.

舉個例子,就是兩個程序共享訊號量sv,一旦其中一個程序執行了P(sv)操作,它將得到訊號量,並可以進入臨界區,使sv減1。而第二個程序將被阻止進入臨界區,因為當它試圖執行P(sv)時,sv為0,它會被掛起以等待第一個程序離開臨界區域並執行V(sv)釋放訊號量,這時第二個程序就可以恢復執行。

Linux的訊號量機制

Linux提供了一組精心設計的訊號量介面來對訊號進行操作,它們不只是針對二進位制訊號量,下面將會對這些函式進行介紹,但請注意,這些函式都是用來對成組的訊號量值進行操作的。它們宣告在標頭檔案sys/sem.h中。

semget函式

它的作用是建立一個新訊號量或取得一個已有訊號量,原型為:

int semget(key_t key, int num_sems, int sem_flags);  
  • 第一個引數key是整數值(唯一非零),不相關的程序可以通過它訪問一個訊號量,它代表程式可能要使用的某個資源,程式對所有訊號量的訪問都是間接的,程式先通過呼叫semget函式並提供一個鍵,再由系統生成一個相應的訊號識別符號(semget函式的返回值),只有semget函式才直接使用訊號量鍵,所有其他的訊號量函式使用由semget函式返回的訊號量識別符號。如果多個程式使用相同的key值,key將負責協調工作。

  • 第二個引數num_sems指定需要的訊號量數目,它的值幾乎總是1。

  • 第三個引數sem_flags是一組標誌,當想要當訊號量不存在時建立一個新的訊號量,可以和值IPC_CREAT做按位或操作。設定了IPC_CREAT標誌後,即使給出的鍵是一個已有訊號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以建立一個新的,唯一的訊號量,如果訊號量已存在,返回一個錯誤。

semget函式成功返回一個相應訊號識別符號(非零),失敗返回-1.

semop函式

它的作用是改變訊號量的值,原型為:

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

sem_id是由semget返回的訊號量識別符號,sembuf結構的定義如下:

struct sembuf{  
    short sem_num;//除非使用一組訊號量,否則它為0  
    short sem_op;//訊號量在一次操作中需要改變的資料,通常是兩個數,一個是-1,即P(等待)操作,  
                    //一個是+1,即V(傳送訊號)操作。  
    short sem_flg;//通常為SEM_UNDO,使作業系統跟蹤訊號,  
                    //並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量  
};  

semctl函式

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

如果有第四個引數,它通常是一個union semum結構,定義如下:

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

前兩個引數與前面一個函式中的一樣,command通常是下面兩個值中的其中一個 
SETVAL:用來把訊號量初始化為一個已知的值。p 這個值通過union semun中的val成員設定,其作用是在訊號量第一次使用前對它進行設定。 
IPC_RMID:用於刪除一個已經無需繼續使用的訊號量識別符號。

參考文獻

訊息佇列

什麼是訊息佇列

訊息佇列是訊息的連結表,包括Posix訊息佇列system V訊息佇列。有足夠許可權的程序可以向佇列中新增訊息,被賦予讀許可權的程序則可以讀走佇列中的訊息。訊息佇列克服了訊號承載資訊量少,管道只能承載無格式位元組流以及緩衝區大小受限等缺點。訊息佇列是隨核心持續的。

每個訊息佇列都有一個佇列頭,用結構struct msg_queue來描述。佇列頭中包含了該訊息佇列的大量資訊,包括訊息佇列鍵值、使用者ID、組ID、訊息佇列中訊息數目等等,甚至記錄了最近對訊息佇列讀寫程序的ID。讀者可以訪問這些資訊,也可以設定其中的某些資訊。

結構msg_queue用來描述訊息佇列頭,存在於系統空間:

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;
};

結構msqid_ds用來設定或返回訊息佇列的資訊,存在於使用者空間:

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 */
};

訊息佇列與核心的聯絡

下圖說明了核心與訊息佇列是怎樣建立起聯絡的:

這裡寫圖片描述

從上圖可以看出,全域性資料結構 struct ipc_ids msg_ids 可以訪問到每個訊息佇列頭的第一個成員:struct kern_ipc_perm;而每個struct kern_ipc_perm能夠與具體的訊息佇列對應起來是因為在該結構中,有一個key_t型別成員key,而key則唯一確定一個訊息佇列。 kern_ipc_perm結構如下:

struct kern_ipc_perm{  //核心中記錄訊息佇列的全域性資料結構msg_ids能夠訪問到該結構;
key_t  key;    //該鍵值則唯一對應一個訊息佇列
uid_t  uid;
gid_t  gid;
uid_t  cuid;
gid_t  cgid;
mode_t  mode;
unsigned long seq;
}

訊息佇列的操作

  • 開啟或建立訊息佇列 
    息佇列的核心持續性要求每個訊息佇列都在系統範圍內對應唯一的鍵值,所以,要獲得一個訊息佇列的描述字,只需提供該訊息佇列的鍵值即可;

      注:訊息佇列描述字是由在系統範圍內唯一的鍵值生成的,而鍵值可以看作對應系統內的一條路經。

  • 讀寫的操作

訊息讀寫操作非常簡單,對開發人員來說,每個訊息都類似如下的資料結構:

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

mtype成員代表訊息型別,從訊息佇列中讀取訊息的一個重要依據就是訊息的型別;mtext是訊息內容,當然長度不一定為1。因此,對於傳送訊息來說, 首先預置一個msgbuf緩衝區並寫入訊息型別和內容,呼叫相應的傳送函式即可;對讀取訊息來說,首先分配這樣一個msgbuf緩衝區,然後把訊息讀入該緩衝區即可。

  • 獲得或設定訊息佇列屬性:

訊息佇列的資訊基本上都儲存在訊息佇列頭中,因此,可以分配一個類似於訊息佇列頭的結構,來返回訊息佇列的屬性;同樣可以設定該資料結構。

這裡寫圖片描述

參考文獻

訊號

訊號本質

  • 訊號是在軟體層次上對中斷機制的一種模擬,在原理上,一個程序收到一個訊號與處理器收到一箇中斷請求可以說是一樣的。訊號是非同步的,一個程序不必通過任何操作來等待訊號的到達,事實上,程序也不知道訊號到底什麼時候到達。
  • 訊號是程序間通訊機制中唯一的非同步通訊機制,可以看作是非同步通知,通知接收訊號的程序有哪些事情發生了。訊號機制經過POSIX實時擴充套件後,功能更加強大,除了基本通知功能外,還可以傳遞附加資訊。

訊號來源

訊號事件的發生有兩個來源:硬體來源(比如我們按下了鍵盤或者其它硬體故障);軟體來源,最常用傳送訊號的系統函式是kill, raise, alarm和setitimer以及sigqueue函式,軟體來源還包括一些非法運算等操作。

訊號的種類

  • 可以從兩個不同的分類角度對訊號進行分類:(1)可靠性方面:可靠訊號與不可靠訊號;(2)與時間的關係上:實時訊號與非實時訊號。

可靠訊號和不可靠訊號

不可靠訊號

Linux訊號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的訊號機制比較簡單和原始,後來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的訊號叫做”不可靠訊號”,訊號值小於SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的訊號都是不可靠訊號。這就是”不可靠訊號”的來源。它的主要問題是:

  • 程序每次處理訊號後,就將對訊號的響應設定為預設動作。在某些情況下,將導致對訊號的錯誤處理;因此,使用者如果不希望這樣的操作,那麼就要在訊號處理函式結尾再一次呼叫signal(),重新安裝該訊號。
  • 訊號可能丟失 
    因此,早期unix下的不可靠訊號主要指的是程序可能對訊號做出錯誤的反應以及訊號可能丟失。

  • Linux支援不可靠訊號,但是對不可靠訊號機制做了改進:在呼叫完訊號處理函式後,不必重新呼叫該訊號的安裝函式(訊號安裝函式是在可靠機制上的實現)。因此,Linux下的不可靠訊號問題主要指的是訊號可能丟失。

可靠訊號

  • 隨著時間的發展,實踐證明了有必要對訊號的原始機制加以改進和擴充,力圖實現”可靠訊號”。由於原來定義的訊號已有許多應用,不好再做改動,最終只好又新增加了一些訊號,並在一開始就把它們定義為可靠訊號,這些訊號支援排隊,不會丟失。
  • 訊號值位於SIGRTMIN和SIGRTMAX之間的訊號都是可靠訊號,可靠訊號克服了訊號可能丟失的問題。Linux在支援新版本的訊號安裝函式sigation()以及訊號傳送函式sigqueue()的同時,仍然支援早期的signal()訊號安裝函式,支援訊號傳送函式kill()

注意:可靠訊號是指後來新增的新訊號(訊號值位於SIGRTMIN及SIGRTMAX之間);不可靠訊號是訊號值小於SIGRTMIN的訊號。訊號的可靠與不可靠只與訊號值有關,與訊號的傳送及安裝函式無關。

實時訊號與非實時訊號

  • 非實時訊號都不支援排隊,都是不可靠訊號,編號是1-31,0是空訊號;實時訊號都支援排隊,都是可靠訊號。

程序對訊號的響應

  • 忽略訊號,即對訊號不做任何處理,其中,有兩個訊號不能忽略:SIGKILL及SIGSTOP;
  • 捕捉訊號。定義訊號處理函式,當訊號發生時,執行相應的處理函式;
  • 執行預設操作,Linux對每種訊號都規定了預設操作

注意:程序對實時訊號的預設反應是程序終止。

訊號的傳送和安裝

  • 傳送訊號的主要函式有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。
  • 如果程序要處理某一訊號,那麼就要在程序中安裝該訊號。安裝訊號主要用來確定訊號值及程序針對該訊號值的動作之間的對映關係,即程序將要處理哪個訊號;該訊號被傳遞給程序時,將執行何種操作。

注意: inux主要有兩個函式實現訊號的安裝:signal()、sigaction()。其中signal()在可靠訊號系統呼叫的基礎上實現, 是庫函式。它只有兩個引數,不支援訊號傳遞資訊,主要是用於前32種非實時訊號的安裝;而sigaction()是較新的函式(由兩個系統呼叫實現:sys_signal以及sys_rt_sigaction),有三個引數,支援訊號傳遞資訊,主要用來與 sigqueue() 系統呼叫配合使用,當然,sigaction()同樣支援非實時訊號的安裝。sigaction()優於signal()主要體現在支援訊號帶有引數。

參考文獻:

共享記憶體

共享記憶體可以說是最有用的程序間通訊方式,也是最快的IPC形式。是針對其他通訊機制執行效率較低而設計的。兩個不同程序A、B共享記憶體的意思是,同一塊實體記憶體被對映到程序A、B各自的程序地址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。由於多個程序共享同一塊記憶體區域,必然需要某種同步機制,互斥鎖和訊號量都可以。

系統V共享記憶體原理

程序間需要共享的資料被放在一個叫做IPC共享記憶體區域的地方,所有需要訪問該共享區域的程序都要把該共享區域對映到本程序的地址空間中去。系統V共享記憶體通過shmget獲得或建立一個IPC共享記憶體區域,並返回相應的識別符號。核心在保證shmget獲得或建立一個共享記憶體區,初始化該共享記憶體區相應的shmid_kernel結構體的同時,還將在特殊檔案系統shm中,建立並開啟一個同名檔案,並在記憶體中建立起該檔案的相應dentry及inode結構,新開啟的檔案不屬於任何一個程序(任何程序都可以訪問該共享記憶體區)。所有這一切都是系統呼叫shmget完成的。

系統V共享記憶體API

shmget()用來獲得共享記憶體區域的ID,如果不存在指定的共享區域就建立相應的區域。shmat()把共享記憶體區域對映到呼叫程序的地址空間中去,這樣,程序就可以方便地對共享區域進行訪問操作。shmdt()呼叫用來解除程序對共享記憶體區域的對映。shmctl實現對共享記憶體區域的控制操作。

套接字(socket)

最早出現在UNIX系統中,是UNIX系統主要的資訊傳遞方式。

Socket相關概念

兩個基本概念:客戶方和服務方。當兩個應用之間需要採用SOCKET通訊時,首先需要在兩個應用之間(可能位於同一臺機器,也可能位於不同的機器)建立SOCKET連線。

  • 發起呼叫連線請求的一方為客戶方 
    在客戶方呼叫連線請求之前,它必須知道服務方在哪裡。所以需要知道服務方所在機器的IP地址或機器名稱,如果客戶方和服務方事前有一個約定就好了,這個約定就是PORT(埠號)。也就是說,客戶方可以通過服務方所在機器的IP地址或機器名稱和埠號唯一的確定方式來呼叫服務方。

  • 接受呼叫連線請求的一方成為服務方。 
    在客戶方呼叫之前,服務方必須處於偵聽狀態,偵聽是否有客戶要求建立連線。一旦接到連線請求,服務方可以根據情況建立或拒絕連線。當客戶方的訊息到達服務方埠時,會自動觸發一個事件(event),服務方只要接管該事件,就可以接受來自客戶方的訊息了。

Socket型別

  • 流式Socket(STREAM):是一種面向連線的Socekt,針對面向連線的TCP服務應用,安全,但是效率低;
  • 資料報式Socket(DATAGAM):是一種無連線的Socket,對應於無連線的UDP服務應用。不安(丟失,順序混亂,在接受端要分析重排及要求重發),但效率高。

Socket一般應用模式(服務端和客戶端)

這裡寫圖片描述

Socket通訊基本流程圖

這裡寫圖片描述

參考文獻