天天看點

程序間的通信方式(含代碼)程序間通信方式的分類詳解尾

程序間通信方式的分類

程序間通信方式總共可以分為七類,分别為有名管道,無名管道,信号,消息隊列,共享記憶體,信号量,套接字(socket)。

1、有名管道FIFO:有名管道是一種半雙工的通信方式,可以實作兩個互不相關的程序之間的通信,并且在檔案系統中可見,可以通過檔案名找到。且遵循先進先出的原則,不支援lseek().

2、無名管道pipe:無名管道是一種半雙工的通信方式,具有固定的讀端fd[0]和寫端fd[1],且隻能用于具有親緣關系的程序之間的通信,速度慢且容量有限;管道可以看成特殊的檔案,讀寫可以使用檔案IO中的read和write。

3、信号:信号 ( signal ) : 信号是一種比較複雜的通信方式,用于通知接收程序某個事件已經發生。

4、消息隊列MessageQueue:消息隊列是一種全雙工的通信方式,消息隊列由消息隊列ID來唯一辨別,消息丢列就是一個消息清單,使用者可以在消息隊列中添加消息、讀取消息等。消息隊列可以按照類型來發送/接收消息,相同類型先入先出,不同類型随意存取。

5、共享記憶體SharedMemory:共享記憶體是一種最為高效的通信方式,程序可以直接讀寫記憶體,而不需要任何資料的拷貝。為了在多個程序間交換資料,核心專門留出了一塊記憶體區,可以由需要通路的程序将其映射到自己的私有位址空間。程序可以直接讀寫這一記憶體區而不需要進行資料的拷貝,進而大大提高效率。由于多個程序共享一段記憶體,是以也需要某種同步機制,如互斥鎖和信号量。

6、信号量Semaphore:一般與共享記憶體配合使用。信号量是一個計數器,可以用來控制多個程序對共享資源的通路。

7、套接字Socket:套解口也是一種程序間通信機制,與其他通信機制不同的是,它可用于不同主機之前的程序通信。

詳解

有名管道

特點

1.隻能用于具有親緣關系(父子程序/兄弟程序)的程序之間的通信,速度慢,容量有限

2.半雙工的通信方式,具有固定的讀端fd[0]和寫端fd[1].

3.管道可以看成一種特殊的檔案,對于它的讀寫我們實用檔案IO中read和write

相關函數

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼案例

實作從有名管道中讀資料

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define FIFO "myfifo"
int main(void)
{
	//1.建立有名管道
         //管道檔案不存在則建立,存在,傳回	
	if(mkfifo(FIFO,O_CREAT|0777)<0 && errno!=EEXIST)
	{
		perror("mkfifo error");
		return -1;
	}
	printf("mkfifo ok\n");

	//2.打開有名管道
	//以隻讀方式打開檔案
	int fd=open(FIFO,O_RDONLY);
	if(fd<0)
	{
		perror("file open error");
		return -1;
	}
	//3.讀資料
	char buf[20]={'\0'};

	while(1)
	{
		memset(buf,'\0',sizeof(buf));
		read(fd,buf,sizeof(buf));//1.從有名管道讀取到緩沖區

		printf("read:%s",buf); //2.将緩沖區的内容顯示在終端
		if(strncmp(buf,"exit",4)==0) 
			break;

	}
	//4.關閉管道
	close(fd);
	return 0;
}

           

實作從有名管道中寫資料

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define FIFO "myfifo"
int main(void)
{
	//1.建立有名管道
         //管道檔案不存在則建立,存在,傳回	
	if(mkfifo(FIFO,O_CREAT|0777)<0 && errno!=EEXIST)
	{
		perror("mkfifo error");
		return -1;
	}
	printf("mkfifo ok\n");

	//2.打開有名管道
	//以隻寫方式打開檔案
	int fd=open(FIFO,O_WRONLY);
	if(fd<0)
	{
		perror("file open error");
		return -1;
	}
	//3.寫入資料
	char buf[20]={'\0'};

	while(1)
	{
		printf("write:");
		fgets(buf,sizeof(buf),stdin); //1.從鍵盤寫入到緩沖區
		
		write(fd,buf,strlen(buf)); //2.将緩沖區的内容寫入到有名管道中
		if(strncmp(buf,"exit",4)==0) 
			break;

	}
	//4.關閉管道
	close(fd);
	return 0;
}

           

無名管道

特點

1.隻能用于具有親緣關系(父子程序/兄弟程序)的程序之間的通信,速度慢,容量有限

2.半雙工的通信方式,具有固定的讀端fd[0]和寫端fd[1].

