Linux下的進程間通信機制:大致包括:
管道
信號(在Windows上成爲消息)
消息隊列
共享內存
信號量
套接字(socket)
一、基本概念
- 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間的通信;
- 信號(Signal):信號是比較複雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
- 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺點。
- 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
- 信號量(semaphore):主要作爲進程間以及同一進程不同線程之間的同步手段。
- 套接口(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
二、管道實例:
類似於水管,在兩個進程之間用來傳遞數據。管道是半雙工的,而且只能在共同父進程的進程之間使用。
int pipe ( int filedes[2] );
filedes返回兩個文件描述符,filedes[0]爲讀端,filedes[1]爲寫端。創建成功返回 0
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int fd[2];
pid_t pid;
char buf[64] = "i'm parent process!!\n"; //父進程要寫入管道的內容
char line[64];
if (0 != pipe(fd)) //創建管道
{
fprintf(stderr,"Fail to create pipe!");
return 0;
}
pid = fork();
if (pid < 0)
{
printf("fork failed!");
return 0 ;
}
else if(0 == pid)
{
close(fd[0]); //關閉讀管道,使父進程只能寫
write(fd[1],buf,strlen(buf));
close(fd[1]); //關閉寫管道
}
else
{
close(fd[1]); //關閉寫管道
read(fd[0],line,64); //只能讀
printf("DATA From Parents:%s",line);
close(fd[0]);
}
return 0;
}
三、共享內存
共享內存:進程需要可以被其他進程瀏覽的內存塊。希望訪問這個內存塊的其他進程請求對它的訪問,或由創建它的進程授予訪問內存塊的權限。可以訪問特定內存塊的所有進程對它具有即時可見性。
共享內存被映射到使用它的每個進程的地址空間。所以,它看起來像是另一個在進程內聲明的變量。當一個進程寫共享內存,所有的進程都立即知道寫入的內容,而且可以訪問。進程間共享內存的關係與函數間全局變量的關係相似。
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
ftok():系統建立IPC通訊 (消息隊列、信號量和共享內存) 時必須指定一個ID值。通常情況下,該id值通過ftok函數得到
當然,也可以直接使用內核的:PC_PRIVATE
IPC_PRIVATE和ftok的比較
函數解析:
int shmget(key_t key, size_t size, int shmflg);
shmget 用來創建共享內存(成功創建,返回一個共享ID):
- key是由ftok()函數生成的唯一關鍵字,用來標識該段內存,
- size參數指定需要的內存字節數
- shmflg:內存操作方式,有讀/寫兩種
把共享內存區對象映射到調用進程的地址空間
void *shmat(int shmid, const void *shmaddr, int shmflg);
- shmid:共享內存ID
- shmaddr:指定共享內存出現在進程內存地址的什麼位置,直接指定爲NULL讓內核自己決定一個合適的地址位置
- shmflg : SHM_RDONLY:爲只讀模式,其他爲讀寫模式
從程序中分離一塊共享內存:
int shmdt(const void *shmaddr);
創建共享內存並向其中寫入數據:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main()
{
int shmid;
char *ptr;
char *shm_str = "string in 0X90";
shmid = shmget(0x90,1024,SHM_W|IPC_CREAT|SHM_R|IPC_EXCL); //創建共享內存(我們已經指定了0x99這段內存,所以無需調用ftok()函數獲得標誌)
if (-1 == shmid)
{
perror("create share memory failed!");
}
ptr = (char *)shmat(shmid,0,0); //通過共享內存ID獲取共享地址內存
if ((void *) -1 == ptr)
perror("get share memory");
strcpy(ptr,shm_str);
shmdt(ptr);
return 0;
}
讀取數據:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int shmid;
char *ptr;
shmid = shmget(0x90,1024,SHM_W|SHM_R|IPC_EXCL); //創建共享內存
if (-1 == shmid)
{
perror("create share memory!");
}
ptr = (char *)shmat(shmid,0,0); //通過共享內存ID獲取共享地址內存
if ((void*) -1 == ptr)
perror("get share memory failed");
printf("string int share memeory : %s\n",ptr);
shmdt(ptr);
return 0;
}
首先得先執行write , 因爲shmget這個函數創建共享內存區域。IPC_CREAT這個參數是代表不存在則創建,所以第二次執行的話需要去掉這個參數。
ipcs:查看當前共享資源。
當然也可以釋放指定的共享內存:
ipcrm -m 0
這裏0是shimd的值
四、socket:
參考:服務器與多個用戶間的通信