通訊方式詳解,無名管道pipe,有名管道fifo,共享記憶體share memory,訊息佇列msg
常用的程序通訊方式有:
傳統的程序通訊方式:無名管道(pipe),有名管道(fifo),訊號(signal)
system V IPC物件:共享記憶體(share memeory),訊息佇列(message queue),訊號燈(semaphore)
BSD:套接字(socket)
pipe:pipe只能用於具有親緣關係的程序之間通訊
半雙工通訊模式,具有固定的讀端和寫端
管道可以看作是一種特殊的檔案,對於它的讀寫可以使用檔案IO如read,write函式。
當管道無資料時,讀操作阻塞
寫資料時,linux不保證寫入原子性,管道緩衝區一有空閒區域,寫程序就會試圖向管道緩衝區寫資料,如果讀操作不讀取緩衝區資料,那麼寫操作將會一直阻塞。
讀端存在,寫資料才有意義,否則管道中寫入資料會收到核心傳來的SIFPIPE訊號
fifo:#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> int main() { int n,fd[2]; pid_t pid; char line[100]; if(pipe(fd)<0)//pipe產生兩個檔案描述符,fd[0]讀取,fd[1]寫 { perror("pipe"); exit(1); } if((pid=fork())<0)//fork產生父子程序 { perror("fork"); exit(1); } if(pid>0)//父程序寫,關閉讀 { close(fd[0]); write(fd[1],"hello world\n",12); } else//子程序讀,關閉寫 { close(fd[1]); n=read(fd[0],line,100); write(STDOUT_FILENO,line,n);//列印螢幕 } exit(0); }
無名管道可以使兩個不相干的程序通訊,使用路徑來指出,在檔案系統中可見
使用I/O操作
遵循先進先出規則
不支援Iseek()操作
! 管道一個以讀方式開啟,一個以寫方式開啟,否則,進入阻塞
1:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> int main(int argc,char **argv) { int fd; char buf[20]="hello world!\n"; if((mkfifo("my_fifo",O_CREAT|O_RDWR|0666))<0)//建立有名管道 { perror("mkfifo"); exit(1); } if((fd=open("my_fifo",O_WRONLY))<0)//寫方式開啟 { perror("open"); exit(1); } if((write(fd,buf,strlen(buf)-1))<0)//寫入內容 { perror("write"); exit(1); } close(fd); return 0; }
2:
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char **argv)
{
int fd;
char buf[20]="";
if((fd=open("my_fifo",O_RDONLY))<0)//讀方式開啟
{
perror("oepn");
exit(1);
}
if((read(fd,buf,20))<0)//讀取內容到buf
{
perror("read");
exit(1);
}
printf("%s",buf);
close(fd);
return 0;
}
shm共享記憶體:
共享記憶體是最為高效的程序通訊方式,程序可以之間讀取資料,不需要任何資料的拷貝
為了在多個程序間交換資訊,核心專門流出一塊記憶體區,可以由需要訪問的程序將其對映到自己的私有地址空間
由於多個程序共享記憶體,所以需要依靠眸中同步機制,如互斥鎖和訊號量
步驟:---:建立/開啟共享記憶體:int shmget(key_t key, int size, int shmflg ) key:tfok返回值,size:共享記憶體大小,
----:對映共享記憶體: void *shmat(int shmid ,const void *shmaddr, int shmflg);//shmadr為NULL,自動完成對映,shmflg為0,共享記憶體可讀寫,SHM_RDONLY:只讀
----:撤銷共享記憶體: int shmdt(const void *shmaddr)//shmaddr:共享對映後的地址
-—:刪除共享記憶體: shmctl(int shmid,int cmd,struct shmid_ds *buf) shmid:要操作的共享記憶體標識,cmd:IPC_STAT,IPC_SET,IPC_RMID buf:儲存資料存放區
common.h:
1 #define TEXT_SZ 2048
2 struct shared_use_st
3 {
4 int written_by_you;
5 char some_text[TEXT_SZ];
6 };
shm1.c:
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/shm.h>
#include"common.h"
int main()
{
int running=1;
void *shared_memory=(void *)0;
struct shared_use_st *shared_stuff;
int shmid;
srand((unsigned int)getpid());
shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory=shmat(shmid,(void *)0,0);
if(shared_memory==(void *)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %x\n",(int)shared_memory );
shared_stuff=(struct shared_use_st *)shared_memory;
shared_stuff->written_by_you=0;
while(running)
{
if(shared_stuff->written_by_you )
{
printf("you wrote:%s",shared_stuff->some_text);
sleep(rand()%4);
shared_stuff->written_by_you=0;
if(strncmp(shared_stuff->some_text ,"end",3)==0)
{
running=0;
}
}
}//while
if(shmdt(shared_memory)==-1)
{
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
if(shmctl(shmid,IPC_RMID,0)==-1)
{
fprintf(stderr,"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
shm2.c
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/shm.h>
#include"common.h"
int main()
{
int running=1;
void *shared_memory=(void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;
shmid=shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
if(shmid==-1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory=shmat(shmid,(void *)0,0);
if(shared_memory==(void *)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %x\n",(int)shared_memory);
shared_stuff=(struct shared_use_st *)shared_memory;
while(running)
{
while(shared_stuff->written_by_you==1)
{
sleep(1);
printf("waiting for client...\n");
}
printf("Enter some text:");
fgets(buffer,BUFSIZ,stdin);
strncpy(shared_stuff->some_text,buffer,TEXT_SZ);
shared_stuff->written_by_you=1;
if(strncmp(buffer,"end",3)==0)
{
running=0;
}
}
if(shmdt(shared_memory)==-1)
{
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
msg訊息佇列:
訊息佇列由佇列ID唯一標識
訊息佇列就是一個訊息列表,使用者可以在訊息佇列裡面新增訊息,讀取訊息
訊息佇列可以按照型別來發送和接受訊息
建立或開啟訊息佇列:int msgget(key_t key,int flag) flag:訪問許可權 返回值:成功:訊息佇列id,錯誤:-1
新增訊息:int msgsnd(int msgid,const void *msgp,size_t size,int flag) msgp:指向訊息的指標struct msgbuf{ long mtype,char mtext[N]},size: 傳送的位元組數 flag:IPC_NOWAIT,訊息沒有傳送完函式也會立刻返回,0:直到傳送完函式才返回
讀取訊息:int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int flag) msgp:接收訊息緩衝區 size: 要接收的位元組數 msgtype: 0:接收第一個訊息,小於0:接收佇列中msgtype絕對值且類限制最小的訊息,大於0:接收佇列中第一個型別為msgtype 的訊息;
控制訊息: int msgctl(int msgid,int cmd,struct msgid_ds *buf) cmd: IPC_STAT: 讀取訊息佇列屬性,放在buf,IPC_SET: 設定訊息佇列屬性,從buf裡面取值,IPC_RMID: 從系統刪除訊息佇列,buf設定為NULL
client1.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define KEY_MSG 0x101 //使用共有的IPC key
#define MSGSIZE 64
typedef struct
{
long mtype;
char mtext[MSGSIZE];
}msgbuf;
#define LEN sizeof(msgbuf)-sizeof(long)
int main()
{
int msgid = 0;
msgbuf buf1;
msgbuf buf2;
msgid = msgget(KEY_MSG,0666);
while(1)
{
printf("input the msg to client2:");
gets(buf1.mtext);
buf1.mtype = 1L;
msgsnd(msgid,&buf1,LEN,0);//客戶端1獲取訊息併發往伺服器
msgrcv(msgid,&buf2,LEN,3L,0);//準備從客戶端2獲取訊息
if(buf2.mtext[0] == 'x' || buf2.mtext[0] == 'X')
{
printf("client1 will quit\n");
break;
}
printf("Receive from client2, message:%s\n",buf2.mtext);
}
return 0;
}
client2.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define KEY_MSG 0x101 //使用共有的IPC key
#define MSGSIZE 64
typedef struct
{
long mtype;
char mtext[MSGSIZE];
}msgbuf;
#define LEN sizeof(msgbuf)-sizeof(long)
int main()
{
int msgid = 0;
msgbuf buf1;
msgbuf buf2;
msgid = msgget(KEY_MSG,0666);
while(1)
{
msgrcv(msgid,&buf2,LEN,4L,0);//等待客戶端1發訊息
if(buf2.mtext[0] == 'x' || buf2.mtext[0] == 'X')
{
printf("Client2 will quit!\n");
break;
}
else
{
printf("Receive from client1,message:%s\n",buf2.mtext);
}
sleep(1);//等待1秒,以確保客戶端1已經收到了應答
printf("input the msg to client1:");
gets(buf1.mtext);
buf1.mtype = 2L;
msgsnd(msgid,&buf1,LEN,0);//給客戶端1傳送應答訊息
}
}
server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define KEY_MSG 0x101 //使用共有的IPC key
#define MSGSIZE 64
typedef struct
{
long mtype;
char mtext[MSGSIZE];
}msgbuf;
#define LEN sizeof(msgbuf)-sizeof(long)
int main()
{
int msgid = 0;
msgbuf buf1;
msgbuf buf2;
msgid = msgget(KEY_MSG,IPC_CREAT|0666);
while(1)//無限迴圈,退出標誌則會break for(;;)
{
msgrcv(msgid,&buf1,LEN,1L,0);//接收客戶端1的訊息
printf("Receive client1 message:%s\n",buf1.mtext);//列印收到的訊息
if(buf1.mtext[0] == 'x' || buf1.mtext[0] == 'X')//給2個客戶端都發退出資訊
{
strcpy(buf1.mtext,"x");
buf1.mtype = 3L;
msgsnd(msgid,&buf1,LEN,0);
buf1.mtype = 4L;
msgsnd(msgid,&buf1,LEN,0);
break;
}
buf1.mtype = 4L;
msgsnd(msgid,&buf1,LEN,0);//將客戶端1的訊息轉發給客戶端2
msgrcv(msgid,&buf2,LEN,2L,0);//接收客戶端2的訊息
printf("Receive client2 message:%s\n",buf2.mtext);
if(buf2.mtext[0] == 'x' || buf2.mtext[0] == 'X')
{
strcpy(buf2.mtext,"x");
buf2.mtype = 3L;
msgsnd(msgid,&buf2,LEN,0);
buf2.mtype = 4L;
msgsnd(msgid,&buf2,LEN,0);
break;
}
buf2.mtype = 3L;
msgsnd(msgid,&buf2,LEN,0);//將客戶端2的訊息轉發給客戶端1
}
sleep(1);//若退出,則先等待,以確保客戶端程式退出
msgctl(msgid,IPC_RMID,NULL);//刪除訊息佇列,釋放空間
exit(0);
}