3.管道可以看成一種特殊的檔案,對于它的讀寫我們實用檔案IO中read和write

相關函數

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼案例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main(void)
{
	//1.建立一個無名管道
	//1-1.定義一個包含兩個元素的整形數組
	int fd[2]={0};

	//1-2.建立無名管道
	if(pipe(fd)<0)
	{
		perror("pipe error");
		return -1;
	}

	//建立成功之後,就有了固定讀端(fd[0])和寫端(fd[1])
	//2.建立子程序
	pid_t pid;
	pid=fork();
	
	if(pid<0)
	{
		perror("fork error");
		return -1;
	}else if(pid==0)
	{
		//子程序  fd[0]讀端   fd[1] 寫端
		//子程序發送:
		//  1.關閉讀端
		close(fd[0]);
		char buf[20]={'\0'};
		//  2.發送資料
		while(1)
		{
		//	printf("child:");
			fgets(buf,sizeof(buf),stdin);  //1.将資料從鍵盤輸入
			write(fd[1],buf,strlen(buf));                       //2.将資料寫入到無名管道中
		}
	}else if(pid>0)
	{
		//父程序 fd[0] 讀端  fd[1]寫端
		//1.關閉寫端
		close(fd[1]);
		char buf[20]={'\0'};
		//2.讀取資料
		while(1)
		{
			//1.從無名管道讀取到buf
			//printf("father:");
			read(fd[0],buf,sizeof(buf));
			//2.從buf顯示中标準輸出
			printf("father:%s",buf);
		}
	}
}

           

信号

特點

信号是一種異步通信方式。

信号産生的方式

1、硬體産生(組合鍵)信号

以下三種均為硬體産生的信号

ctrl+c <>SIGINT

ctrl+z <>SIGTSTP

ctrl+\ <==>SIGQUIT

2、核心發送信号

相關函數舉例

alarm()也稱為鬧鐘函數,它可以在程序中設定一個定時器。當定時器指定的時間到時,核心就向程序發送SIGALRM信号。

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼實作

#include <stdio.h>
#include <unistd.h>
int main(void)
{

	alarm(5); //設定了鬧鐘函數,時間到了之後,
		 //核心給程序發送SIGALRM信号,收到該信号後程序結束
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

           

pause()函數是用于将調用程序挂起直到收到信号為止。

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼實作

#include <stdio.h>
#include <unistd.h>
int main(void)
{

	alarm(5); //設定了鬧鐘函數,時間到了之後,
		 //核心給程序發送SIGALRM信号,收到該信号後程序結束
	pause(); //将程序挂起,直到受到某個信号為止
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}


           

3、軟體方式産生信号

相關函數

raise() 自己給自己發送信号

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

kill() 給别人發送信号

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

int main(void)
{
	//1.建立子程序
	pid_t pid;

	pid=fork();

	if(pid<0)
	{
		perror("fork error");
		return -1;
	}else if(pid==0)
	{
		int i=1;
		while(1)
		{
			if(i%13==0)
			{
				printf("子程序被自己停止\n");
				raise(SIGSTOP); //當i是13.發送一個停止的信号
			}

			printf("recv cam\n");
			sleep(1);
			i++;
		}
	}else if(pid>0)
	{
		int j=1;
		while(1)
		{
			if(j%17==0)
			{
				printf("父程序啟動子程序\n");
				kill(pid,SIGCONT);  //給别人發送信号
			}
			printf("send data\n");
			sleep(1);
			j++;
		}
	}
	return 0;
}

           

捕獲信号

1.執行預設操作

2.忽略信号 即對信号不做任何處理,有兩個信号不能忽略 SIGKILL SIGSTOP

相關函數

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main(void)
{


	//捕獲信号,捕獲到信号之後幹什麼忽略
	//參數1:信号
	//參數2:SIG_IGN 忽略信号
	signal(SIGINT,SIG_IGN); //ctrl+c
	signal(SIGTSTP,SIG_IGN);//ctrl+z
	signal(SIGQUIT,SIG_IGN);//ctrl+'\\'
	signal(SIGALRM,SIG_IGN);

	alarm(5);
	while(1)
	{
		printf("helloworld\n");
		sleep(1);
	}
	return 0;
}

           

3.安裝信号,獲得信号之後,我們要去執行自定義函數

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼實作

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//void (int) 函數
void handler(int sig)
{
	switch(sig)
	{
		case SIGINT:
			printf("I am ctrl+c\n");
			break;
		case SIGTSTP:
			printf("I am ctrl+z\n");
			break;
		case SIGQUIT:
			printf("I am ctrl+\\\n");
			break;
		case SIGALRM:
			printf("I am alarm\n");
			break;
	}
}
int main(void)
{


	//捕獲信号,捕獲到信号之後執行自定義函數   void (int)
	//參數1:信号
	//參數2:SIG_IGN 忽略信号  handler    void (int) 函數指針
	signal(SIGINT,handler); //ctrl+c
	signal(SIGTSTP,handler);//ctrl+z
	signal(SIGQUIT,handler);//ctrl+'\\'
	signal(SIGALRM,handler);

	alarm(5);
	while(1)
	{
		printf("helloworld\n");
		sleep(1);
	}
	return 0;
}

           

消息隊列

特點

1.消息隊列由消息隊列ID來唯一辨別

2.消息隊列就是一個消息的清單。使用者可以在消息隊列中添加消息、讀取消息等。

3.消息隊列可以按照類型來發送/接收消息,相同類型先入先出,不同類型随意存取。

4.全雙工的一個通信方式

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

相關函數

建立消息隊列

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

發送接收消息隊列

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾
程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

删除消息隊列

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
	//1.獲得key值
	key_t key;
	//參數1:檔案路徑名  一般使用目前路徑./
	//參數2:一個int  随機數
	key=ftok("./",12);
	//2.建立消息隊列  msgget
	//參數1:key值  參數2:建立權限
	system("ipcs  -q");
	sleep(5);
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}
	printf("------------------------------------------------\n");
	system("ipcs -q");
	sleep(5);
	// 建立共享記憶體   shmget
	// 建立信号燈集   semget
	//3.删除消息隊列
	//msgctl(msgid,IPC_RMID,NULL);
	printf("================================================\n");
	system("ipcs -q");
	sleep(5);
	return 0;
}

           

