1. 程式人生 > >Linux 程序間通訊(IPC)總結

Linux 程序間通訊(IPC)總結

概述

一個大型的應用系統,往往需要眾多程序協作,程序(Linux程序概念見附1)間通訊的重要性顯而易見。本系列文章闡述了 Linux 環境下的幾種主要程序間通訊手段。

程序隔離

程序隔離是為保護作業系統中程序互不干擾而設計的一組不同硬體和軟體的技術。這個技術是為了避免程序A寫入程序B的情況發生。 程序的隔離實現,使用了虛擬地址空間。程序A的虛擬地址和程序B的虛擬地址不同,這樣就防止程序A將資料資訊寫入程序B。

虛擬地址空間

當建立一個程序時,作業系統會為該程序分配一個 4GB 大小的虛擬程序地址空間。之所以是 4GB ,是因為在 32 位的作業系統中,一個指標長度是 4 位元組,而 4 位元組指標的定址能力是從 0x00000000~0xFFFFFFFF ,最大值 0xFFFFFFFF 表示的即為 4GB 大小的容量。與虛擬地址空間相對的,還有一個實體地址空間,這個地址空間對應的是真實的實體記憶體。要注意的是這個 4GB 的地址空間是“虛擬”的,並不是真實存在的,而且每個程序只能訪問自己虛擬地址空間中的資料,無法訪問別的程序中的資料,通過這種方法實現了程序間的地址隔離。

針對 Linux 作業系統,將最高的1G位元組(從虛擬地址 0xC0000000 到 0xFFFFFFFF )供核心使用,稱為核心空間,而較低的 3G 位元組(從虛擬地址 0x00000000 到0xBFFFFFFF),供各個程序使用,稱為使用者空間。每個程序都可以通過系統呼叫進入到核心。其中在 Linux 系統中,程序的使用者空間是獨立的,而核心空間是共有的,程序切換時,使用者空間切換,核心空間不變。

建立虛擬地址空間目的是為了解決程序地址空間隔離的問題。但程式要想執行,必須執行在真實的記憶體上,所以,必須在虛擬地址與實體地址間建立一種對映關係。這樣,通過對映機制,當程式訪問虛擬地址空間上的某個地址值時,就相當於訪問了實體地址空間中的另一個值。人們想到了一種分段、分頁的方法,它的思想是在虛擬地址空間和實體地址空間之間做一一對映。這種思想理解起來並不難,作業系統保證不同程序的地址空間被對映到實體地址空間中不同的區域上,這樣每個程序最終訪問到的實體地址空間都是彼此分開的。通過這種方式,就實現了程序間的地址隔離。

 

系統呼叫/核心態/使用者態

雖然從邏輯上抽離出使用者空間和核心空間;但是不可避免的的是,總有那麼一些使用者空間需要訪問核心的資源;比如應用程式訪問檔案,網路是很常見的事情,怎麼辦呢?

使用者空間訪問核心空間的唯一方式就是系統呼叫;通過這個統一入口介面,所有的資源訪問都是在核心的控制下執行,以免導致對使用者程式對系統資源的越權訪問,從而保障了系統的安全和穩定。使用者軟體良莠不齊,要是它們亂搞把系統玩壞了怎麼辦?因此對於某些特權操作必須交給安全可靠的核心來執行。

當一個任務(程序)執行系統呼叫而陷入核心程式碼中執行時,我們就稱程序處於核心執行態(或簡稱為核心態)此時處理器處於特權級最高的(0級)核心程式碼中執行。當程序在執行使用者自己的程式碼時,則稱其處於使用者執行態(使用者態)。即此時處理器在特權級最低的(3級)使用者程式碼中執行。處理器在特權等級高的時候才能執行那些特權CPU指令。

IPC 通訊原理

理解了上面的幾個概念,我們再來看看程序之間是如何實現通訊的。

通常的做法是訊息傳送方將要傳送的資料存放在記憶體快取區中,通過系統呼叫進入核心態。然後核心程式在核心空間分配記憶體,開闢一塊核心快取區,呼叫 copy_from_user() 函式將資料從使用者空間的記憶體快取區拷貝到核心空間的核心快取區中。同樣的,接收方程序在接收資料時在自己的使用者空間開闢一塊記憶體快取區,然後核心程式呼叫 copy_to_user() 函式將資料從核心快取區拷貝到接收程序的記憶體快取區。這樣資料傳送方程序和資料接收方程序就完成了一次資料傳輸,我們稱完成了一次程序間通訊。如下圖:

程序間通訊方式

Linux 程序間基本的通訊方式主要有:管道(pipe) (包括匿名管道和命名管道)、訊號(signal)、訊息佇列(queue)、共享記憶體、訊號量和套接字。

管道

管道的實質是一個核心緩衝區(呼叫 pipe 函式來開闢),管道的作用正如其名,需要通訊的兩個程序在管道的兩端,程序利用管道傳遞資訊。管道對於管道兩端的程序而言,就是一個檔案,但是這個檔案比較特殊,它不屬於檔案系統並且只存在於記憶體中。 Linux一切皆檔案,作業系統為管道提供操作的方法:檔案操作,用 fork 來共享管道原理。

管道依據是否有名字分為匿名管道和命名管道(有名管道),這兩種管道有一定的區別。

匿名管道有幾個重要的限制:

  1. 管道是半雙工的,資料只能在一個方向上流動,A程序傳給B程序,不能反向傳遞
  2. 管道只能用於父子程序或兄弟程序之間的通訊,即具有親緣關係的程序。

命名管道允許沒有親緣關係的程序進行通訊。命名管道不同於匿名管道之處在於它提供了一個路徑名與之關聯,這樣一個程序即使與建立有名管道的程序不存在親緣關係,只要可以訪問該路徑,就能通過有名管道互相通訊。

pipe 函式接受一個引數,是包含兩個整數的陣列,如果呼叫成功,會通過 pipefd[2] 傳出給使用者程式兩個檔案描述符,需要注意 pipefd[0] 指向管道的讀端, pipefd[1] 指向管道的寫端,那麼此時這個管道對於使用者程式就是一個檔案,可以通過 read(pipefd [0]);或者 write(pipefd [1]) 進行操作。pipe 函式呼叫成功返回 0,否則返回 -1.

那麼再來看看通過管道進行通訊的步驟:

  • 父程序建立管道,得到兩個檔案描述符指向管道的兩端

  • 利用fork函式創建出子程序,則子程序也得到兩個檔案描述符指向同一管道

  • 父程序關閉讀端(pipe[0]),子程序關閉寫端pipe[1],則此時父程序可以往管道中進行寫操作,子程序可以從管道中讀,從而實現了通過管道的程序間通訊。

     

管道的特點:

  • 只能單向通訊

兩個檔案描述符,用一個,另一個不用,不用的檔案描述符就要 close

  • 只能血緣關係的程序進行通訊

  • 依賴於檔案系統

  • 生命週期隨程序

  • 面向位元組流的服務

面向位元組流:資料無規則,沒有明顯邊界,收發資料比較靈活:對於使用者態,可以一次性發送也可以分次傳送,當然接受資料也如此;而面向資料報:資料有明顯邊界,資料只能整條接受 

  • 管道內部提供了同步機制

臨界資源: 大家都能訪問到的共享資源

臨界區: 對臨界資源進行操作的程式碼

同步: 臨界資源訪問的可控時序性(一個操作完另一個才可以操作)

互斥: 對臨界資源同一時間的唯一訪問性(保護臨界資源安全)

說明:因為管道通訊是單向的,在上面的例子中我們是通過子程序寫父程序來讀,如果想要同時父程序寫而子程序來讀,就需要再開啟另外的管道;

管道的讀寫端通過開啟的檔案描述符來傳遞,因此要通訊的兩個程序必須從它們的公共祖先那裡繼承管道的件描述符。 上面的例子是父程序把檔案描述符傳給子程序之後父子程序之 間通訊,也可以父程序fork兩次,把檔案描述符傳給兩個子程序,然後兩個子程序之間通訊, 總之 需要通過fork傳遞檔案描述符使兩個程序都能訪問同一管道,它們才能通訊。

