Linux IPC總結(全)
IPC程式間通訊(Inter-Process Communication)就是指多個程式之間相互通訊,交換資訊的方法。Linux IPC基本上都是從Unix平臺上繼承而來的。主要包括最初的Unix IPC,System V IPC以及基於Socket的IPC。另外,Linux也支援POSIX IPC。
一、System V, BSD, POSIX
System V是Unix作業系統最早的商業發行版之一。它最初由AT&T(American Telephone & Telegraph)開發,最早在1983年釋出。System V主要發行了4個版本,其中SVR4(System V Release 4)是最成功的版本。BSD(Berkeley Software Distribution,有時也被稱為Berkeley Unix)是加州大學於1977至1995年間開發的。在19世紀八十年代至九十年代之間,System V和BSD代表了Unix的兩種主要的操作風格。它們的主要區別如下:
系統 System V BSD
root指令碼位置 /etc/init.d/ /etc/rc.d/
預設shell Bshell Cshell
檔案系統資料 /etc/mnttab /etc/mtab
核心位置 /UNIX /vmUnix
印表機裝置 lp rlp
字串函式 memcopy bcopy
終端初始化設定檔案 /etc/initab /etc/ttys
終端控制 termio termios
Linux系統的操作風格往往介於這兩種風格之間。
POSIX(Portable Operating System Interface [for Unix])是由IEEE(Institute of Electrical and Electronics Engineers,電子電氣工程協會)開發的。現有的大部分Unix都遵循POSIX標準,而Linux從一開始就遵循POSIX標準。
二、最初的Unix IPC
1、訊號
訊號是Unix/Linux系統在一定條件下生成的事件。訊號是一種非同步通訊機制,程式不需要執行任何操作來等待訊號的到達。訊號非同步通知接收訊號的程式發生了某個事件,然後作業系統將會中斷接收到訊號的程式的執行,轉而去執行相應的訊號處理程式。
(1)註冊訊號處理函式
#include <signal.h>
/*typedef void (*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler);*/
void (*signal(int signum, void (*handler)(int)))(int); //SIG_IGN && SIG_DFL
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
(2)傳送訊號
#include <signal.h>
int kill(pid_t pid,int sig); //#include <sys/types.h>
int raise(int sig); //kill(getpid(),sig);
unsigned int alarm(unsigned int seconds); //(#include <unistd.h>) seconds秒後,向程式本身傳送SIGALRM訊號。
(3)訊號集
訊號集被定義為:typedef struct {unsigned long sig[_NSIG_WORDS];} sigset_t;
int sigaddset(sigset_t *set,int sig);
int sigemptyset(sigset_t *set);
2、管道(Pipe)
管道用來連線不同程式之間的資料流。
(1)在兩個程式之間傳遞資料的最簡單的方法是使用popen()和pclose()函式:
#include <stdio.h>
FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *stream);
popen()函式首先呼叫一個shell,然後把command作為引數傳遞給shell。這樣每次呼叫popen()函式都需要啟動兩個程式;但是由於在Linux中,所有的引數擴充套件(parameter expansion)都是由shell執行的,這樣command中包含的所有引數擴充套件都可以在command程式啟動之前完成。
(2)pipe()函式:
#include <unistd.h>
int pipe(int pipefd[2]);
popen()函式只能返回一個管道描述符,並且返回的是檔案流(file stream),可以使用函式fread()和fwrite()來訪問。pipe()函式可以返回兩個管道描述符:pipefd[0]和pipefd[1],任何寫入pipefd[1]的資料都可以從pipefd[0]讀回;pipe()函式返回的是檔案描述符(file
descriptor),因此只能使用底層的read()和write()系統呼叫來訪問。pipe()函式通常用來實現父子程式之間的通訊。
(3)命名管道:FIFO
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *fifo_name, mode_t mode);
前面兩種管道只能用在相關的程式之間,使用命名管道可以解決這個問題。在使用open()開啟FIFO時,mode中不能包含O_RDWR。mode最常用的是O_RDONLY,O_WRONLY與O_NONBLOCK的組合。O_NONBLOCK影響了read()和write()在FIFO上的執行方式。
PS:要想檢視庫函式用法,最可靠的資料來自Linux manual page:
$sudo apt-get install manpages-dev
$man 3 function_name
三、System V IPC
System V IPC指的是AT&T在System V.2發行版中引入的三種程式間通訊工具:(1)訊號量,用來管理對共享資源的訪問 (2)共享記憶體,用來高效地實現程式間的資料共享 (3)訊息佇列,用來實現程式間資料的傳遞。我們把這三種工具統稱為System V IPC的物件,每個物件都具有一個唯一的IPC識別符號(identifier)。要保證不同的程式能夠獲取同一個IPC物件,必須提供一個IPC鍵(IPC key),核心負責把IPC鍵轉換成IPC識別符號。
System V IPC具有相似的語法,一般操作如下:
(1)選擇IPC鍵,可以使用如下三種方式:
a)IPC_PRIVATE。由核心負責選擇一個鍵然後生成一個IPC物件並把IPC識別符號直接傳遞給另一個程式。
b)直接選擇一個鍵。
c)使用ftok()函式生成一個鍵。
(2)使用semget()/shmget()/msgget()函式根據IPC鍵key和一個標誌flag建立或訪問IPC物件。如果key是IPC_PRIVATE;或者key尚未與已經存在的IPC物件相關聯且flag中包含IPC_CREAT標誌,那麼就會建立一個全新的IPC物件。
(3)使用semctl()/shmctl()/msgctl()函式修改IPC物件的屬性。
(4)使用semctl()/shmctl()/msgctl()函式和IPC_RMID標誌銷燬IPC例項。
System V IPC為每個IPC物件設定了一個ipc_perm結構體並在建立IPC物件的時候進行初始化。這個結構體中定義了IPC物件的訪問許可權和所有者:
struct ipc_perm{
uid_t uid; //所有者的使用者id
gid_t gid; //所有者的組id
uid_t cuid; //建立者的使用者id
gid_t cgid; //建立者的組id
mode_t mode; //訪問模式
…
};
shell中管理IPC物件的命令是ipcs、ipcmk和ipcrm。
1、訊號量(Semaphores)
System V的訊號量集表示的是一個或多個訊號量的集合。核心為每個訊號量集維護一個semid_ds資料結構,而訊號量集中的每個訊號量使用一個無名結構體表示,這個結構體至少包含以下成員:
struct{
unsigned short semval;//訊號量值,總是>=0
pid_t sempid; //上一次操作的pid
…
};
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
(1)建立或訪問訊號量
* int semget(key_t key,int nsems,int flag);
nsems指定訊號量集中訊號量的個數,如果只是獲取訊號量集的識別符號(而非新建),那麼nsems可以為0。flag的低9位作為訊號量的訪問許可權位,類似於檔案的訪問許可權;如果flag中同時指定了IPC_CREAT和IPC_EXCL,那麼如果key已與現存IPC物件想關聯的話,函式將會返回EEXIST錯誤。例如,flag可以為IPC_CREAT|0666。
(2)控制訊號量集
* int semctl(int semid,int semnum,int cmd,union semun arg);
對semid訊號量集合執行cmd操作;cmd常用的兩個值是:SETVAL初始化第semnum個訊號量的值為arg.val;IPC_RMID刪除訊號量。
(3)對一個或多個訊號量進行操作
* int semop(int semid,struct sembuf *sops,unsigned nsops);
* struct sembuf{
unsigned short sem_num; //訊號量索引
short sem_op; //對訊號量進行的操作,常用的兩個值為-1和+1,分別代表P、V操作
short sem_flag; //比較重要的值是SEM_UNDO:當程式結束時,相應的操作將被取消;同時,如果程式結束時沒有釋放資源的話,系統會自動釋放
};
2、共享記憶體
共享記憶體允許兩個或多個程式共享一定的儲存區,因為不需要拷貝資料,所以這是最快的一種IPC。
#include <sys/ipc.h>
#include <sys/shm.h>
(1)建立或訪問共享記憶體
* int shmget(key_t key,size_t size,int shmflg);
(2)附加共享記憶體到程式的地址空間
* void *shmat(int shmid,const void *shmaddr,int shmflg);//shmaddr通常為NULL,由系統選擇共享記憶體附加的地址;shmflg可以為SHM_RDONLY
(3)從程式的地址空間分離共享記憶體
* int shmdt(const void *shmaddr); //shmaddr是shmat()函式的返回值
(4)控制共享記憶體
* int shmctl(int shmid,int cmd,struct shmid_ds *buf);
* struct shmid_ds{
struct ipc_perm shm_perm;
…
};
cmd的常用取值有:(a)IPC_STAT獲取當前共享記憶體的shmid_ds結構並儲存在buf中(2)IPC_SET使用buf中的值設定當前共享記憶體的shmid_ds結構(3)IPC_RMID刪除當前共享記憶體
3、訊息佇列
訊息佇列儲存在核心中,是一個由訊息組成的連結串列。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
(1)建立或訪問訊息佇列
* int msgget(key_t key,int msgflg);
(2)操作訊息佇列
* int msgsnd(int msqid,const void *msg,size_t nbytes,int msgflg);
msg指向的結構體必須以一個long int成員開頭,作為msgrcv()的訊息型別,必須大於0。nbytes指的是msg指向結構體的大小,但不包括long int部分的大小
* ssize_t msgrcv(int msqid,void *msg,size_t nbytes,long msgtype,int msgflg);
如果msgtype是0,就返回訊息佇列中的第一個訊息;如果是正整數,就返回佇列中的第一個該型別的訊息;如果是負數,就返回佇列中具有最小值的第一個訊息,並且該最小值要小於等於msgtype的絕對值。
(3)控制訊息佇列
* int msgctl(int msqid,int cmd,struct msqid_ds *buf);
* struct msqid_ds{
struct ipc_perm msg_perm;
…
};
Socket
套接字(Socket)是由Berkeley在BSD系統中引入的一種基於連線的IPC,是對網路介面(硬體)和網路協議(軟體)的抽象。它既解決了無名管道只能在相關程式間單向通訊的問題,又解決了網路上不同主機之間無法通訊的問題。
套接字有三個屬性:域(domain)、型別(type)和協議(protocol),對應於不同的域,套接字還有一個地址(address)來作為它的名字。
域(domain)指定了套接字通訊所用到的協議族,最常用的域是AF_INET,代表網路套接字,底層協議是IP協議。對於網路套接字,由於伺服器端有可能會提供多種服務,客戶端需要使用IP埠號來指定特定的服務。AF_UNIX代表本地套接字,使用Unix/Linux檔案系統實現。
IP協議提供了兩種通訊手段:流(streams)和資料包(datagrams),對應的套接字型別(type)分別為流式套接字和資料包套接字。流式套接字(SOCK_STREAM)用於提供面向連線、可靠的資料傳輸服務。該服務保證資料能夠實現無差錯、無重複傳送,並按順序接收。流式套接字使用TCP協議。資料包套接字(SOCK_DGRAM)提供了一種無連線的服務。該服務並不能保證資料傳輸的可靠性,資料有可能在傳輸過程中丟失或出現資料重複,且無法保證順序地接收到資料。資料包套接字使用UDP協議。
一種型別的套接字可能可以使用多於一種的協議來實現,套接字的協議(protocol)屬性用於指定一種特定的協議。
總結:
System V IPC API
1,訊息佇列
int ftok(const char *pathname, int prj_id);
int msgget(key_t key,int msgflag);
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
int msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
2,訊號量
int semget(key_t key,int nsems,int semflag);
int semctl(int semid,int semnum,int cmd,…);
int semop(int semid,struct sembuf *sops,unsigned nsops,struct timespec *timeout);
3,共享記憶體
int shmget(key_t key,size_t size,int shmflag);
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
POSIX IPC API
待補上
相關文章
- linux程式間通訊(IPC)小結Linux
- linux ipc namespace筆記Linuxnamespace筆記
- linux總結Linux
- linux命令總結Linux
- Linux命令大全總結Linux
- Linux命令總結大全Linux
- Linux shell命令總結Linux
- Linux實踐總結Linux
- 排序演算法全總結排序演算法
- 【Linux】萬字總結Linux常用指令Linux
- Linux裸裝置總結Linux
- Linux命令總結--mkdir命令Linux
- Linux 系統管理總結Linux
- Linux 命令總結之 topLinux
- 檔案上傳漏洞總結(全)
- Linux常用命令總結Linux
- linux 新手必會命令總結Linux
- Linux文字三劍客總結Linux
- 適合前端的HTTP全流程總結前端HTTP
- RestTemplate全網最強總結(永久更新)REST
- Linux磁碟管理基礎知識全彙總Linux
- Linux 常見的六大 IPC 通訊方式Linux
- linux檔案目錄結構彙總!Linux學習Linux
- IPC call
- linux總結及常用命令Linux
- 7、Linux 埠轉發特徵總結Linux特徵
- Linux爬坑之路(學習總結)Linux
- Linux_Centos_yum報錯總結LinuxCentOS
- Linux firewalld防火牆學習總結Linux防火牆
- 600 條最強 Linux 命令總結Linux
- Form 表單提交知識的總結(全)ORM
- Spring Boot 整合 Shiro ,兩種方式全總結!Spring Boot
- 附答案!超全SpringBoot面試題總結Spring Boot面試題
- RabbitMQ由淺入深入門全總結(一)MQ
- 回溯法解決全排列問題總結
- RabbitMQ由淺入深入門全總結(二)MQ
- 【Linux】關於Linux的系統程式設計總結Linux程式設計
- Linux學習之linux檔案目錄結構彙總Linux
- Go 中空結構體的用法,我幫你總結全了!Go結構體