程序間通信方式的分類
程序間通信方式總共可以分為七類,分别為有名管道,無名管道,信号,消息隊列,共享記憶體,信号量,套接字(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)
由于此套接字為網絡程式設計的内容,後期會更新相關知識,再次不做解釋,忘諒解。
尾
以上為自己對程序間通信方式内容的了解,有不足之處還請大佬們在評論區指正出來,我後期改正。