博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程间通信--管道、消息队列
阅读量:4184 次
发布时间:2019-05-26

本文共 4842 字,大约阅读时间需要 16 分钟。

我们学习高级进程通信,指用户可直接利用操作系统所提供的一组通信命令高效地传送大量数据的一种通信方式。

进程间通信的几种主要手段:管道、消息队列、共享内存、套接字

管道

管道,指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,只存在于内存中,是一种两个进程间进行单向通信的机制。在创建管道时,系统为管道分配一个页面作为数据缓冲区,通过管道通信的两个进程通过读写这个缓冲区来进程通信,写在缓冲区尾部,从头部读取数据。

这里写图片描述

局限性:

数据单向传递,即半双工;管道没有名字,只能用于具有亲缘关系的进程间通信;管道的缓冲区大小受限制;管道传送的是无格式的字节流。

管道的创建:

int pipe(int fd[2]) //调用成功返回0,且数组中包含两个新的fd,失败返回-1

管道两端分别用fd[0]和fd[1]描述,管道两端的任务固定,fd[0]只能用于读,称为管道读端;fd[1]只能用于写,称为管道写端。

管道一旦创建成功,就可以作为一般的文件来使用,对一般文件进行操作的I/O函数也适用于管道。

管道的读写:如果某进程要读取管道中的数据,那么该进程应当关闭fd[1],同时向管道写数据的进程应当关闭fd[0]。向管道中写数据类似。

这两个例子中pipe在fork之前调用,让子进程可以直接共享父进程的文件描述符。但是如果子进程调用exec函数执行另外一个程序时,就不能共享了。这种情况下可以将子进程中的文件描述符重定向到标准输入,当新执行的程序从标准输入获取数据时实际上是从父进程中获取输入数据。

#include 
#include
#include
#include
#include
/*read pipe*/void read_from_pipe(int fd){ char message[100]; read(fd,message,100); printf("read from pipe:%s\n",message);}/*write pipe*/void write_from_pipe(int fd){ char message[100]; printf("input message: "); scanf("%s",message); write(fd,message,strlen(message)+1);}int main(){ int fd[2]; pid_t pid; int stat_val; if(pipe(fd)) { printf("create pipe failed\n"); exit(1); } pid = fork(); switch(pid) { case -1: printf("fork error!\n"); exit(1); case 0: /*子进程关闭写端*/ close(fd[1]); read_from_pipe(fd[0]); exit(0); default:/*父进程关闭读端*/ close(fd[0]); write_from_pipe(fd[1]); exit(0); } return 0;}

有名管道:nameed pipe或FIFO

有名管道是一个设备文件,它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中,只要进程可以访问该路径,就能够通过FIFO相互通信。与队列类似,先进先出,第一个被写入的数据首先从管道中读出。

有名管道的创建与读写
第一种方式:shell下使用mknod或mkfifo命令创建
第二种方式:系统函数

#include 
#include
int mknod(const char* path,mode_t mod, dev_t dev); int mkfifo(const char *path,mode_t mode);

第一个参数是创建的有名管道的全路径名,第二个参数是有名管道的模式,指明权限

有名管道创建后,存在与硬盘上,必须先用open()将其打开。打开有名管道的进程可能会被阻塞,如果同时以读写方式打开,则一定不会阻塞。如果以只读方式打开,则调用open的进程将会被阻塞直到有写方打开管道。

/*fiford.c*/#define FIFO_NAME "myfifo"#define BUF_SIZE 1024int main(){    int fd;    char buf[BUF_SIZE];    umask(0);    fd = open(FIFO_NAME,O_RDONLY);    read(fd,buf,BUF_SIZE);    printf("Read content: %s\n",buf);    close(fd);    return 0;}/*fifowr.c*/#define FIFO_NAME "myfifo"#define BUF_SIZE 1024int main(){    int fd;    char buf[BUF_SIZE] = "hello fiford,I come from fifowr!";    umask(0);    if(mkfifo(FIFO_NAME,S_IFIFO | 0666) == -1)    {        perror("mkfifo error!");        exit(1);    }    if((fd = open(FIFO_NAME,O_WRONLY)) == -1)    {        perror("open error!");        exit(1);    }    write(fd,buf,strlen(buf)+1);    close(fd);    return 0;}

