进程间通信IPC:
1.无名管道:半双工(单向)通信,有固定的读端和写端。
适用情况:通信的进程只有两个,且为亲缘进程(父子进程,或者间接继承关系的进程)
- 当管道中没有数据可读后,读端的进程会被等待(阻塞)
- 当进程写一个所有读端都被关闭的管道时,写端进程会被内核返回的SIGPIPE信号终止,如果不想被终止,则需要忽略、捕获、屏蔽该信号
- 用两个管道才能实现双向通信
2.有名管道:FIFO 单向通信,
消息队列:
消息队列使用步骤:
- 使用msget函数创建新的消息队列,或者获取已经存在的消息队列,并返回唯一的标识消息队列的标识符(没事情ID),后续收发消息使用这个标识符李来实现。
- 使用msgsnd函数,(send的意思)利用消息队列标识符发送某编号的消息。
- 使用msgrcv函数,(receive的意思)利用消息队列标识符接受某编号的消息。
- 使用msgctl函数,(ctrl的意思) 利用消息队列标识符删除消息队列
多个进程间通信,只需要创建一个消息队列。为了保证消息队列的创建,最好让每个进程都包含创建消息队列的代码。谁先运行就由谁来创建。后运行的进程会直接使用已经创建好的消息队列。
如何验证消息队列是否被创建成功:
使用ipcs 命令查看 :
- -a :显示 消息队列、共享内存、信号量的信息
- -m:(memory)显示共享内存的信息
- -q: (queue)显示消息队列的信息
- -s : (semapaore)显示信号量的信息
使用ipcrm命令删除:
------删除共享内存
- -M : 按照key值删除
ipcrm -M key - -m:按照标识符删除
ipcrm -m msgid
------删除消息队列
- -Q:按照key值删除
- -q:按照标识符删除
------删除信号量
- -S:按照key值删除
- -s:按照标识符删除
下面是用消息队列实现的 多进程网状通信的例子程序:
/*
本程序 用一个消息队列,实现多进程之间的网状通信
运行方式: 需要Linux操作系统环境
1.先打开一个终端窗口(后面称为TTY1),编译gcc message_queue.c -o message_queue
运行 ./message_queue 1
input your send message text:
qqqqqqqqqqqqqqwwwwwwwwwww
input snd_msgtype:
2
2.另外打开2个终端(后面称为TTY2 TTY3)
3. TTY2下运行 ./message_queue 2,将会看到 TTY1窗口发送来的 qqqqqqqqqqqqqqwwwwwwwwwww
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#define MSG_FILE "./msgfile" //生成key 需要的文件路径
#define MSG_SIZE 1024 //指定消息内容大小
struct msgbuf //固定的结构体,msgsnd 函数和 msgrcv函数 规定的消息格式
{
long mtype; //存放消息编号,必须 >0
char mtext[MSG_SIZE]; //消息内容(消息正文)
};
void print_err(char *estr)
{
perror(estr);
exit(-1);
}
/*创建消息队列,或着获得消息队列id
返回值: msgid: 新创建的消息队列的id,或者已经存在的消息队列的id
*/
int creat_or_get_msgque(void)
{
int msgid = -1;
key_t key=-1;
int fd=0;
fd =open(MSG_FILE,O_RDWR|O_CREAT,0664);
if(fd == -1) print_err("open fail");
key=ftok(MSG_FILE, 'a'); //利用ftok函数获取 msgget函数的key值
if(key == -1)
print_err("open fail");
msgid=msgget(key,0664|IPC_CREAT); //如果key值相同,则会返回已经存在的消息队列ID,否则会创建一个新的消息队列
if(msgid == -1)
print_err("msgget fail");
return msgid;
}
int msgid = -1;
void signal_fun(int signo) //捕获到信号后要执行的函数
{
msgctl(msgid,IPC_RMID,NULL);
exit(-1);
}
int main(int argc,char *argv[])
{
pid_t pid=-1;
long recv_msgtype =0; //指定 接收消息编号, 该进程只能读取,该消息编号里的类容
if(argc!=2)
{
print_err("./message_queue recv_msgtpye\n");
exit(-1);
}
recv_msgtype=atol(argv[1]);
msgid =creat_or_get_msgque();
pid=fork();
if(pid<0)
print_err("fork fail");
if(pid>0) //发送消息
{
signal(SIGINT,signal_fun);
struct msgbuf msg_buf={0};
while(1)
{
bzero(&msg_buf,sizeof(msg_buf)); //清空消息队列
//封装消息包
scanf("%s",msg_buf.mtext); //输入你要发送的消息
printf("input snd_msgtype: \n");
scanf("%ld",&msg_buf.mtype); //输入 消息编号
//发送消息
msgsnd(msgid,&msg_buf,MSG_SIZE, 0);
}
}
else if(pid==0)// 接收消息
{
struct msgbuf msg_buf={0};
int ret=0;
while(1)
{
bzero(&msg_buf,sizeof(msg_buf)); //清空消息队列
ret=msgrcv(msgid,&msg_buf,MSG_SIZE,recv_msgtype,0);
if(ret>0)
{
printf("%s\n",msg_buf.mtext);
}
}
}
}
共享内存:
共享内存使用步骤:
- 进程调用shmget函数创建新的或者已经存在的共享内存,shm是share memory的缩写
- 进程调用shmat函数,将物理内存映射到自己的进程空间
- shmdt函数,取消映射
- 调用shmctl函数释放开辟的物理内存空间
多个进程共享内存通信时,创建者只需要一个,一般是谁先运行谁创建。