四個特殊情況:

  1. 如果所有指向管道寫端的檔案描述符都關閉了,而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣

  2. 如果有指向管道寫端的檔案描述符沒關閉,而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。

  3. 如果所有指向管道讀端的檔案描述符都關閉了,這時有程序指向管道的寫端write,那麼該程序會收到訊號SIGPIPE,通常會導致程序異常終止。

  4. 如果有指向管道讀端的檔案描述符沒關閉,而持有管道寫端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再write會阻塞,直到管道中有空位置了才寫入資料並返回。

命名管道FIFO

在管道中,只有具有血緣關係的程序才能進行通訊,對於後來的命名管道,就解決了這個問題。FIFO 不同於管道之處在於它提供一個路徑名與之關聯,以 FIFO 的檔案形式儲存於檔案系統中。命名管道是一個裝置檔案,因此,即使程序與建立FIFO的程序不存在親緣關係,只要可以訪問該路徑,就能夠通過 FIFO 相互通訊。值得注意的是, FIFO (first input first output) 總是按照先進先出的原則工作,第一個被寫入的資料將首先從管道中讀出。

命名管道的建立

建立命名管道的系統函式有兩個: mknod 和 mkfifo。兩個函式均定義在標頭檔案 sys/stat.h,
函式原型如下:

#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
函式 mknod 引數中 path 為建立的命名管道的全路徑名: mod 為建立的命名管道的模指明其存取許可權; dev 為裝置值,該值取決於檔案建立的種類,它只在建立裝置檔案時才會用到。這兩個函式呼叫成功都返回 0,失敗都返回 -1。

命名管道開啟特性:

  1. 如果用只讀開啟命名管道,open 函式將阻塞等待直至有其他程序以寫的方式開啟這個命名管道,如果沒有程序以寫的方式發開這個命名管道,程式將停在此處

  2. 如果用只寫開啟命名管道,open 函式將阻塞等到直至有其他程序以讀的方式開啟這個命名管道,如果沒有程序以讀的方式發開這個命名管道,程式將停在此處;

  3. 如果用讀寫開啟命名管道,則不會阻塞(但是管道是單向)

System V IPC   

IPC(Inter-Process Communication)是指多個程序之間相互通訊,交換資訊的方法,System V 是 Unix 作業系統最早的商業發行版,由 AT&T(American Telephone & Telegraph)開發。System V IPC 是指 Linux 引入自 System V 的程序通訊機制,一共有三種:

  • 訊號量,用來管理對共享資源的訪問;

  • 共享記憶體,用來高效地實現程序間的資料共享;

  • 訊息佇列,用來實現程序間資料的傳遞。

這三種統稱 IPC 資源,每個 IPC 資源都是請求時動態建立的,都是永駐記憶體,除非被程序顯示釋放,都是可以被任一程序使用。每個 IPC 資源都使用一個 32 位的 IPC 關鍵字和 32 位的 IPC 識別符號,前者類似檔案系統中的路徑名,由程式自由定製,後者類似開啟檔案的檔案描述符,由核心統一分配,在系統內部是唯一的,當多個程序使用同一個IPC資源通訊時需要該資源的 IPC 識別符號。     

建立新的 IPC 資源時需要指定 IPC 關鍵字,如果沒有與之關聯的 IPC 資源,則建立一個新的 IPC 資源;如果已經存在,則判斷當前程序是否具有訪問許可權,是否超過資源使用限制等,如果符合條件則返回該資源的 IPC 識別符號。為了避免兩個不同的 IPC 資源使用相同的 IPC 關鍵字,建立時可以指定IPC關鍵字為 IPC_PRIVATE,由核心負責生成一個唯一的關鍵字。   

建立新的 IPC 資源時最後一個引數可以包括三個標誌,PC_CREAT 說明如果IPC資源不存在則必須建立它,IPC_EXCL 說明如果資源已經存在且設定了 PC_CREAT 標誌則建立失敗,IPC_NOWAIT 說明訪問 IPC 資源時程序從不阻塞。 

訊號量

訊號量(semaphore)是一種用於提供不同程序之間或者一個給定的不同執行緒間同步手段的原語。訊號量多用於程序間的同步與互斥,簡單的說一下同步和互斥的意思:

同步:處理競爭就是同步,安排程序執行的先後順序就是同步,每個程序都有一定的先後執行順序。

互斥:互斥訪問不可共享的臨界資源,同時會引發兩個新的控制問題(互斥可以說是特殊的同步)。

