Linux 常見的六大IPC 通信方式
Linux環(huán)境下,進(jìn)程地址空間相互獨(dú)立,每個(gè)進(jìn)程各自有不同的用戶地址空間。任何一個(gè)進(jìn)程的全局變量在另一個(gè)進(jìn)程中都看不到,所以進(jìn)程和進(jìn)程之間不能相互訪問,要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進(jìn)程1把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進(jìn)程2再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信(IPC,InterProcess Communication)。今天我們來看一下,Linux下常見的六大IPC通信方式。
1、信號
信號是Unix/Linux系統(tǒng)在一定條件下生成的事件。信號是一種異步通信機(jī)制,進(jìn)程不需要執(zhí)行任何操作來等待信號的到達(dá)。信號異步通知接收信號的進(jìn)程發(fā)生了某個(gè)事件,然后操作系統(tǒng)將會中斷接收到信號的進(jìn)程的執(zhí)行,轉(zhuǎn)而去執(zhí)行相應(yīng)的信號處理程序。
(1)注冊信號處理函數(shù)
#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)發(fā)送信號
#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秒后,向進(jìn)程本身發(fā)送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)
管道用來連接不同進(jìn)程之間的數(shù)據(jù)流。
(1)在兩個(gè)程序之間傳遞數(shù)據(jù)的最簡單的方法是使用popen()和pclose()函數(shù):
#include <stdio.h>
FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *stream);
popen()函數(shù)首先調(diào)用一個(gè)shell,然后把
command作為參數(shù)傳遞給shell。這樣每次調(diào)用popen()函數(shù)都需要啟動(dòng)兩個(gè)進(jìn)程;但是由于在Linux中,所有的參數(shù)擴(kuò)展(parameter expansion)都是由shell執(zhí)行的,這樣
command中包含的所有參數(shù)擴(kuò)展都可以在
command程序啟動(dòng)之前完成。
(2)pipe()函數(shù):
int pipe(int pipefd[2]);
popen()函數(shù)只能返回一個(gè)管道描述符,并且返回的是文件流(file stream),可以使用函數(shù)fread()和fwrite()來訪問。pipe()函數(shù)可以返回兩個(gè)管道描述符:
pipefd[0]和
pipefd[1],任何寫入
pipefd[1]的數(shù)據(jù)都可以從
pipefd[0]讀回;pipe()函數(shù)返回的是文件描述符(file descriptor),因此只能使用底層的read()和write()系統(tǒng)調(diào)用來訪問。pipe()函數(shù)通常用來實(shí)現(xiàn)父子進(jìn)程之間的通信。
(3)命名管道:FIFO
int mkfifo(const char *fifo_name, mode_t mode);
前面兩種管道只能用在相關(guān)的程序之間,使用命名管道可以解決這個(gè)問題。在使用open()打開FIFO時(shí),mode中不能包含O_RDWR。mode最常用的是O_RDONLY,O_WRONLY與O_NONBLOCK的組合。O_NONBLOCK影響了read()和write()在FIFO上的執(zhí)行方式。
3、信號量(Semaphores)
System V的信號量集表示的是一個(gè)或多個(gè)信號量的集合。內(nèi)核為每個(gè)信號量集維護(hù)一個(gè)semid_ds數(shù)據(jù)結(jié)構(gòu),而信號量集中的每個(gè)信號量使用一個(gè)無名結(jié)構(gòu)體表示,這個(gè)結(jié)構(gòu)體至少包含以下成員:
struct{
unsigned short semval;//信號量值,總是>=0
pid_t sempid; //上一次操作的pid
…
};
(1)創(chuàng)建或訪問信號量
int semget(key_t key,int nsems,int flag);
nsems指定信號量集中信號量的個(gè)數(shù),如果只是獲取信號量集的標(biāo)識符(而非新建),那么nsems可以為0。flag的低9位作為信號量的訪問權(quán)限位,類似于文件的訪問權(quán)限;如果flag中同時(shí)指定了IPC_CREAT和IPC_EXCL,那么如果key已與現(xiàn)存IPC對象想關(guān)聯(lián)的話,函數(shù)將會返回EEXIST錯(cuò)誤。例如,flag可以為IPC_CREAT|0666。
(2)控制信號量集
int semctl(int semid,int semnum,int cmd,union semun arg);
對semid信號量集合執(zhí)行cmd操作;cmd常用的兩個(gè)值是:SETVAL初始化第semnum個(gè)信號量的值為arg.val;IPC_RMID刪除信號量。
(3)對一個(gè)或多個(gè)信號量進(jìn)行操作
int semop(int semid,struct sembuf *sops,unsigned nsops);
struct sembuf{
unsigned short sem_num; //信號量索引
short sem_op; //對信號量進(jìn)行的操作,常用的兩個(gè)值為-1和+1,分別代表P、V操作
short sem_flag; //比較重要的值是SEM_UNDO:當(dāng)進(jìn)程結(jié)束時(shí),相應(yīng)的操作將被取消;同時(shí),如果進(jìn)程結(jié)束時(shí)沒有釋放資源的話,系統(tǒng)會自動(dòng)釋放
};
4、共享內(nèi)存
共享內(nèi)存允許兩個(gè)或多個(gè)進(jìn)程共享一定的存儲區(qū),因?yàn)椴恍枰截悢?shù)據(jù),所以這是最快的一種IPC。
(1)創(chuàng)建或訪問共享內(nèi)存
int shmget(key_t key,size_t size,int shmflg);
(2)附加共享內(nèi)存到進(jìn)程的地址空間
void *shmat(int shmid,const void *shmaddr,int shmflg);//shmaddr通常為NULL,由系統(tǒng)選擇共享內(nèi)存附加的地址;shmflg可以為SHM_RDONLY
(3)從進(jìn)程的地址空間分離共享內(nèi)存
* int shmdt(const void *shmaddr); //shmaddr是shmat()函數(shù)的返回值
(4)控制共享內(nèi)存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
struct shmid_ds{
struct ipc_perm shm_perm;
…
};
cmd的常用取值有:(a)IPC_STAT獲取當(dāng)前共享內(nèi)存的shmid_ds結(jié)構(gòu)并保存在buf中(2)IPC_SET使用buf中的值設(shè)置當(dāng)前共享內(nèi)存的shmid_ds結(jié)構(gòu)(3)IPC_RMID刪除當(dāng)前共享內(nèi)存
本文版權(quán)歸傳智播客C++培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!
作者:傳智播客C/C++培訓(xùn)學(xué)院
首發(fā):http://m.8y3kgpwe.cn/c/