Linux下的IPC機制
IPC(Inter-Process Communication)是多個進程之間相互溝通的一種方法。在linux下有多種進程間通信的方法。
共享內存
Linux內存共享有多種,如mmap()、Posix共享內存、System V 共享內存。
1>mmp()通過映射一個普通文件實現共享內存,具有文件實體,shmget()對應文件在內存中,無文件實體。
2>mmp()不建議使用疊加方式共享,shmget()用於多個進程間交換數據。
3>mmp() shmget() 進程重啟后共享內存中的數據都不會丟失;但是機器重啟后只有mmp()方式的共享內存可以保存數據
4>mmap()接口更簡單,通用性也更高。
這里首先先使用shmget建立一塊共享內存,然后向該內存中寫入數據並返回該共享內存shmid。使用另一個程序通過上一程序返回的shmid讀該共享內存內的數據
建立共享內存並寫入數據的程序:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
void get_buf(char *buf)
{
int i=0;
while((buf[i]=getchar())!='\n'&&i<1024)
i++;
}
int main(void)
{
int shmid;
shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,IPC_CREAT|0666);
if(shmid==-1)
{
perror("shmget");
}
char *buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
get_buf(buf);
printf("%d\n",shmid);
return 0;
}
讀取數據的程序
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int shmid;
shmid=atoi(argv[1]);
char *buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
printf("%s\n",buf);
shmdt(buf);
return 0;
}
管道
管道是由內核管理的一個緩沖區,創建管道的進程稱為管道服務器,連接到一個管道的進程為管道客戶機。一個進程在向管道寫入數據后,另一進程就可以從管道的另一端將其讀取出來。
管道的特點:
1、管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;
2、一般只能用於父子進程之間。
3、單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,並且只存在與內存中。
4、數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,並且每次都是從緩沖區的頭部讀出數據。
管道程序實例(創建一個從父進程到子進程的管道,並且父進程經由該管道向子進程傳送數據):
#include <unistd.h>
#include <stdio.h>
#define MAXLINE 1024
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)
printf("pipe error\n");
if ((pid = fork()) < 0) {
printf("fork error\n");
} else if (pid > 0) { /* parent */
close(fd[0]);
write(fd[1], "hello world\n", 12); /* write data to fd[1] */
} else { /* child */
close(fd[1]);
n = read(fd[0], line, MAXLINE); /* read data from fd[0] */
write(STDOUT_FILENO, line, n); /* write data to standard output */
}
return (0);
}
FIFO
FIFO有時被稱為命名管道,未命名的管道只能在兩個相關的進程之間使用,而且這兩個相關的進程還要有一個共同的祖先進程。但是,通過FIFO,不相關的進程之間也能交換數據。
FIFO的用途:用於客戶進程--服務器進程應用程序中
FIFO的真正優勢在於:服務器可以是一個長期運行的進程(例如守護進程),而且與其客戶可以無親緣關系。
實例:需要對一個輸入文件進行兩次處理
可以使用FIFO和tee命令如下處理:
mkfifo fifo1
prog3 < fifo1 &
prog1 < (輸入文件) | tee fifo1 | prog2
執行流程如下:
信號
信號用於一個或幾個進程之間傳遞異步信號。信號可以有各種異步事件產生,比如鍵盤中斷等。shell也可以使用信號將作業控制命令傳遞給它的子進程。
實例(向進程本身發送信號,並傳遞指針參數,信號實現了附加信息的傳遞):
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
union sigval mysigval;
int i;
int sig;
pid_t pid;
char data[10];
memset(data,0,sizeof(data));
for(i=0;i < 5;i++)
data[i]='2';
mysigval.sival_ptr=data;
sig=atoi(argv[1]);
pid=getpid();
sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;//三參數信號處理函數
act.sa_flags=SA_SIGINFO;//信息傳遞開關
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
sigqueue(pid,sig,mysigval);//向本進程發送信號,並傳遞附加信息
}
}
void new_op(int signum,siginfo_t *info,void *myact)//三參數信號處理函數的實現
{
int i;
for(i=0;i<10;i++)
{
printf("%c\n ",(*( (char*)((*info).si_ptr)+i)));
}
printf("handle signal %d over;",signum);
}
消息隊列
消息隊列是內核地址空間中的內部鏈表,通過linux內核在各個進程直接傳遞內容,消息順序地發送到消息隊列中,並以幾種不同的方式從隊列中獲得,每個消息隊列可以用IPC標識符唯一地進行識別。內核中的消息隊列是通過IPC的標識符來區別,不同的消息隊列直接是相互獨立的。每個消息隊列中的消息,又構成一個獨立的鏈表。
消息隊列克服了信號承載信息量少,管道只能承載無格式字符流。
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。每個數據塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。我們可以通過發送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制。
參考資料:
Linux 進程間通信(一)(經典IPC:管道、FIFO)
linux基礎——linux進程間通信(IPC)機制總結
linux下IPC機制之“內存共享”
Linux下內存共享的一個實例(設置共享內存,一個程序寫,一個程序讀)