競爭:當併發程序競爭使用同一個資源的時候,我們就稱為競爭程序。

共享資源通常分為兩類:一類是互斥共享資源,即任一時刻只允許一個程序訪問該資源;另一類是同步共享資源,即同一時刻允許多個程序訪問該資源;訊號量是解決互斥共享資源的同步問題而引入的機制。

下面說一下訊號量的工作機制,可以直接理解成計數器(當然其實加鎖的時候肯定不能這麼簡單,不只只是訊號量了),訊號量會有初值(>0),每當有程序申請使用訊號量,通過一個 P 操作來對訊號量進行-1操作,當計數器減到 0 的時候就說明沒有資源了,其他程序要想訪問就必須等待(具體怎麼等還有說法,比如忙等待或者睡眠),當該程序執行完這段工作(我們稱之為臨界區)之後,就會執行 V 操作來對訊號量進行 +1 操作。

  • 臨界區:臨界區指的是一個訪問共用資源(例如:共用裝置或是共用儲存器)的程式片段,而這些共用資源又無法同時被多個執行緒訪問的特性。

  • 臨界資源:只能被一個程序同時使用(不可以多個程序共享),要用到互斥。

我們可以說訊號量也是程序間通訊的一種方式,比如互斥鎖的簡單實現就是訊號量,一個程序使用互斥鎖,並通知(通訊)其他想要該互斥鎖的程序,阻止他們的訪問和使用。

當有程序要求使用共享資源時,需要執行以下操作:

  1. 系統首先要檢測該資源的訊號量;

  2. 若該資源的訊號量值大於 0,則程序可以使用該資源,此時,程序將該資源的訊號量值減1;

  3. 若該資源的訊號量值為 0,則程序進入休眠狀態,直到訊號量值大於 0 時程序被喚醒,訪問該資源;

       當程序不再使用由一個訊號量控制的共享資源時,該訊號量值增加 1,如果此時有程序處於休眠狀態等待此訊號量,則該程序會被喚醒

每個訊號量集都有一個與其相對應的結構,該結構定義如下:

/* Data structure describing a set of semaphores.  */  
struct semid_ds  
{  
    struct ipc_perm sem_perm;   /* operation permission struct */  
    struct sem *sem_base;       /* ptr to array of semaphores in set */  
    unsigned short sem_nsems;   /* # of semaphores in set */  
    time_t sem_otime;           /* last-semop() time */  
    time_t sem_ctime;           /* last-change time */  
};  
  
/* Data structure describing each of semaphores.  */  
struct sem  
{  
    unsigned short semval;  /* semaphore value, always >= 0 */  
    pid_t          sempid;  /* pid for last successful semop(), SETVAL, SETALL */  
    unsigned short semncnt; /* # processes awaiting semval > curval */  
    unsigned short semzcnt; /* # processes awaiting semval == 0 */  
};  

訊號量集的結構圖如下所示:

訊息佇列

訊息佇列,是訊息的連結表,存放在核心中。一個訊息佇列由一個識別符號(即佇列 ID)來標識。其具有以下特點:

  1. 訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序。

  2. 訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容並不會被刪除。

  3. 訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取。

原型

1 #include <sys/msg.h>
2 // 建立或開啟訊息佇列:成功返回佇列ID,失敗返回-1
3 int msgget(key_t key, int flag);
4 // 新增訊息:成功返回0,失敗返回-1
5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
6 // 讀取訊息:成功返回訊息資料的長度,失敗返回-1
7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
8 // 控制訊息佇列:成功返回0,失敗返回-1
9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

在以下兩種情況下,msgget 將建立一個新的訊息佇列:

  • 如果沒有與鍵值key相對應的訊息佇列,並且flag中包含了IPC_CREAT標誌位。

  • key引數為IPC_PRIVATE

函式msgrcv在讀取訊息佇列時,type引數有下面幾種情況:

  • type == 0,返回佇列中的第一個訊息;

  • type > 0,返回佇列中訊息型別為 type 的第一個訊息;

  • type < 0,返回佇列中訊息型別值小於或等於 type 絕對值的訊息,如果有多個,則取型別值最小的訊息。

可以看出,type 值非 0 時用於以非先進先出次序讀訊息。也可以把 type 看做優先順序的權值。

