Linux——进程间通信(1)——管道、消息队列

一、进程间通信(IPC)介绍

进程间通信(IPC,interProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Streams支持不同主机上的两个进程IPC。

这个是重点 背下来

二、无名管道

通常指无名管道

特点

1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写段。
2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

原型

#include<unistd.h>
int pipe(int fd[2]); //返回值:若返回成功为0,失败返回-1

当一个管道建立时,它会创建两个文件描述符;fd[0]为读而打开,fd[1]为写而打开。

要关闭管道只需将这两个文件描述符关闭即可。

代码演示:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <unistd.h>
int main()
{
        int fd[2];

        int pid;
        char buf[128]={0};

//      int pipe(int pipefd[2]);

        if(pipe(fd)==-1){
                printf("create pipe failed!\n");
        }
        pid = fork();
        if(pid<0){
                printf("create fork failed!\n");
        }
        else if(pid>0){
                sleep(3); //这里测试一下 父进程如果睡三秒 子进程的读操作会被阻塞直至父进程恢复
                printf("this is father\n");
                close(fd[0]);//这里特别要注意的是 如果写就要先关闭读 如果读就要关闭写
                write(fd[1],"hello son",strlen("hello son"));//读是fd[0] 写是fd[1] 死记就完事了
                wait(); //这里收集一下防止僵尸进程
        }else{
                printf("this is child\n");
                close(fd[1]);
                read(fd[0],buf,128);
                printf("read buf is:%s\n",buf);
                exit(0);
        }
        return 0;
}

在这里插入图片描述

三、FIFO命名管道

FIFO,它是一种文件类型。

特点

1.FIFO可以在无关的进程之间交换数据,与无名管道不同
2.FIFO有路径名与之相关联,它是一种特殊设备文件形式存在于文件系统中。

原型

       int mkfifo(const char *pathname, mode_t mode);
       //第一个是文件名   mode和open一样 例 0600

当open一个FIFO文件时,是否设置非阻塞标志(O_NONBLOCK)的区别:
1.没有指定(默认),只读open要阻塞到某个其他进程为写而打开此FIFO。类似的,open要阻塞到某个其他进程为读而打开它。
2.指定了O_NONBLOCK,则只读open立即返回。而只写open将出错返回-1如果没有进程已经为读而打卡该FIFO,其errno置ENXIO。

代码示例

#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<errno.h>

//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
        if(mkfifo("./file",0600)==-1&&errno==EEXIST){
        	//调用mkfifo创建命名管道,如果返回-1说明创建失败
        	//调用errno判断是否存在文件
                printf("mkfifo failed!\n");
                perror("why");
        }
        return 0;
}

在这里插入图片描述

数据通信编程实现

read.c

#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>

//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
        char buf[30]={0};
        int nread=0;
        if(mkfifo("./file",0600)==-1&&errno==EEXIST){
                printf("mkfifo failed!\n");
                perror("why");
        }

        int fd=open("./file",O_RDONLY);//无论是读还是写都要先打开文件
        printf("open success!\n");
        while(1){
                nread=read(fd,buf,30); //这里做个循环不断读取 read在write没有运行的情况下会一直阻塞 直至write运行
                printf("read byte:%d context:%s\n",nread,buf);
        }
        close(fd);
        return 0;
}

write.c

#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>

//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
        int cnt=0;
        char *str="message from fifo!";
        int fd=open("./file",O_WRONLY);
        printf("write open success!\n");
        while(1){
                write(fd,str,strlen(str));
                sleep(1);
                cnt++;
                if(cnt==5){
                        break;
                }
        }
        close(fd);

        return 0;
}

在这里插入图片描述

四、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符即队列ID来标识。

1、特点

1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会删除。
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

2、原型

获取/创建 消息队列

以下两种情况msgget将创建一个新的消息队列:
1.如果没有与键值key相对应的消息队列,并且flag中包含IPC_CREAT标志位
2.key参数为IPC_PRIVATE。

int msgget(key_t key, int msgflg);
//创建或打开消息队列:成功返回队列ID,失败返回-1

添加发送消息

成功返回0,失败返回-1

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
		//队列id       发送内容地址       内容大小        flg默认为0,读消息时没有收到发送消息会阻塞

得到读取消息

成功返回消息数据的长度,失败返回-1

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);                                            

type非0时用于以非先进先出次序读消息。也可以把type看做优先级的权值。

例子

实现简单的消息队列通信

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
      // int msgget(key_t key, int msgflg);
struct msgbuf {//定义一个结构体 因为消息队列是链表,其中的结点是结构体
        long mtype;       /* message type, must be > 0 */  //type
        char mtext[128];    /* message data */
};
int main()
{
        //1.chuang jian dui lie
        struct msgbuf readbuf;
        struct msgbuf sendBuf={111,"thank for you message!"};
        int msgID=msgget(0x1234,IPC_CREAT|0777);  //获取/创建队列 给予0777可读可写可执行权限
                       //第一个参数为key,类似于暗号 对上了就能通信
        if(msgID==-1){//如果返回-1说明创建失败
                printf("get que failed!\n");  
        }

        //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
        //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
          //            int msgflg);
        msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),888,0);
             //和文件描述符类似  readbuf为读取创建接收区   888为type  
        printf("read from send:%s\n",readbuf.mtext);

        msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);

        return 0;
}
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
      // int msgget(key_t key, int msgflg);
struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
        //1.chuang jian dui lie
        struct msgbuf sendBuf={888,"this from send message!"};;
        struct msgbuf readBuf;
        int msgID=msgget(0x1234,IPC_CREAT|0777);
        if(msgID==-1){
                printf("get que failed!\n");
        }

        //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
        //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
          //            int msgflg);

        msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);

        msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),111,0);
        printf("return from get:%s\n",readBuf.mtext);
        return 0;
}
~ 

在这里插入图片描述
在这里插入图片描述

三、ftok函数

系统建立IPC通讯时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

示例

key_t key;
        key=ftok(".",'z');
        printf("key=%x\n",key);
        int msgID=msgget(key,IPC_CREAT|0777);

在这里插入图片描述

四、msgctl控制消息队列

用来控制消息队列,如果过多创建队列会导致系统资源紧张,这个时候需要队列去除

原型

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
					//第二个参数为指令 有 STAT SET RMID INFO使用率较高的为 RMID(删除)

示例

msgctl(msgID,IPC_RMID,NULL);

在不知道发和收哪个先退出的情况下 两个都有写这段代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值