概述
关于Linux管道的简单说明、使用、编码
1. 管道的特点
管道是指用于连接读进程和写进程,以实现进程间通信的共享文件。也就是说,两个进程一个以写的方式打开一个文件,向里面写数据,一个以读的方式打开同一个文件,从里面读另一个进程写入的数据,管道通信方式说白了就是通过读写同一个文件来实现数据传递。
序号 | 特点 |
---|---|
1 | 管道中的数据遵守先入先出的顺序 |
2 | 管道一般是单向的 |
3 | 写入数据的进程无法对写入的数据进行读取 |
4 | 每个数据只能被读取数据的进程读一次 |
5 | Linux的管道文件大小是固定的: 4KB |
2. 管道读写进程间的同步机制
序号 | 处理方式 |
---|---|
1 | 写通道如果已满,那么继续写入数据的进程将会被阻塞,直到管道中的数据被读走 |
2 | 如果管道中没有数据,那么从管道中读取数据的进程将会被阻塞,直到有数据写入 |
3 | 如果一个进程以读的方式打开一个管道,但是没有另一个进程以写的方式打开一个管道,那么该进程阻塞 |
4 | 如果一个进程试图对没有读进程的管道进行写操作,那么该进程将被终止 |
3. 管道的局限性
序号 | 局限 |
---|---|
1 | 数据自己读,却不能自己写;数据自己写,却不能自己读。 |
2 | 数据一旦被读取,就不会存在于管道中,不能反复进行读取 |
3 | 数据只能在一个方向流动 |
4 | 只能在公共祖先的进程间使用管道 |
4. 与管道相关的系统调用
序号 | 系统调用方式 |
---|---|
1 | 创建管道 |
2 | 读管道 |
3 | 写管道 |
4 | 等待子进程中断或结束 |
4.1 创建管道pipe
int pipe(int filedes[2]);
// filedes表示文件描述符,[0]位表示管道读端,[1]表示管道写端
// 函数调用成功返回0
// 管道创建时默认打开了文件描述符,而且默认是以阻塞模式打开的
4.2 读管道read
// 读管道的特点
// 1.所有的读操作总是从管道当前位置开始读,不支持文件指针的移动
// 2.如果管道没有被其他进程以写的方式打开,那么read()系统调用将返回0
// 3.空管道的情况下,read()系统调用将会被阻塞
4.3 写管道write
// 写通道的特点
// 1.每一次的写请求总是附加在管道的末端
// 2.当有多个对同一个管道的写请求发生时,系统保证小于或者等于4KB大小的写请求操作不会交叉进行
// 3.如果试图对一个没有被任何进程以读方式打开的通道进行写操作,则将会产生SIGPIPE信号
4.4 等待子进程中断或者结束的函数wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * status);
// 进程一旦调用的wait,就立刻阻塞自己,由wait自动分析当前进程的某个子进程是否已经退出,如果找到一个已经变成了僵尸进程,wait就会将之彻底销毁后返回;如果
// 没有找到这样一个进程,就一直等这种进程出现。
// status:子进程结束状态指值,如果不在意状态,可以设置status为NULL
// 执行成功的话,会返回子进程pid,否则返回-1。
5. 示例程序
// 管道的使用方法:
// 1.主进程创建管道
// 2.读通道的进程关闭管道文件写描述符
// 3.写通道的进程关闭管道文件读描述符
// 4.进程向通道读/写数据
// 接下来的实例程序展示了两种情况:
// 1.主进程写,子进程读
// 2.主进程写,子进程写
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <memory.h>
int ChildPid1(int fd[2]); // 子进程1
int ChildPid2(int fd[2]); // 子进程2
int MainPid(int fd[2]);
// 主进程
int main()
{
pid_t pidtChild1 = 0, pidtChild2 = 0;
int arrPipeChild1[2] = {0}; // 主进程与子进程1的管道
int i32Ret = pipe(arrPipeChild1); // 创建管道
if (0 != i32Ret)
{
std::cout << "管道创建失败" << std::endl;
return EXIT_FAILURE;
}
if (0 == (pidtChild1 = fork()))
{
// 创建第一个子进程,第一个子进程为读进程,从管道中读取两种内容,第一种内容打印,第二种内容执行退出操作
i32Ret = ChildPid1(arrPipeChild1);
if (0 == i32Ret)
{
std::cout << "子进程1正常退出" << std::endl;
return EXIT_SUCCESS;
}
else
{
std::cout << "子进程1异常退出" << std::endl;
return EXIT_FAILURE;
}
}
else if (0 < pidtChild1)
{
i32Ret = MainPid(arrPipeChild1);
if (0 == i32Ret)
{
std::cout << "主进程正常退出" << std::endl;
wait(NULL); // 等待子进程1退出
}
}
else
{
std::cout << "子进程1创建失败" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int ChildPid1(int fd[2]) // 子进程1
{
int i32Ret = 0;
char buff[1024]; // 用于从主进程读
close(fd[1]); // 关闭子进程1的写端口
while(1)
{
// 从管道中读取数据
memset(buff, 0, sizeof(buff));
i32Ret = read(fd[0], buff, 1024); // 从通道中读
if (-1 == i32Ret)
{
std::cout << "子进程1读失败" << std::endl;
}
char arrEndFlag[1024] = "end"; // 结束标志
if (0 == memcmp(buff, arrEndFlag, 3))
{
// 查询是否是结束标志
std::cout << "子进程1收到结束标志" << std::endl;
return EXIT_SUCCESS;
}
else
{
std::cout << "子进程1收到" << buff << std::endl;
}
}
return EXIT_SUCCESS;
}
int ChildPid2(int fd[2]) // 子进程2
{
close(fd[0]); // 关闭读通道
int i32Ret = 0;
int arrSend[1024] = {0};
int i32SendEndFlag = 12345;
for (int i = 0; i < 1024; i++)
{
arrSend[i] = i;
}
write(fd[1], arrSend, sizeof(arrSend));
std::cout << "子进程2的写数据发送成功" << std::endl;
write(fd[1], &i32SendEndFlag, sizeof(i32SendEndFlag));
std::cout << "子进程2的写结束标志发送成功" << std::endl;
return EXIT_SUCCESS;
}
int MainPid(int fd[2]) // 主进程
{
close(fd[0]); // 关闭读端口
int i32Ret = 0; // 返回值
char arrWriteData[1024] = {'a'}; // 准备写入的数据
char arrEndFlag[1024] = "end"; // 写给子进程1的结束标志
sleep(1);
i32Ret = write(fd[1], arrWriteData, sizeof(arrWriteData));
if (-1 == i32Ret)
{
std::cout << "主进程向子进程1发送数据失败" << std::endl;
}
else
{
std::cout << "主进程向子进程1发送" << i32Ret << "个字节" << std::endl;
}
for (int i = 0; i < 10; i++)
{
arrWriteData[i] = 'a';
}
i32Ret = write(fd[1], arrWriteData, sizeof(arrWriteData));
if (-1 == i32Ret)
{
std::cout << "主进程向子进程1发送数据失败" << std::endl;
}
else
{
std::cout << "主进程向子进程1发送" << i32Ret << "个字节" << std::endl;
}
i32Ret = write(fd[1], arrEndFlag, sizeof(arrEndFlag));
if (-1 == i32Ret)
{
std::cout << "主进程向子进程1发送管道通信结束标志" << std::endl;
}
// 向第二个子进程进行管道通信
pid_t pidtChild2 = 0;
int arrFd[2] = {0};
i32Ret = pipe(arrFd);
if (0 != i32Ret)
{
std::cout << "与子进程2的通道创建失败" << std::endl;
}
if (0 == (pidtChild2 = fork()))
{
i32Ret = ChildPid2(arrFd);
if (EXIT_SUCCESS == i32Ret)
{
std::cout << "子进程2正常退出" << std::endl;
return EXIT_SUCCESS;
}
else
{
std::cout << "子进程2异常退出" << std::endl;
return EXIT_FAILURE;
}
}
else if (0 < pidtChild2)
{
close(arrFd[1]); // 主进程为读进程
int arrRev[1024] = {0};
sleep(5); // 现睡眠一段,等子进程写满了再读
while(1)
{
memset(arrRev, 0, sizeof(arrRev));
i32Ret = read(arrFd[0], arrRev, sizeof(arrRev));
if (-1 == i32Ret)
{
std::cout << "主进程读失败" << std::endl;
wait(NULL);
return EXIT_FAILURE;
}
if (12345 == arrRev[0])
{
std::cout << "读结束" << std::endl;
wait(NULL);
break;
}
else
{
for(int i = 0; i < 1024; i++)
{
std::cout << arrRev[i];
}
std::cout << std::endl;
}
}
}
else
{
std::cout << "子进程2创建失败" << std::endl;
}
return EXIT_SUCCESS;
}
最后
以上就是高高墨镜为你收集整理的关于Linux管道的简单说明、使用、编码 关于Linux管道的简单说明、使用、编码 的全部内容,希望文章能够帮你解决关于Linux管道的简单说明、使用、编码 关于Linux管道的简单说明、使用、编码 所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复