进程间通信方式的分类
进程间通信方式总共可以分为七类,分别为有名管道,无名管道,信号,消息队列,共享内存,信号量,套接字(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)
由于此套接字为网络编程的内容,后期会更新相关知识,再次不做解释,忘谅解。
尾
以上为自己对进程间通信方式内容的理解,有不足之处还请大佬们在评论区指正出来,我后期改正。