消息队列

消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识。消息队列存放在内核中,只有在内核重启或者显式地删除一个消息队列时,该消息队列才会被真正地删除。

消息队列的创建:

#incllude 
#include
key_t ftok(const char *pathname, int proj_id); //根据pathname和proj_id这两个参数生成惟一的键值,失败返回-1#include
int msgget(key_t key,int msgflg); //根据ftok传来的键值,创建一个新的消息队列或者访问一个已存在的消息队列成功返回消息队列的描述符,失败返回-1

读/写消息队列

#include 
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflag);int msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long ing msgtype, int msgflag);参数:msqid:消息队列描述符msgp:读取的消息存储到msg指向的结构体中msgsz:消息缓冲区大小 msgflag:操作标志位。一般为0,即消息队列已满的时候,函数将会阻塞msgtype:请求读取的消息类型 从消息队列中根据消息类型得到对应消息成功返回0,失败返回-1
/*往消息队列写数据*/#define BUF_SIZE    256#define PROJ_ID     32#define PATH_NAME   "."int main(){    /*用户自定义消息缓冲*/    struct mymsgbuf    {        long msgtype;        char ctrlstring[BUF_SIZE];    }msgbuffer;    int qid;    int msglen;    key_t msgkey;    /*获取键值*/    if((msgkey = ftok(PATH_NAME,PROJ_ID)) == -1)    {        perror("ftok error!");        exit(1);    }    /*创建消息队列*/    if((qid = msgget(msgkey,IPC_CREAT | 0660)) == -1)    {        perror("msgget error!");        exit(1);    }    /*填充消息结构,发送到消息队列*/    msgbuffer.msgtype = 3;    strcpy(msgbuffer.ctrlstring,"hello,message queue!");    msglen = sizeof(struct mymsgbuf) - 4;    if((msgsnd(qid,&msgbuffer,msglen,0) == -1))    {        perror("msgsng error!\n");        exit(1);    }    return 0;}/*从刚才创建的消息队列中获取数据*/#define PATH_NAME   "."#define PROJ_ID     32#define BUF_SIZE    256int main(){    key_t key;  //键值    int qid;    //消息队列标识符    int msglen; //消息大小    struct mymsgbuf    {        long msgtype; //消息类型        char ctrlstring[BUF_SIZE]; //消息内容    }msgbuffer; //自定义消息缓冲区    /*获取键值*/    if((key = ftok(PATH_NAME,PROJ_ID)) == -1)    {        perror("ftok error!\n");        exit(1);    }    /*访问消息队列*/    if((qid = msgget(key,IPC_CREAT | 0660)) == -1)    {        perror("msgget error!\n");        exit(1);    }    /*读取消息队列内容*/    msglen = sizeof(struct mymsgbuf) - 4;    if(msgrcv(qid,&msgbuffer,msglen,3,0) == -1)    {        perror("msgrcv error!\n");        exit(1);    }    printf("Get message: %s\n",msgbuffer.ctrlstring);    return 0;}
你可能感兴趣的文章
在Eclipse中查看Android源码
查看>>
[转]C语言printf
查看>>
Mysql中下划线问题
查看>>
Xcode 11 报错,提示libstdc++.6 缺失,解决方案
查看>>
vue项目打包后无法运行报错空白页面
查看>>
1136 . 欧拉函数
查看>>
面试题:强制类型转换
查看>>
Decorator模式
查看>>
Template模式
查看>>
Observer模式
查看>>
高性能服务器设计
查看>>
图文介绍openLDAP在windows上的安装配置
查看>>
Pentaho BI开源报表系统
查看>>
Pentaho 开发: 在eclipse中构建Pentaho BI Server工程
查看>>
android中SharedPreferences的简单例子
查看>>
android中使用TextView来显示某个网址的内容,使用<ScrollView>来生成下拉列表框
查看>>
andorid里关于wifi的分析
查看>>
Hibernate和IBatis对比
查看>>
Spring MVC 教程,快速入门,深入分析
查看>>
Ubuntu Navicat for MySQL安装以及破解方案
查看>>