1. 程式人生 > >通訊方式詳解,無名管道pipe,有名管道fifo,共享記憶體share memory,訊息佇列msg

通訊方式詳解,無名管道pipe,有名管道fifo,共享記憶體share memory,訊息佇列msg

常用的程序通訊方式有:

傳統的程序通訊方式:無名管道(pipe),有名管道(fifo),訊號(signal)

system V IPC物件:共享記憶體(share memeory),訊息佇列(message queue),訊號燈(semaphore)

BSD:套接字(socket)

pipepipe只能用於具有親緣關係的程序之間通訊

             半雙工通訊模式,具有固定的讀端和寫端

              管道可以看作是一種特殊的檔案,對於它的讀寫可以使用檔案IO如read,write函式。

當管道無資料時,讀操作阻塞

寫資料時,linux不保證寫入原子性,管道緩衝區一有空閒區域,寫程序就會試圖向管道緩衝區寫資料,如果讀操作不讀取緩衝區資料,那麼寫操作將會一直阻塞。

讀端存在,寫資料才有意義,否則管道中寫入資料會收到核心傳來的SIFPIPE訊號

#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);
}
fifo:

            無名管道可以使兩個不相干的程序通訊,使用路徑來指出,在檔案系統中可見

             使用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);
}