文章目录

  • 阻塞和非阻塞概念
  • 通过fcntl函数设置文件的阻塞特性
  • 文件描述符概述
  • 文件描述符的复制
  • 有名管道
    • 有名管道的创建
    • 有名管道的基本读写操作
    • 有名管道实现进程间通信
    • 有名管道的读写规律(阻塞)

阻塞和非阻塞概念

如果是阻塞,管道中没有数据,read会一直等待,直到有数据才会继续运行,否则一 直等待。
如果是非阻塞,read函数运行时,会先看一下管道中是否有数据,如果有数据,则正常运行读取数据,如果管道中没有数据,则read函数会立即返回,继续下面的代码运行。

通过fcntl函数设置文件的阻塞特性

功能 函数
设置为阻塞: fcntl(fd, F_SETFL, 0);
设置为非阻塞: fcntl(fd, F_SETFL, O_NONBLOCK);
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{int fd_pipe[2];char buf[] = "hello world";pid_t pid;if (pipe(fd_pipe) < 0){perror("fail to pipe");exit(1);}pid = fork();if (pid < 0){perror("fail to fork");exit(0);}if (pid == 0){while(1){sleep(5);write(fd_pipe[1], buf, strlen(buf));}}else{//将fd_pipe[0]设置为阻塞//fcntl(fd_pipe[0], F_SETFL, 0);//将fd_pipe[0]设置为非阻塞fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK);while(1){memset(buf, 0, sizeof(buf));read(fd_pipe[0], buf, sizeof(buf));printf("buf=[%s]\n", buf);sleep(1);}}return 0;
}

文件描述符概述

文件描述符是非负整数,是文件的标识。 用户使用文件描述符(file descriptor)来访问文件。
利用open打开一个文件时,内核会返回一个文件描述符。 每个进程都有一张文件描述符的表,进程刚被创建时,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符0、1、2 记录在表中。
在进程中打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件 描述符记录在表中。
注意: Linux中一个进程最多只能打开NR_OPEN_DEFAULT (即1024)个文件,故当文件不再使用时应及时调用close函数关闭文件。
例子

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{//在进程中打开其他文件时,//系统会返回文件描述符表中最小可用的文件描述符,//并将此文件描述符记录在进程的文件描述符表中。close(0);int fd1, fd2, fd3;fd1 = open("file.txt", O_RDONLY | O_CREAT, 0664);fd2 = open("file.txt", O_RDONLY | O_CREAT, 0664);fd3 = open("file.txt", O_RDONLY | O_CREAT, 0664);printf("fd1 = %d\n", fd1);printf("fd2 = %d\n", fd2);printf("fd3 = %d\n", fd3);return 0;
}

文件描述符的复制

dup函数

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(void)
{//通过dup函数复制一个文件描述符int fd;//dup执行后给返回值文件描述符分配的值是文件描述符表中最小可用的文件描述符fd = dup(1);printf("fd = %d\n", fd);//由于通过dup函数将1这个文件描述符复制了一份为fd,所以fd现在就相当于1,所以写数据就是想终端写入数据write(fd, "nihao beijing\n", strlen("nihao beijing\n"));return 0;
}


实现输出重定向的功能

如果需要实现输出重定向的功能
首先像printf函数是操作文件描述符1所对应的文件,默认是操作终端,只要能够把1对应标识的文件改变,
就可以实现输出重定向。所以实现创建好文件对应的文件描述符之后,将1文件描述符关闭,接着通过dup
函数复制的新的文件描述符就是1,这样printf函数对1操作,就写到了文件中。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{//如果需要实现输出重定向的功能//首先像printf函数是操作文件描述符1所对应的文件,//默认是操作终端,只要能够把1对应标识的文件改变,就可以实现输出重定向//所以实现创建好文件对应的文件描述符之后,将1文件描述符关闭,接着通过dup//函数复制的新的文件描述符就是1,这样printf函数对1操作,就写到了文件中int fd_file;fd_file = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);if(fd_file == -1){perror("fail to open");exit(1);}close(1);int fd = dup(fd_file);printf("hello world\n");printf("fd = %d\n", fd);return 0;
}


实现输出重定向后,还想标准输出

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{int fd1;int fd2;fd2 = dup(1);printf("new:fd2 = %d\n",fd2);fd1 = open("test.txt", O_RDWR | O_CREAT, 0664);close(1);int fd3 = dup(fd1);printf("hello world\n");printf("fd = %d\n", fd3);close(1);int fd4 = dup(fd2);printf("nihao beijing\n");printf("fd = %d\n", fd4);return 0;
}

有名管道

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同, 其特点是:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、写入FIFO中的数据遵循先入先出的规则。
3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格 式,如多少字节算一个消息等。
4、FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,所以有名管道可 以实现不相关进程间通信,但FIFO中的内容却存放在内存中。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更 多的数据。
7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。

有名管道的创建

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
//有名管道创建
int main()
{if((mkfifo("./file",0600)==-1)&&errno!=EEXIST){printf("mkfifo failued\n");perror("why");}else if(errno==EEXIST){printf("file exist\n");}else{printf("suscceess\n");}return 0;
}

有名管道的基本读写操作

由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道进行操作, 但是不能使用lseek修改管道文件的偏移量。
注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的 操作实质就是对内核空间的操作。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define FIFONAME "fifo_file"
int main(int argc, char const *argv[])
{//通过mkfifo函数创建有名管道if(mkfifo(FIFONAME, 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//对有名管道进行操作//管道后写入的数据会保存在之前写入数据的后面,不会替换//如果管道中没有数据了,读操作会阻塞//通过open函数打开管道文件并得到文件描述符int fd;fd = open(FIFONAME, O_RDWR);if(fd == -1){perror("fail to open");exit(1);}//通过write函数向管道中写入数据if(write(fd, "hello world", strlen("hello world")) == -1){perror("fail to write");exit(1);}write(fd, "nihao beijing", strlen("nihao beijing"));//通过read函数读取管道中的数据char buf[32] = "";if(read(fd, buf, sizeof(buf)) == -1){perror("fail to read");exit(1);}printf("buf = [%s]\n", buf);if(read(fd, buf, sizeof(buf)) == -1){perror("fail to read");exit(1);}printf("buf = [%s]\n", buf);//使用close函数关闭文件描述符close(fd);return 0;
}

有名管道实现进程间通信

由于有名管道在本地创建了一个管道文件,所以不相关的进程间也可以实现通信。
send

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{//如果没有创建有名管道,则创建有名管道//为了实现两个进程都可以收发数据,所以需要创建两个有名管道if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//打开两个有名管道并得到文件描述符int fd_w, fd_r;if((fd_w = open("myfifo1", O_WRONLY)) == -1){perror("fail to open");exit(1);}if((fd_r = open("myfifo2", O_RDONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";ssize_t bytes;while(1){fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';//send进程负责将数据写入myfifo1,接着从myfifo2中读取数据if((bytes = write(fd_w, buf, sizeof(buf))) == -1){perror("fail to write");exit(1);}if((bytes = read(fd_r, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("from recv: %s\n", buf);}return 0;
}

** recv**

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{//如果没有创建有名管道,则创建有名管道 //为了实现两个进程都可以收发数据,所以需要创建两个有名管道if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//打开两个有名管道并得到文件描述符int fd_w, fd_r;if((fd_r = open("myfifo1", O_RDONLY)) == -1){perror("fail to open");exit(1);}if((fd_w = open("myfifo2", O_WRONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";ssize_t bytes;while(1){if((bytes = read(fd_r, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("from send: %s\n", buf);fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';write(fd_w, buf, sizeof(buf));}return 0;
}

有名管道的读写规律(阻塞)

读写端都存在,只读不写
如果原本管道中有数据,则正常读取。
如果管道中没有数据,则read函数会阻塞等待。
读写端都存在,只写不读
当有名管道的缓冲区写满后,write函数会发生阻塞。
默认有名管道的缓冲区为64K字节。
在一个进程中,只有读端,没有写端
在一个进程中,只有读端,没有写端。
会在open函数的位置阻塞。
在一个进程中,只有写端,没有读端
在一个进程中,只有写端,没有读端。
会在open函数的位置阻塞。
一个进程只读,一个进程只写
只要保证有名管道的读写端都存在,不管是几个进程,都不会再open这阻塞了。
如果一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0。
如果一个进程只读,一个进程只写,都运行后,如果关闭读端,写端会立即产生 SIGPIPE信号,默认的处理方式是退出进程。

linux进程间通讯-有名管道相关推荐

  1. linux命名管道进程间通信,Linux进程间通讯--命名管道

    IPC安全 前面总结了匿名管道,如今来看命名管道:因为匿名管道的一个限制就是:只能是有血缘关系的进程间才能够通讯,好比:有两个同祖先的子进程,父子进程等:为了突破这一个限制,想让没有任何关系的两个进程 ...

  2. linux进程间通讯-无名管道

    文章目录 无名管道 无名管道的创建 -- pipe函数 无名管道的读写规律 无名管道 无名管道概述 管道(pipe)又称无名管道. 无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符.任 ...

  3. linux无名管道实验代码,Linux 进程间通讯之创建无名管道和读写无名管道

    Linux进程间通讯的方式: 1. 管道(pipe)和有名管道(FIFO). 2. 信号(signal) 3. 消息队列 4. 共享内存 5. 信号量 6. 套接字(socket) 管道通讯: 无名管 ...

  4. 【Linux】进程间通讯之管道

    进程间通信的机制包括:管道.信号量.共享内存.消息队列. 这篇博客主要介绍的是进程间通讯之管道的应用 一.管道的分类 管道都属于半双工通讯机制 管道分为有名管道和无名管道 1.有名管道 在磁盘上有一个 ...

  5. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  6. Linux 进程间通讯方式 pipe()函数

    Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...

  7. linux进程间通讯的几种方式的特点和优缺点,和适用场合。

    http://blog.csdn.net/kakaka2011/article/details/6636661 1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有 ...

  8. Linux 进程间通讯详解一

    进程间的通讯 两台主机间的进程通讯 --socket一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量, ...

  9. Linux进程间通讯

    最初Unix IPC包括:管道.FIFO.信号: System V IPC包括:System V消息队列.System V信号灯.System V共享内存区: Posix IPC包括: Posix消息 ...

最新文章

  1. 【五线谱】符干朝向与连音线 ( 符干朝向 | 第三线以下符干朝上 | 第三线以上符干朝下 | 连音线 )
  2. NSIS 的 Modern UI 教程(二)
  3. 人月神话贯彻执行_上古神话知识梳理,精华帖
  4. 深入理解 Angular 变化检测(change detection)
  5. 如何使用 kubectl 通过命令行的方式操作 SAP Kyma
  6. java file ip_java常用工具类 IP、File文件工具类
  7. Asp.Net Boilerplate微服务实战(二)架构解析
  8. Spark的枚举类型实例!scala的枚举。
  9. mysql创建表格1warning_MySQL 复制表
  10. recvfrom函数 非阻塞_那些年让你迷惑的阻塞、非阻塞、异步、同步
  11. 数据库期末总结笔记( 零基础 )--数据库安全性与完整性-范式-E-R图
  12. HDU 3400 Line belt (三分)
  13. R_ggplot2地理信息可视化_史上最全(二)
  14. 算法导论18.1-4
  15. navicat编程界面_主界面初识 Navicat Premium使用技巧(一)
  16. Unity3D-----简易游戏项目开发01
  17. mysql 上传rar文件大小_js文件上传 自定义压缩文件和文件格式及大小限制
  18. 110115-07-6,Ac-LLM-CHOCalpain 抑制剂
  19. FusionCharts参数中文说明
  20. 【XSY2689】王子 - 网络流

热门文章

  1. linux 删除mysql
  2. java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 解决方法 java.lang.ClassNotFoundException: com.
  3. javaScript中的提示对话框
  4. Anaconda | conda常用命令
  5. Android源码下载总结
  6. npj Microbiomes|细菌群落的整体涌现特性诱导了拟南芥的干旱抗性(一作解读)
  7. 海南大学植物保护学院刘铜教授课题组招聘简介
  8. Science:科学家亲眼看到细菌产生耐药性的全过程(视频)
  9. PNAS-皮肤微生物群综合分析揭示人类皮肤的独特性并为其在哺乳动物的系统发育共生现象提供证据
  10. 如何入门生信Linux