共享記憶體

共享記憶體是 System V 版本的最後一個程序間通訊方式。共享記憶體,顧名思義就是允許兩個不相關的程序訪問同一個邏輯記憶體,共享記憶體是兩個正在執行的程序之間共享和傳遞資料的一種非常有效的方式。不同程序之間共享的記憶體通常為同一段實體記憶體。程序可以將同一段實體記憶體連線到他們自己的地址空間中,所有的程序都可以訪問共享記憶體中的地址。如果某個程序向共享記憶體寫入資料,所做的改動將立即影響到可以訪問同一段共享記憶體的任何其他程序。

特別提醒:共享記憶體並未提供同步機制,也就是說,在第一個程序結束對共享記憶體的寫操作之前,並無自動機制可以阻止第二個程序開始對它進行讀取,所以我們通常需要用其他的機制來同步對共享記憶體的訪問,例如訊號量。

共享記憶體的通訊原理
在 Linux 中,每個程序都有屬於自己的程序控制塊(PCB)和地址空間(Addr Space),並且都有一個與之對應的頁表,負責將程序的虛擬地址與實體地址進行對映,通過記憶體管理單元(MMU)進行管理。兩個不同的虛擬地址通過頁表對映到物理空間的同一區域,它們所指向的這塊區域即共享記憶體。

共享記憶體的通訊原理示意圖:

 

對於上圖我的理解是:當兩個程序通過頁表將虛擬地址對映到實體地址時,在實體地址中有一塊共同的記憶體區,即共享記憶體,這塊記憶體可以被兩個程序同時看到。這樣當一個程序進行寫操作,另一個程序讀操作就可以實現程序間通訊。但是,我們要確保一個程序在寫的時候不能被讀,因此我們使用訊號量來實現同步與互斥。

對於一個共享記憶體,實現採用的是引用計數的原理,當程序脫離共享儲存區後,計數器減一,掛架成功時,計數器加一,只有當計數器變為零時,才能被刪除。當程序終止時,它所附加的共享儲存區都會自動脫離。

為什麼共享記憶體速度最快?

藉助上圖說明:Proc A 程序給記憶體中寫資料, Proc B 程序從記憶體中讀取資料,在此期間一共發生了兩次複製

(1)Proc A 到共享記憶體       (2)共享記憶體到 Proc B

因為直接在記憶體上操作,所以共享記憶體的速度也就提高了。

共享記憶體的介面函式以及指令

檢視系統中的共享儲存段

ipcs -m

刪除系統中的共享儲存段

ipcrm -m [shmid]

shmget ( ):建立共享記憶體

int shmget(key_t key, size_t size, int shmflg);

[引數key]:由ftok生成的key標識,標識系統的唯一IPC資源。

[引數size]:需要申請共享記憶體的大小。在作業系統中,申請記憶體的最小單位為頁,一頁是4k位元組,為了避免記憶體碎片,我們一般申請的記憶體大小為頁的整數倍。

[引數shmflg]:如果要建立新的共享記憶體,需要使用IPC_CREAT,IPC_EXCL,如果是已經存在的,可以使用IPC_CREAT或直接傳0。

[返回值]:成功時返回一個新建或已經存在的的共享記憶體識別符號,取決於shmflg的引數。失敗返回-1並設定錯誤碼。


shmat ( ):掛接共享記憶體

void *shmat(int shmid, const void *shmaddr, int shmflg);

[引數shmid]:共享儲存段的識別符號。

[引數*shmaddr]:shmaddr = 0,則儲存段連線到由核心選擇的第一個可以地址上(推薦使用)。

[引數shmflg]:若指定了SHM_RDONLY位,則以只讀方式連線此段,否則以讀寫方式連線此段。

[返回值]:成功返回共享儲存段的指標(虛擬地址),並且核心將使其與該共享儲存段相關的shmid_ds結構中的shm_nattch計數器加1(類似於引用計數);出錯返回-1。


shmdt ( ):去關聯共享記憶體:當一個程序不需要共享記憶體的時候,就需要去關聯。該函式並不刪除所指定的共享記憶體區,而是將之前用shmat函式連線好的共享記憶體區脫離目前的程序。

