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