執行結果

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

利用消息隊列實作程序間通信的代碼

發送端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h"
#include <string.h>
int main(void)
{
	//1.獲得key值
	key_t key;
	//參數1:檔案路徑名  一般使用目前路徑./
	//參數2:一個int  随機數
	key=ftok("./",12);
	//2.建立消息隊列  msgget
	//參數1:key值  參數2:建立權限
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}


	//3.發送消息
	//3.1.定義一個消息結構變量
	Mbuf sndbuf;
	sndbuf.mtype=100; //設定消息類型是100的

	while(1){
		printf("send:\n");
		fgets(sndbuf.mtext,sizeof(sndbuf.mtext),stdin);
		//3.2.發送給接受端
		//參數1:msgID值
		//參數2:  msgsnd(msgid,&sndbuf,sizeof(sndbuf.mtext),0);
		//參數3:正文的大小
		//參數4:阻塞發送 0
		msgsnd(msgid,&sndbuf,strlen(sndbuf.mtext),0);
	}

	// 建立共享記憶體   shmget
	// 建立信号燈集   semget
	//3.删除消息隊列
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}

           

接收端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h"
#include <string.h>
int main(void)
{
	//1.獲得key值
	key_t key;
	//參數1:檔案路徑名  一般使用目前路徑./
	//參數2:一個int  随機數
	key=ftok("./",12);
	//2.建立消息隊列  msgget
	//參數1:key值  參數2:建立權限
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}


	//3.按照類型來接受消息
	Mbuf recBuf;
	recBuf.mtype=100;

	while(1){
		memset(&recBuf,'\0',sizeof(recBuf));
		//參數1:msgid
		//參數2:接受消息的緩沖區
		//參數3:接受消息的位元組數
		//參數4:接受消息的類型
		//參數5:阻塞接受
		msgrcv(msgid,&recBuf,sizeof(recBuf.mtext),recBuf.mtype,0);
		printf("read:%s\n",recBuf.mtext);
	}

	//
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}
           

共享記憶體

特點

1.共享記憶體是一種最為高效的程序間通信方式,程序可以直接讀寫記憶體,而不需要任何資料的拷貝。

2.為了在多個程序間交換資訊,核心專門留出了一塊記憶體區,可以由需要通路的程序将其映射到自己的私有位址空間。

3.程序就可以直接讀寫這一記憶體區而不需要進行資料的拷貝,進而大大提高的效率。

4.由于多個程序共享一段記憶體,是以也需要依靠某種同步機制,如互斥鎖和信号量等。

相關函數

1、建立共享記憶體

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

2、映射共享記憶體

即把指定的共享記憶體映射到程序的位址空間用于通路

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

3.向共享記憶體寫入資料

4.撤銷映射

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

