一。基本概念
什麼是進程間通信:就是多個進程在運行時傳遞數據
爲什麼需要進程間通信:進程是一個獨立的資源單位,基本可以自給自足(能解決一個獨立的問題)。但很多問題卻不是獨立的,所以進程也需要相互合作,這時就需要進程間通信了。
進程間的通信方式:
傳統:管道
簡單:命令行參數,環境變量、信號、文件
xsi:消息隊列,共享內存、信號量
網絡:
二、傳統的進程間通信-管道
管道是UNIX系統最古老的進程間通信方式(基本不再使用),歷史上的管道通常是半雙工(只允許單向數據流動),現在的系統大都可以全雙工,數據可以雙向流動。
1、有名管道(創建實體文件)
命令:mkfifo
函數:int mkfifo(const char *pathname, mode_t mode);
功能:創建管道文件
pathname:文件路徑
mode:權限
返回值:成功返回0,失敗返回-1。
編程模型:
進程A 進程B
創建管道(mkfifo) ...
打開管道(open) 打開管道
讀/寫數據 (read/write) 讀/寫數據
關閉管道(close) 關閉管道
刪除管道(unlink) ...
2、無名管道(用於通過fork創建的父子進程之間通信)
int pipe(int pipefd[2]);
功能:創建無名管道
pipefd:用來存儲內核返回的文件描述符
pipefd[0] 用於讀操作
pipefd[1] 用於寫操作
編程模型同上
三、XSI進程間通信
X/open組織爲UNIX系統設計一套進程間通信機制,有共享內存、消息隊列、信號量。
1、IPC標識
內核會爲每個進程間通信維護一個IPC對象(XSI對象)。
該對象通過一個非負整數來引用(類似於文件描述符)。
與文件描述符不同的是,每用一個IPC對象標識符就持續+1,達到最大值時再從零開始。
IPC標識需要程序員自己創建(類似於創建文件)。
2、IPC鍵值
創建IPC鍵值的依據(類似創建文件的文件名),也是一個非負整數。
1、自定義(不建議,可能會衝突)。
2、自動生成(項目路徑,項目編號)。
key_t ftok(const char *pathname, int proj_id);
注意:項目路徑一定要是有效路徑,生成IPC鍵依靠的是路徑而不是字符串。
3、IPC對象的創建用到的宏
IPC_PRIVATE 創建IPC對象時永遠創建成功。
IPC_CREAT 對象存在則獲取,不存在則創建。
IPC_EXCL 如果對象已經創建,則創建失敗。
4、IPC對象銷燬/控制用到的宏
IPC_STAT 獲取IPC對象的屬性
IPC_SET 設置IPC對象的屬性
IPC_RMID 刪除IPC對象
四、共享內存
共享內存就是內核中開闢一塊由IPC對象管理內存,進程A和進程B都用自己的虛擬地址與它進程映射,這樣他就共享了同一塊內存,然後就可以通信了。
特點:
1、不需要複製信息,是最快的一種進程間通信機制。
2、需要考慮同步問題(必須藉助其它的機制,如信號)。
編程模型:
進程A 進程B
生成IPC鍵值 ftok 生成IPC鍵 ftok
創建共享內存 shmget 獲取共享內存 shmget
映射共享內存 shmat 映射共享內存 shmat
使用共享內存 *ptr 使用共享內存 *ptr
取消映射 shmdt 取消映射 shmdt
刪除共享內存 shmctl …
int shmget(key_t key, size_t size, int shmflg)
功能:創建/獲取共享內存
key:IPC鍵,由ftok函數生成
size:共享內存的大小,最好是4096的整數倍,獲取共享內存時,此值無效。
shmflg:
0 獲取共享內存
IPC_CREAT 創建
IPC_EXCL 如果存在則創建失敗
返回值:成功返回共享內存標識(IPC標識),失敗返回-1。
void* shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享內存
shmid:共享內存標識符,shmget函數的返回值。
shmaddr:進程提供的虛擬地址,與內核中的內存映射用的,也可以是NULL(內核會自動選擇一個地址映射)。
shmflg:
0 自動分配
SHM_RDONLY 只讀權限
SHM_RND 當shmaddr不爲空時shmaddr向下取整頁。
返回值:映射成功後的虛擬地址。
int shmdt(const void *shmaddr);
功能:取消虛擬地址與共享內存的映射
shmaddr:被映射過的虛擬地址
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
功能:刪除共享內存,獲取/設置共享內存的屬性
shmid:共享內存標識符shmget的返回值
cmd:
IPC_STAT 獲取共享內存的屬性
IPC_SET 設置共享內存的的屬性
IPC_RMID 刪除IPC共享內存
struct shmid_ds {
struct ipc_perm shm_perm; // 內存所有者及權限
size_t shm_segsz; // 內存的大小
time_t shm_atime; // 最後的映射時間
time_t shm_dtime; // 最後的取消映射時間
time_t shm_ctime; // 最後修改時間
pid_t shm_cpid; // 創建者進程ID
pid_t shm_lpid; // 最後映射/取消映射的進程ID
shmatt_t shm_nattch; // 映射的次數
struct ipc_perm {
key_t __key; // IPC鍵值
uid_t uid; // 有效用戶ID
gid_t gid; // 有效組ID
uid_t cuid; // 創建者的用戶ID
gid_t cgid; // 創建者組ID
unsigned short mode; // 權限
unsigned short __seq; // IPC標識
五、消息隊列
消息隊列就是由內核負責管理的一個管道,可以按順序發送消息包(消息類型+消息內容),可以全雙工工作,可以不按消息的順序接收消息。
int msgget(key_t key, int msgflg);
功能:創建/獲取消息隊列
key:IPC鍵值,由ftok函數自動生成
msgflg:
0 獲取消息隊列
IPC_CREAT 創建消息隊列
IPC_EXCL 如果存在則創建失敗
返回值:消息隊列標識
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息隊列發送消息
msqid:消息隊列標識,msgget函數的返回值
msgp:結構指針
struct msgbuf {
long mtype; //消息類型
char mtext[n]; //消息內容
};
msgsz:消息的長度,不包括消息類型,sizeof(msgbuf)-4。
msgflg:
0 阻塞,當消息隊列滿時,等待。
IPC_NOWAIT 不阻塞,當消息隊列滿時,不等待。
返回值:成功發送返回0,失敗返回-1。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:從消息隊列中按類型獲取消息
msqid:消息隊列標識,msgget函數的返回值
msgp:
struct msgbuf {
long mtype; //消息類型
char mtext[n]; //消息內容
};
msgsz:要接收一消息的長度,可以長一些。
msgtyp:要接收的消息類型
0 接收任意類型的消息(接收隊列中第一個消息)。
>0 只接收msgtyp類型的消息
<0 接收消息隊列中小於等於msgtyp絕對值的消息,取小的那個。
msgflg:
0 阻塞,消息隊列中是否有對應類型的消息,沒有則等待。
1 不阻塞,消息隊列中沒有對應類型的消息,則返回。
----------------------------------------
MSG_NOERROR:
消息類型正確,而消息的實際長度大於msgsz,則不接收消息並返回-1。
如果msgflg帶MSG_NOERROR標誌,則把多餘的消息截取,成功接收。
IPC_NOWAIT:如果消息隊列沒有要接收的消息,則不等待,返回-1。
MSG_EXCEPT:接收消息隊列中第一個消息類型不是msgtyp的消息,編譯時添加-D_GNU_SOURCE參數。
返回值:成功接收到消息的字節數
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:刪除消息隊列,設置或獲取消息隊列屬性
msqid:消息隊列標識,msgget函數的返回值
cmd:
IPC_STAT 獲取消息隊列的屬性
IPC_SET 設置消息隊列的的屬性
IPC_RMID 刪除消息隊列
返回值:成功返回0,失敗返回-1。
struct msqid_ds {
struct ipc_perm msg_perm; // 權限
time_t msg_stime; // 最後一個消息發送時間
time_t msg_rtime; // 最後一次消息接收時間
time_t msg_ctime; // 最後一次修改時間
unsigned long __msg_cbytes; // 消息隊列中的字節數
msgqnum_t msg_qnum; // 消息隊列中消息的個數
msglen_t msg_qbytes; // 消息隊列中容納的最大字節數
pid_t msg_lspid; // 最後一次發送消息進程
pid_t msg_lrpid; // 最後一次接收消息進程
六、信號量
內核維護的計數量,用於管理多進程之間共享資源。
例如:有個變量n表示資源的數量,當有進程想要獨佔一個資源時,n的值要減1(可能減多個),如果n的值等於0(不夠減),則進程阻塞,直到n的值可以減再被喚醒,當資源使用完畢後n的要加1(可能加多個)。
int semget(key_t key, int nsems, int semflg);
功能:創建/獲取信號量
key:IPC鍵值
nsems:信號量的數量
semflg:
0 獲取信號量
IPC_CREAT 創建信號量(己存在則獲取,不存在則創建)。
IPC_EXCL 如果已經存在則創建失敗。
返回值:信號量標識
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:操作信號量(對信號進行加/減操作)
semid:信號量標識,semget的返回值
sops:結構體數組
nsops:數組的長度
struct sembuf{
unsigned short sem_num; 信號量的下標
short sem_op; 操作
short sem_flg; 標記
IPC_NOWAIT 當信號量不夠減時,不阻塞。
SEM_UNDO 當進程結束時,信號量的值自動歸還。
int semtimedop(int semid, struct sembuf *sops, unsigned nsops,struct timespec *timeout);
功能:帶時間限制的操作信號量
struct timespec
__time_t tv_sec; 秒
long int tv_nsec; 納秒 1000000000
int semctl(int semid, int semnum, int cmd, ...);
功能:初始化信號量,刪除信號量,獲取、設置信號量的屬性。
cmd:
GETALL 獲取所有信號量的值
GETNCNT 獲取信號量的數量
GETVAL 獲取某個信號量的值
SETALL 設置所有信號量的值
SETVAL 設置某個信號量的值
IPC_RMID 刪除信號量
IPC_STAT 獲取信號量的屬性
IPC_SET 設置信號量的屬性
// 獲取、設置信號量屬性
struct ipc_perm {
key_t __key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
unsigned short mode;
unsigned short __seq;
IPC_INFO 獲取信號量的信息
SEM_INFO 設置信號量的信息
struct seminfo
編程模型:
進程A 進程B
創建信號量 semget 獲取信號量
初始化信號量的值 semctl …
加減信號量 semop 加減信號量
刪除信號量 semctl …
注意:信號量是用來計數的,一定要與資源對應。