int shmdt(const void *shmaddr);

[引數*shmaddr]:連線以後返回的地址。

[返回值]:成功返回0,並將shmid_ds結構體中的 shm_nattch計數器減1;出錯返回-1。


shmctl ( ):銷燬共享記憶體

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

[引數shmid]:共享儲存段識別符號。

[引數cmd]:指定的執行操作,設定為IPC_RMID時表示可以刪除共享記憶體。

[引數*buf]:設定為NULL即可。

[返回值]:成功返回0,失敗返回-1。

POSIX 訊息佇列   

POSIX 訊息佇列是 POSIX 標準在 2001 年定義的一種 IPC 機制,與 System V 中的訊息佇列相比有如下差異:

  • 更簡單的基於檔案的應用介面,Linux 通過 mqueue 的特殊檔案系統來實現訊息佇列,佇列名跟檔名類似,必須以"/"開頭,每個訊息佇列在檔案系統內都有一個對應的索引節點,返回的佇列描述符實際是一個檔案描述符

  • 完全支援訊息優先順序,訊息在佇列中是按照優先順序倒序排列的(即0表示優先順序最低)。當一條訊息被新增到佇列中時,它會被放置在佇列中具有相同優先順序的所有訊息之後。如果一個應用程式無需使用訊息優先順序,那麼只需要將msg_prio指定為0即可。

  • 完全支援訊息到達的非同步通知,當新訊息到達且當前佇列為空時會通知之前註冊過表示接受通知的程序。在任何一個時刻都只有一個程序能夠向一個特定的訊息佇列註冊接收通知。如果一個訊息佇列上已經存在註冊程序了,那麼後續在該佇列上的註冊請求將會失敗。可以給程序傳送訊號或者另起一個執行緒呼叫通知函式完成通知。當通知完成時,註冊即被撤銷,程序需要繼續接受通知則必須重新註冊。

  • 用於阻塞傳送與接收操作的超時機制,可以指定阻塞的最長時間,超時自動返回 

套接字:

套接字是更為基礎的程序間通訊機制,與其他方式不同的是,套接字可用於不同機器之間的程序間通訊。

有兩種型別的套接字:基於檔案的和麵向網路的。

  • Unix 套接字是基於檔案的,並且擁有一個“家族名字”--AF_UNIX,它代表地址家族 (address family):UNIX。

  • 第二型別的套接字是基於網路的,它也有自己的家族名字--AF_INET,代表地址家族 (address family):INTERNET

不管採用哪種地址家族,都有兩種不同的套接字連線:面向連線的和無連線的。

  • 面向連線的套接字 (SOCK_STREAM):進行通訊前必須建立一個連線,面向連線的通訊提供序列化的、可靠地和不重複的資料交付,而沒有記錄邊界。

這意味著每條資訊可以被拆分成多個片段,並且每個片段都能確保到達目的地,然後在目的地將資訊拼接起來。

實現這種連線型別的主要協議是傳輸控制協議 (TCP)。

  • 無連線的套接字 (SOCK_DGRAM):在通訊開始之前並不需要建立連線,在資料傳輸過程中並無法保證它的順序性、可靠性或重複性。

然而,資料報確實儲存了記錄邊界,這就意味著訊息是以整體傳送的,而並非首先分成多個片段。

由於面向連線的套接字所提供的保證,因此它們的設定以及對虛擬電路連線的維護需要大量的開銷。然而,資料報不需要這些開銷,即它的成本更加“低廉”

實現這種連線型別的主要協議是使用者資料報協議 (UDP)。

訊號

訊號是軟體層次上對中斷機制的一種模擬,是一種非同步通訊方式,程序不必通過任何操作來等待訊號的到達。訊號可以在使用者空間程序和核心之間直接互動,核心可以利用訊號來通知使用者空間的程序發生了哪些系統事件。

訊號來源:

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

程序對訊號的響應:

程序可以通過三種方式來響應訊號:

  • 忽略訊號,即對訊號不做任何處理,但是有兩個訊號是不能忽略的:SIGKLL 和 SIGSTOP;

  • 捕捉訊號,定義訊號處理函式,當訊號發生時,執行相應的處理函式;

  • 執行預設操作,Linux 對每種訊號都規定了預設操作。