進程間通信(IPC,Inter-Process Communication),指至少兩個進程或線程間傳送數據或信號的一些技術或方法。進程是計算機系統分配資源的最小單位(嚴格說來是線程)。每個進程都有自己的一部分獨立的系統資源,彼此是隔離的。爲了能使不同的進程互相訪問資源並進行協調工作,纔有了進程間通信。舉一個典型的例子,使用進程間通信的兩個應用可以被分類爲客戶端和服務器,客戶端進程請求數據,服務端回覆客戶端的數據請求。有一些應用本身既是服務器又是客戶端,這在分佈式計算中,時常可以見到。這些進程可以運行在同一計算機上或網絡連接的不同計算機上。
進程間通信技術包括消息傳遞、同步、共享內存和遠程過程調用。IPC是一種標準的Unix通信機制。
使用IPC 的理由:
1.信息共享:Web服務器,通過網頁瀏覽器使用進程間通信來共享web文件(網頁等)和多媒體;
2.加速:維基百科使用通過進程間通信進行交流的多服務器來滿足用戶的請求;
3.模塊化;
4.私有權分離。
與直接共享內存地址空間的多線程編程相比,IPC的缺點:
1.採用了某種形式的內核開銷,降低了性能;
2.幾乎大部分IPC都不是程序設計的自然擴展,往往會大大地增加程序的複雜度。
Linux常用的進程間的通訊方式:
- 管道(pipe):管道可用於具有親緣關係的進程間的通信,是一種半雙工的方式,數據只能單向流動,允許一個進程和另一個與它有共同祖先的進程之間進行通信。
- 命名管道(named pipe):命名管道克服了管道沒有名字的限制,同時除了具有管道的功能外(也是半雙工),它還允許無親緣關係進程間的通信。命名管道在文件系統中有對應的文件名。命名管道通過命令mkfifo或系統調用mkfifo來創建。
- 信號量(Semaphore):信號量本質上是一個計數器(不設置全局變量是因爲進程間是相互獨立的,而這不一定能看到,看到也不能保證++引用計數爲原子操作),用於多進程對共享數據對象的讀取,它和管道有所不同,它不以傳送數據爲主要目的,它主要是用來保護共享資源(信號量也屬於臨界資源),使得資源在一個時刻只有一個進程獨享。
- 消息隊列:消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺
- 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
- 套接字(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
管道(pipe)示例:
#include <iostream>
#include <unistd.h>
#include <stdio.h>
using namespace std;
int main()
{
int f_pipe[2];
//創建pipe
if(pipe(f_pipe)<0){
cout<<"pipe error"<<endl;
exit(-1);
}
pid_t pid = fork();
if(pid <0 ){
cout<<"fork error"<<endl;
close(f_pipe[0]);
close(f_pipe[1]);
exit(-1);
}else if(pid ==0 ){
//child process
int a = 0;
const int BUF = 20;
char buf[BUF] = "";
//子進程負責寫 , 所以關閉 f_pipe[0] 讀端
close(f_pipe[0]);
while(a < 10){
fgets(buf,BUF,stdin);
if(write(f_pipe[1],buf,sizeof(buf)) < 0){
cout<<"fork error"<<endl;
exit(-1);
}
cout<<"child send : "<<buf<<endl;
a++;
}
}else {
//father process
int b = 0;
const int FBUF = 20;
char fbuf[FBUF] = "";
//父進程負責讀 , 所以關閉 f_pipe[1] 寫端
close(f_pipe[1]);
while(b < 10){
read(f_pipe[0],fbuf,sizeof(fbuf));
cout<<"father recv: "<<fbuf<<endl;
b++;
}
}
return 0;
}
詳情傳送門:https://blog.csdn.net/skyroben/article/details/71513385
有名管道(FIFO)示例:
有名管道只能以讀寫方式打開,FIFO文件只是一個臨時存儲空間,文件大小爲0.
創建FIFO文件:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
if(access("./testfifo1",F_OK) == -1){
if (mkfifo("testfifo1",777) < 0) //創建一個有名管道 ,並且給他賦予所有權限
{
perror("mkfifo:");
exit(-1);
}
}
printf ("mkfifo testfifo1 ok \n");
return 0;
}
寫程序:
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> //open
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main()
{
int ret;
int a = 0;
char buf[20] = "";
const char* path = "./testfifo1";
const int pre = O_WRONLY;
ret = open(path,pre);
if(ret == -1){
perror("open error");
exit(-1);
}
while(a < 10){
fgets(buf,20,stdin);
write(ret,buf,sizeof(buf));
printf("write : %s\n",buf);
}
return 0;
}
讀程序:
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> //open
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(){
int ret,fd;
char buf[20] = "";
const char* path = "./testfifo1";
const int pre = O_RDONLY;
fd = open(path,pre);
if(ret == -1){
perror("open error");
exit(-1);
}
do{
ret = read(fd,buf,sizeof(buf));
printf("read : %s\n",buf);
}while(ret > 0);
return 0;
}
信號量
信號量機制:是一種功能較強的機制,可以用來解決互斥與同步問題,它只能被兩個標準的原語
wait(S) 和 signal(S) 來訪問,也可以記爲“P操作”和“V操作”。
詳情請參考:https://blog.csdn.net/skyroben/article/details/72513985
待續...