(P24)管道:管道的读写规则
文章目录
- 1.管道大的读写规则
1.管道大的读写规则
当没有数据可读时
(1)O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来为止
(2)O_NONBLOCK enable:read调用返回01,errno值为EAGAIN管道写规则
(1)当管道满的时候,O_NONBLOCK enable:write 调用非阻塞,fd为非阻塞模式,则返回错误,错误码是EAGAIN;
(2)当管道满的时候,O_NONBLOCK disable:write 调用阻塞,fd为阻塞模式,write调用就会阻塞如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE
SIGPIPE信号会将当前进程终止
-当要写入的数据量小于等于PIPE_BUF,Linux将保证写入的原子性
原子性:假设A进程和B进程都要向管道写入数据,A进程写入的数据量小于等于PIPE_BUF,则A进程写入的数据是连续的,中间并不会插入B进程写入的数据,man 7 pipe看PIPE_BUF;否则多个进程往管道写入数据,可能会出现数据穿插的问题,进程A的写入的数据就不是连续的了,可能会夹杂着B的数据
当要写入的数据量大于PIPE_BUF,Linux将不再保证写入的原子性
PIPE_BUF=4K,在#include <linux/limits.h>
管道的大小是65536,64K,在redhat 9上面是4K,所以管道的容量不一定等于PIPE_BUFeg:P24pipe.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();if (pid == -1)ERR_EXIT("fork error");if (pid == 0){sleep(3);//模拟此时管道写操作没数据close(pipefd[0];write(pipefd[1], "hello", 5);close(pipefd[1]);ERR_EXIT(EXIT_SUCCESS);}close(pipefd[1]);char buf[10] = {0};read(pipefd[0], buf, 10);close(pipefd[1]);printf("buf = %s\n", buf);return 0;
}
- 测试:
管道数据为空,读操作会阻塞
- eg:P24pipe1.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();if (pid == -1)ERR_EXIT("fork error");if (pid == 0){sleep(3);//模拟此时管道写操作没数据close(pipefd[0];write(pipefd[1], "hello", 5);close(pipefd[1]);ERR_EXIT(EXIT_SUCCESS);}close(pipefd[1]);char buf[10] = {0};//将pipefd[0],设置为非阻塞模式int flags = fcntl(pipefd[0], F_GETTFL);fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK);int ret = read(pipefd[0], buf, 10);if (ret == -1)ERR_EXIT("read error");close(pipefd[1]);printf("buf = %s\n", buf);return 0;
}
- 测试:
- eg:P24pipe2.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();if (pid == -1)ERR_EXIT("fork error");if (pid == 0){//关闭子进程的写端close(pipefd[1]);ERR_EXIT(EXIT_SUCCESS);}//关闭父进程的写端close(pipefd[1]);sleep(1);char buf[10] = {0};int ret = read(pipefd[0]. buf, 10);printf("ret = %d \n", ret);return 0;
}
测试:
所有的写端(父,子进程)文件描述符都关闭了。任何(父进程,子进程)的读操作都是0,不是读失败了,而是表示读取到了文件的末尾
eg:P24pipe3.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)
void handler(int sig)
{printf("recv a sig=%d\n", sig);
}int main(int argc, char *argv[])
{signal(SIGPIPE, handler);int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();if (pid == -1)ERR_EXIT("fork error");if (pid == 0){close(pipefd[0]);ERR_EXIT(EXIT_SUCCESS);}close(pipefd[0]);sleep(1);char buf[10] = {0};int ret = write(pipefd[1], "hello", 10);if (ret == -1)printf("write error\n");return 0;
}
- 测试:
- eg:P24pipe4.c:阻塞模式
验证管道的内存缓冲区的大小
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)//测试管道的容量
int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");int ret;int count = 0;while (1){//管道模式是阻塞的ret = write(pipefd[1], "A", 1);if (ret == -1)break;count++;}return 0;
}
- 测试:
当管道满的时候,且fd是阻塞模式,ret操作就会阻塞
- eg:P24pipe5.c:非阻塞模式
验证管道的内存缓冲区的大小
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)//测试管道的容量
int main(int argc, char *argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");int ret;int count = 0;int flags = fcntl(pipefd[1], F_GETFL);fcntl(pipefd[1], F_SETFL, flags | O_NOBLOCK);while (1){ret = write(pipefd[1], "A", 1);if (ret == -1){printf("err = %s\n", strerror(errno));break;}count++;}printf("count=%d\n", count);return 0;
}
- 测试
将其改成非阻塞模式,管道的容量是64K(man 7 pipe中的Pipe Capacity也可以看到),
- eg:P24pipe6.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)//PIPE_BUF是4KB,用大于>4K 就行了,但是不容易测出穿插情况
//所以用了68K
//68KB
#define TEST_SIZE 68*1024int main(int argc, char *argv[])
{char a[TEST_SIZE];char b[TEST_SIZE];memset(a, 'a', sizeof(a));memset(b, 'b', sizeof(b));int pipefd[2];int ret = pipe(pipefd);if (ret == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();//子进程aif (pid == 0){close(pipefd[0]);ret = write(pipefd[1], a, sizeof(a));//往管道写入68K的数据printf("apid = %d write %d bytes to pipe\n", getpid(), ret);exit(0);}pid = fork();//子进程bif (pid == 0){close(pipefd[0]);ret = write(pipefd[1], b, sizeof(a));//往管道写入68K的数据printf("bpid = %d write %d bytes to pipe\n", getpid(), ret);exit(0);}//初始的父进程,接收进程a和进程b的数据close(pipefd[1]);sleep(1);int fd = open("test.txt", O_WRPNLY | O_CREAT| O_TRUNC, 0644);char buf[1024*4] = {0};int n = 1;while(1){ret = read(pipefd[0], buf, sizeof(buf));//当连个子进程写端没数据了,就会返回=0if (ret == 0)break;//打印输出最后一个字符:buf[4095]printf("n=%2d pid =%d read %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);write(fd, buf, ret);}return 0;
}
测试:
子进程a和子进程b都写入了68K数据;
子进程写完68K数据才返回;
子进程写完一部分数据,父进程就已经开始读了
读取了16次,就是64K;
可以看到穿插现象
Makefile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01pipe
all:$(BIN)
%.o:%.c$(CC) $(CFLAGS) -c $< -o $@
clean:rm -f *.o $(BIN)
(P24)管道:管道的读写规则相关推荐
- 进程返回linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF
题记:写这篇博客要主是加深自己对进程返回的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. 一. 当没有数据可读时 O_NONBLOCK disable:read调用阻塞,即进程暂停 ...
- linux系统编程之管道(二):管道读写规则和Pipe Capacity、PIPE_BUF
一. 当没有数据可读时 O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止. O_NONBLOCK enable:read调用返回-1,errno值为EAG ...
- java 管道流的读写操作 用于两个线程之间的通信
/* java 管道流的读写操作 用于两个线程之间 PipedOutputStream PipedInputStream 连接起来就是一个管道 管道输出流可以向管道写入数据 ...
- sql:命名管道管道程序_学习SQL:命名约定
sql:命名管道管道程序 A naming convention is a set of unwritten rules you should use if you want to increase ...
- 重叠IO--命名管道同时进行读写操作
弄了一整天了,总算初步完成以下功能: 创建一个命名管道作为服务器,以重叠IO的方式: 创建一个客户端,可同时启动多个客户端: 服务器线程中对接入的客户端进行同时读和写操作: 每个客户端对服务器在线程中 ...
- WCF中的管道——管道类型
管道是所有消息进出WCF应用程序的渠道.它的职责是以统一的方式编制和提供消息.管道中定义了传输.协议和消息拦截.管道以层级结构的形式汇总,就创建了一个管道栈.管道栈以分层的方式进行通信并处理消息.例如 ...
- Java 泛型的读写规则:PECS
PECS 是 "Producer Extends Consumer Super" 的缩写,是 Java 泛型中的重要用法. PECS 就是当你需要遍历某一个类型和子类的集合数据时, ...
- linux 管道文件上机总结,[转载]LINUX 管道 fifo 等总结
Linux进程通信:命名管道FIFO小结 Linux下进程之间通信可以用命名管道FIFO完成.命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在. 在程序 ...
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...
最新文章
- WMI技术介绍和应用——事件通知
- 超硬核的 Python 数据可视化教程
- A Network-based End-to-End Trainable Task-oriented Dialogue System
- PL0编译器TurboPascal版再现时间:2009-07-20 17:24:49来源:网络 作者:未知 点击:52次
- mpvue还在维护吗_mpvue 微信小程序开发之生命周期
- java修饰符总结,java访问修饰符总结
- 阿里云云计算ACP考试知识点(标红为重点)
- bzoj1925地精部落——数学
- java date只保留年月日_java.util.Date、java.sql.Date、java.sql.Timestamp区别和总结
- Nodejs中Mongodb的基本使用
- C程序设计(第四版)谭浩强著-学习笔记
- 零基础在Linux环境安装Cadence系列软件
- ios safari 模拟器_iOS 模拟器调试大法了解一下?
- 渥太华大学计算机科学,【加拿大渥太华大学计算机科学排名第四】渥太华大学录取条件...
- python使用126发邮件代码
- python发邮件被认定为垃圾邮件_Python:脚本发送的邮件被Gmail标记为垃圾邮件
- 【MATLAB图像处理】图像复原
- 用Pandas秒秒钟搞定24张Excel报表,还做了波投放分析!
- Android 深入理解 ANR 触发原理:Service
- 塞班 s60v5 开发