5.删除共享記憶體

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼實作

利用共享記憶體的方式實作程序間通信

發送端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	//1.獲得key值
	key_t key;
	key=ftok("./",12);

	//	system("ipcs -m");
	//	sleep(5);
	//2.建立共享記憶體
	//參數1:ftok()獲得的key值
	//參數2:空間的大小 
	//參數3:不存在IPC_CREAT | 0777
	//IPC_EXCL

	int shmid=shmget(key,1024,IPC_CREAT|0600);
	if(shmid<0)
	{
		perror("shmget error");
		return -1;
	}
	printf("--------------------------------------------\n");
	//	system("ipcs -m");
	//	sleep(5);

	//3.映射共享記憶體區域,并傳回映射後的首位址
	char * pShm=NULL;
	pShm=(char *)shmat(shmid,NULL,0);
	if(pShm==NULL)
	{
		perror("shmat error");
		return -1;
	}

	while(1){
		printf("write:");
		//4.直接給映射後的位址(共享記憶體)寫入資料
		fgets(pShm,1024,stdin);
	}
	//	system("ipcs -m");
	//	sleep(5);

	//5.撤銷共享記憶體
	shmdt(pShm);
	//6.删除共享記憶體
	//shmctl(shmid,IPC_RMID,NULL);

}

           

接收端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	//1.獲得key值
	key_t key;
	key=ftok("./",12);

	//system("ipcs -m");
	//sleep(5);
	//2.建立共享記憶體
	//參數1:ftok()獲得的key值
	//參數2:空間的大小 
	//參數3:不存在IPC_CREAT | 0777
	//IPC_EXCL

	int shmid=shmget(key,1024,IPC_CREAT|0600);
	if(shmid<0)
	{
		perror("shmget error");
		return -1;
	}
	//printf("--------------------------------------------\n");
	//system("ipcs -m");
	//sleep(5);

	//3.映射共享記憶體區域,并傳回映射後的首位址
	char * pShm=NULL;
	pShm=(char *)shmat(shmid,NULL,0);
	if(pShm==NULL)
	{
		perror("shmat error");
		return -1;
	}

	while(1){
		//4.從映射的空間内讀取資料
		printf("%s",pShm);
	}
	//system("ipcs -m");
	//sleep(5);

	//5.撤銷共享記憶體
	shmdt(pShm);
	//6.删除共享記憶體
	//	shmctl(shmid,IPC_RMID,NULL);

}

           

信号量

特點

信号量不能傳遞資料,一般和共享記憶體配合使用,實作共享記憶體同步。

無名信号量

sem_t sem1 信号量是共享的

sem_init() 初始化信号量

sem_wait() P操作 sem>0 -1操作 ==0 阻塞線程

sem_post() V操作 +1操作,同時喚醒阻塞的線程

有名信号量

相關函數

無名信号量

sem_t sem1 信号量是共享的

sem_init() 初始化信号量

sem_wait() P操作 sem>0 -1操作 ==0 阻塞線程

sem_post() V操作 +1操作,同時喚醒阻塞的線程

有名信号量

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

sem_wait (P操作)

Sem_post(V操作)

程式間的通信方式(含代碼)程式間通信方式的分類詳解尾

代碼實作

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(void)
{
	//1.建立信号量1和2
	//參數1:名字
	//參數2:O_CREAT 不存在建立,存在傳回
	//參數3:權限 八進制
	//參數2:信号量值
	//傳回值:信号量指針
	sem_t * psem1=sem_open("aaa",O_CREAT,0666,1);
	sem_t * psem2=sem_open("bbb",O_CREAT,0666,0);

	while(1)
	{
		sem_wait(psem1);  //P操作
		printf("hello\n");
		sleep(1);
		sem_post(psem2); //V操作
	}
}

           
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(void)
{
	//1.建立信号量1和2
	//參數1:名字
	//參數2:O_CREAT 不存在建立,存在傳回
	//參數3:權限 八進制
	//參數2:信号量值
	//傳回值:信号量指針
	sem_t * psem1=sem_open("aaa",O_CREAT,0666,1);
	sem_t * psem2=sem_open("bbb",O_CREAT,0666,0);

	while(1)
	{
		sem_wait(psem1);  //P操作
		printf("hello\n");
		sleep(1);
		sem_post(psem2); //V操作
	}
}

           

套接字(socket)

由于此套接字為網絡程式設計的内容,後期會更新相關知識,再次不做解釋,忘諒解。

以上為自己對程序間通信方式内容的了解,有不足之處還請大佬們在評論區指正出來,我後期改正。