进程间通信(1)--管道
来源:互联网 发布:js concat连接字符串 编辑:程序博客网 时间:2024/06/05 09:41
一、什么是管道
Linux下一切皆文件,我们可以创建一个管道文件进行通信,实际上是调用pipe函数在内核中开辟一块缓冲区(称为管道)用于通信,管道是一种最基本的IPC机制,由pipe函数创建 #include <unistd.h>
int pipe (int filedes[2]);
它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调⽤用成功返回0,调⽤用失败返回-1。
管道的种类
<1>普通管道:pipe也叫匿名管道
a.单向通信
b.只有在具有亲缘关系的进程间通信
c.具有同步机制
d.它是一种面向字节流的通信服务
e.生命周期随进程
<2>流管道:s_pipe
可以进行双向传输,其他同上
<3>命名管道:name_pipe
可以使毫不相干的进程进行通信,其他同匿名管道
二、使用管道进行通信
单向通信
#include <stdio.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/wait.h>int main(){ int _pipe[2]; int ret = pipe(_pipe); if(ret == -1){ printf("creat pipe error!errno code is:%d\n",errno); return 1; } pid_t id = fork(); if(id < 0) { printf("fork error!"); return 2; }else if(id==0){//child close(_pipe[0]);//关掉读 int i = 0; char* _mesg_c = NULL; while(i<20){ if(i<10){ _mesg_c = " i am child!"; write(_pipe[1], _mesg_c,strlen(_mesg_c)+1);//写入 } sleep(1); i++; } // close(_pipe[1]); }else{//father close(_pipe[1]);//关掉写 char _mesg[100]; int j = 0; while(j<3) { memset(_mesg,'\0',sizeof(_mesg)); int ret = read(_pipe[0],_mesg,sizeof(_mesg));//读出 printf("%s:code is %d\n",_mesg,ret); j++; } close(_pipe[0]); sleep(10); if(waitpid(id,NULL,0)< 0) { return 3; } } return 0;}
运行结果:
分析:父子进程进行单向通信,子进程写,父进程读
使用管道需要注意以下四种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志)
1. 如果所有指向管道写端的文件描述符都关闭了,(管道写端的引用计数为0),而仍然有进程从管道的读端读取数据,那么管道中剩余的数据都被读取之后,再次read将会返回0,就像读到文件结尾一样。也就是说,写端不会写,读端读完之后就会再等着写端去写,但是写端关闭了啊,不会写了,所以就出现上面说的情况。这就体现出了管道的同步机制。
如果有指向管道写端的文件描述符没有关闭,(管道写端的引用计数大于0)而持有管道写端的进程也没有向管道中写数据,这时有进程管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。通俗讲就是,读端读数据,一直读,但是写端不写了,而且写端并没有关闭,所以这时读端就会一直等着写端去写。这就造成了阻塞式等待。
如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数为0),这时有进程向管道的写端写数据,那么该进程会收到SIGPIPE,通常会导致进程异常终止。所以进程就会异常退出了。
如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0)而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道写端写数据,那么在管道写满时再写将会阻塞,直到管道中有了空位置才写入并返回,也就是管道的同步机制。
双向通信
命名管道:
命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。而且,FIFO总是按照先进先出的原则工作,第一个被写入的数据首先从管道中读出。
创建:
#include<sys/types.h>#include<sys/stat.h>int mknod(const char*path,mode_t mod,dev_t dev);int mkfifo(const char*path,mode_t mode);参数:path为创建的命名管道的路径名,mod为创建命名管道的模式,指明其存取权限,dev为设备值,该值文件创建的种类,它只在创建设备文件时才会用到。这两个函数掉用成功返回0,失败都返回-1.
命名管道实现通信的代码:
这里写代码片.PHONY:allall:server clientclient:client.c gcc -o $@ $^server:server.c gcc -o $@ $^.PHONY:cleanclean: rm -f server client
comm.h
#ifndef __COMM_H_#define __COMM_H_#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#define ONEPATH "./fifo_one.c"#define TWOPATH "./fifo_two.c"#define SIZE 1000#endif
client.c
#include "comm.h"int main(){ int _ret = mkfifo(ONEPATH,S_IFIFO|0666); if(_ret == -1) { printf("mkfifo ONEPATH error\n"); return 1; } int fdone = open(ONEPATH,O_WRONLY); int fdtwo = open(TWOPATH,O_RDONLY); if((fdone<0)||(fdtwo<0)) { printf("open file error\n"); return 1; } char buf[SIZE]; memset(buf,'\0',sizeof(buf)); while(1) { printf("guangyuan->>>"); scanf("%s,buf"); int retone = write(fdone,buf,strlen(buf)+1); if(retone<0) { printf("write error\n"); break; } printf("guangyuan<<<-"); int rettwo = read(fdtwo,buf,sizeof(buf)); if(rettwo<0) { printf("write error\n"); break; } printf("%s\n",buf); } close(fdone); close(fdtwo); return 0;}
server.c
#include "comm.h"int main(){ int ret = mkfifo(TWOPATH,S_IFIFO|0666); if(ret == -1) { printf("mkfifo TWOPATH error\n"); return 1; } int fdone = open(ONEPATH,O_RDONLY); int fdtwo = open(TWOPATH,O_WRONLY); if((fdone<0)||(fdtwo<0)) { printf("open file error\n"); return 1; } char buf[SIZE]; memset(buf,'\0',sizeof(buf)); while(1) { printf("mengnan<<<-"); int retone = read(fdone,buf,sizeof(buf)); if(retone<0) { printf("read error\n"); break; } printf("%s\n",buf); printf("megnnan->>>"); scanf("%s",buf); int rettwo = write(fdtwo,buf,strlen(buf)+1); if(rettwo<0) { printf("write error\n"); break; } } close(fdone); close(fdtwo); return 0;}
结果:
注意
命名管道的使用和匿名管道基本相同,只是在使用命名管道之前首先要使用open函数打开,因为命名管道是存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
需要注意,使用open的几点:
1. 调用open()打开命名管道可能会被阻塞,但是如果同时用读写方式(O_RDWR)打开,则一定不会造成阻塞。
2. 如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写才能打开管道。
3. 同样,以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。
- 进程间通信方式(1)---管道
- 进程间通信(1)--管道
- 进程间通信(1):匿名管道
- 进程间通信(1)-有名管道
- 进程间通信(IPC)1 ------ 管道
- 进程间通信(1)---匿名管道与命名管道
- 进程间通信(管道)
- 进程间通信-管道(有名管道和无名管道)
- 进程间通信之管道通信(匿名管道)
- 进程间管道通信
- 进程间通信: 管道
- 进程间通信--管道
- 进程间通信----管道
- 进程间通信--管道
- 进程间通信--管道
- 进程间通信----管道
- 进程间通信----管道
- 进程间通信--管道
- javascript切换搜索引擎的导航网页搜索框
- CSS中实现左边固定,右边自适应
- Android开发文件压缩与解压
- C++ 13 —— 多重继承
- Map 综述 彻头彻尾理解 HashMap
- 进程间通信(1)--管道
- openfire服务端消息回执插件(接收方离线时的情况),判断用户的在线状态
- 网络请求框架OkHttp基础用法
- 我的javaweb之路
- 如何用android:layout_weight弄个满意的比例
- jmeter文件上传下载测试
- Android的App Shortcut功能
- activemq--MASTER SLAVE+BROKER CLUSTER 实践(二)
- Android ViewPager使用