一、进程间通信概述
1.进程间通信:IPC(InterProcess Communication)
2.单机通信:一台电脑上的通信
3.多机通信:多台电脑上的通信,是基于网络的通信
4.单机通信类型: 半双工管道 (FIFO),全双工管道 (命名全双工管道),消息队列,信号量,共享内存
5.多机通信类型:嵌套字,STREAMS,Socket
二、管道通信原理
1.管道:通常指无名管道
2.特点:
(1).半双工,即数据只能在一个方向上流动,具有固定的读端和写段
(2).只能用于进程间的通信
(3).可看做一种特殊的存在于内存的文件,write,read等系统函数可对它进行操作
3.原形:
int pipe(int fd[2]);//建立管道,失败返回-1,成功返回0
fd[0]:读
fd[1]:写
4.用法:
读时,关闭写
写时,关闭读
三、管道编程之无名管道
int main(){
int fd[2];
pid_t repid;
char buf[128];
int repipe=pipe(fd);//创建无名管道
if(repipe < 0){
printf("creat unamed pipe failt\n");
}
repid=fork();//创建进程
if(repid < 0){
printf("creat fork failt\n");
}
else if(repid > 0){
sleep(3);//等待3秒便于观察父子进程
printf("this is father fork\n");
close(fd[0]);//关闭可读通道
write(fd[1],"you are a lucky dog",strlen("you are a lucky dog"));//往可写通道里写入内容
wait(NULL);//让子进程先执行
}
else{
printf("this is child fork\n");
close(fd[1]);//关闭可写通道
read(fd[0],buf,128);//读取可写通道内容
printf("readBuf=%s\n",buf);
exit(0);//正常退出
}
return 0;
}
运行结果:
四、管道编程之有名管道
1. FIFO:命名管道,也是这一种文件类型
2.特点:无关进程之间的数据交换,有路径名与之关联
3.原形:
int mkfifo(const char *pathname,mode_t mode);//成功返回0,反之-1
4.代码实现:
int main(){
int fifo=mkfifo("./file",0600);//建立实名管道并设置权限
if(fifo == -1 && errno != EEXIST){//errno,错误;EEXIST,如果创建的管道存在则会返回EEXIST
printf("creat fifo fault!\n");
}
return 0;
}
五、有名管道数据通信实现
1.实现管道的读取内容:
// int mkfifo(const char *pathname, mode_t mode);
// int open(const char *pathname, int flags);
// ssize_t read(int fd, void *buf, size_t count);
int main(){
char readBuf[40];
int n_read;
int cnt=0;
int fifo=mkfifo("./file",0600);//创建有名管道并设置权限
if(fifo == -1 && errno != EEXIST){
printf("creat fifo fault!\n");
}
int fd=open("./file",O_RDONLY);//打开管道,并设置为只读
if(fd != -1){
printf("open file success!\n");
}
while(1){
n_read=read(fd,readBuf,40);//读取写入的内容
sleep(1);
printf("return value:%d,readBuf=%s\n",n_read,readBuf);
cnt++;
if(cnt==5){
break;
}
}
close(fd);//关闭文件
return 0;
}
2.实现管道的写入内容 :
int main(){
int cnt=0;
int n_write;
char* str="you are a lucky dog";
int fd=open("./file",O_WRONLY);
while(1){
n_write=write(fd,str,strlen(str));//写入内容
cnt++;
if(cnt==5){//控制写入的次数
break;
}
}
if(n_write != -1){
printf("write success!\n");
}
close(fd);//关闭文件
return 0;
}
3.运行结果
六、消息队列通信原理
1.消息队列:即消息的链表,存放于内核中,每一个消息队列都有一个标识符
2.特点:
(1).消息队列是面向记录,具有特定的格式及优先级
(2).独立的接受与发送。进程终止时,消息队列不会被删除
(3).可以实现随机查询,可以按消息的类型读取
3.原形:
int msgget(key_t key,int flag);//创建或打开消息队列,成功返回ID,反之-1
int msgsnd(ind msgid,const void *ptr,size_t size,int flag);//发送消息,成功返回0,反之-1
int msgrcv(int msgid,void *ptr,size_t size,long type,long flag);//读取消息,成功返回消息长度,反之-1
int msgctl((int msgid,int cmd,struct msgid_ds *buf);//控制消息队列,成功返回0 ,反之-1
4.参数功能:
从标识符为msgid的消息队列里接收一个指定类型的消息 并 存储于msgp中 读取后 把消息从消息队列中删除
msgtyp:为 0 表示无论什么类型 都可以接收
msgp:存放消息的结构体
msgsz:要接收的消息的大小 不包含消息类型占用的4字节
msgflg:如果是0 标识如果没有指定类型的消息 就一直等待,如果是IPC_NOWAIT 则表示不等待
七、消息队列收发数据与移除
代码实现1:
typedef struct msgbuf {
long mtype;
char mtext[80];
}msgs;
//key_t ftok(const char *pathname, int proj_id);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int main(){
key_t key;//键值
key=ftok(".",1);
printf("key=%x\n",key);//%x 转化为16进制
msgs readBuf;
msgs sendBuf={998,"dugad is a handsome man"};
int msgId=msgget(key,IPC_CREAT|0777);//创建消息队列,成功返回0,反之-1,并设置权限
if(msgId == -1){//判断是否创建成功
printf("get msg failure\n");
}
int receive=msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//接受消息队列
if(receive == -1){//判断是否接受成功
printf("readBuf errno!\n");
}
else{
printf("readBuf success!\n");
}
int send=msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//发送消息
if(send == -1){//判断是否发送成功
printf("sendBuf errno!\n");
}
else{
printf("sendBuf success!\n");
}
printf("readBuf=%s\n",readBuf.mtext);//打印接受的内容
int n_msgctl= msgctl(msgId,IPC_RMID,NULL);//清除消息队列
printf("n_msgctl=%d\n",n_msgctl);
if(n_msgctl == -1){//判断是否清除成功
printf("remove msg errno!\n");
}
else{
printf("remove msg success!\n");
}
return 0;
}
代码实现2:
typedef struct msgbuf {
long mtype;
char mtext[80];
}msgs;
int main(){
key_t key;
key=ftok(".",1);
printf("key=%x\n",key);
msgs sendBuf={888,"this magsnd from quer"};
msgs readBuf;
int msgId=msgget(key,IPC_CREAT|0777);//创建消息队列,成功返回0,反之-1,并设置权限
if(msgId == -1){
printf("get msg failure\n");
}
int send=msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
if(send == -1){
printf("msgsend errno!\n");
}
else{
printf("magsend success!\n");
}
int receive=msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),998,0);
if(receive == -1){
printf("readBuf errno!\n");
}
else{
printf("readBuf success!\n");
}
printf("readBuf=%s\n",readBuf.mtext);
int n_msgctl=msgctl(msgId,IPC_RMID,NULL);
printf("n_msgctl=%d\n",n_msgctl);
if(n_msgctl == -1){
printf("remove msg errno!\n");
}
else{
printf("remove msg success!\n");
}
return 0;
}
运行结果:
注意:
在读写完成后删除消息队列时,只需在其中一方设置删除即可,如果两边都设置了删除消息队列会使,有一方会删除失败