关于建设 医院网站的请示,备案网站名称与实际网站名称不一致,江苏省两学一做网站,青岛企业网站制作公司Linux 中 pipe 的详细介绍
在 Linux 中#xff0c;pipe 是一个系统调用#xff0c;用于创建一个管道#xff0c;这是一种用于进程间通信#xff08;IPC#xff09;的机制。管道允许两个进程之间进行单向数据传输#xff0c;通常是一个进程向管道写入数据#xff0c;而另…Linux 中 pipe 的详细介绍
在 Linux 中pipe 是一个系统调用用于创建一个管道这是一种用于进程间通信IPC的机制。管道允许两个进程之间进行单向数据传输通常是一个进程向管道写入数据而另一个进程从管道读取数据。
管道的工作原理
当 pipe 调用成功时它会返回两个文件描述符分别对应管道的两端pipefd[0] 和 pipefd[1]。
pipefd[0] 通常用于读取数据。pipefd[1] 通常用于写入数据。
数据从 pipefd[1] 写入后会存储在内核缓冲区中直到被 pipefd[0] 的读取操作读取。
创建管道
使用 pipe 系统调用来创建管道
#include unistd.hint pipe(int pipefd[2]);如果 pipe 调用成功它将返回 0如果失败则返回 -1并且 errno 将被设置以指示错误。
管道的用法
以下是一个简单的例子展示了如何使用 pipe
#include stdio.h
#include unistd.h
#include sys/wait.hint main() {int pipefd[2];pid_t cpid;// 创建管道if (pipe(pipefd) -1) {perror(pipe);return 1;}// 创建子进程cpid fork();if (cpid -1) {perror(fork);close(pipefd[0]);close(pipefd[1]);return 1;}if (cpid 0) { // 子进程// 关闭不需要的文件描述符close(pipefd[1]);// 读取管道中的数据char message[20];read(pipefd[0], message, sizeof(message) - 1);printf(Received: %s\n, message);close(pipefd[0]);return 0;} else { // 父进程// 关闭不需要的文件描述符close(pipefd[0]);// 写入数据到管道const char *message Hello, this is the parent!;write(pipefd[1], message, strlen(message));close(pipefd[1]);// 等待子进程结束wait(NULL);return 0;}
}pipefd 的介绍
pipefd 是一个整数数组它包含两个元素用于存储管道的两个文件描述符。在创建管道后pipefd[0] 和 pipefd[1] 分别被赋予管道的读取和写入端。
pipefd[0]管道的读取端通常用于从管道中读取数据。pipefd[1]管道的写入端通常用于向管道中写入数据。
在上述例子中pipefd 被用作参数传递给 pipe 函数并在子进程中用于读取数据在父进程中用于写入数据。
注意事项
当一个进程关闭管道的任一端时另一个进程会收到一个 SIGPIPE 信号除非它已经设置了 SIG_IGN 或 SIG_ERR 处理程序。管道是单向的如果你需要双向通信需要创建两个管道。管道是阻塞的这意味着如果管道的另一端没有准备好读取或写入操作会阻塞直到条件满足。
总结
pipe 是 Linux 中实现进程间通信的一种简单而有效的方法。通过 pipefd可以访问管道的两个端点从而实现数据的双向传输。正确使用 pipe 和 pipefd 可以帮助开发者实现高效的进程间通信。 自己总结
pipe创建一个管道
pipe的介绍
1完成这件事
看图分析 运行结果 #includeiostream
#includeunistd.h
using namespace std;
int main()
{//创建管道//先创建一个pipefd数组int pipefd[2];//用n接受一下判断是否成功int n pipe(pipefd);if(n0) return 1;//创建失败了//创建成功//测试一下文件描述符是3和4coutpipefd[0]:pipefd[0]pipefd[1]:pipefd[1]endl;return 0;
} 2完成这件事 创建一个子进程 pid_t id fork();if(id 0)return 2;//创建失败if(id 0)//创建成功{//子进程}//父进程 让子进程写入父进程读取 要想让子进程进程写就需要在进程中关闭读端 if(id 0)//创建成功
{//子进程close(pipefd[0]);
}
同理 //父进程
close(pipefd[1]);
都用完结束后可以都关掉 if(id 0)//创建成功{//子进程close(pipefd[0]);//.....close(pipefd[1]);}//父进程close(pipefd[1]);//.....close(pipefd[0]);
IPC code写通信代码
3这件事也完成了 结构就有了 然后在pipefd[1]这个管道里写定义一个Writer函数 if(id 0)//创建成功{//子进程close(pipefd[0]);//.....IPC code写通信代码//在pipefd[1]这个管道里写Writer(pipefd[1]);close(pipefd[1]);exit(0);//正常退出}
同理父进程的 //父进程close(pipefd[1]);//.....IPC code写通信代码//在pipefd[0]这个管道里写Reader(pipefd[0]);close(pipefd[0]); //子进程
void Writer(int wfd)
{}
//父进程
void Reader(int rfd)
{}
Writer //子进程
void Writer(int wfd)
{string s hello,I am child;pid_t self getpid();int number 0;char buffer[10];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了}
} 用到snprintf 介绍
将s和self和number放进buffer char buffer[100];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了snprintf(buffer,sizeof(buffer),%s pid:%d\n,s.c_str(),self);cout buffer endl;sleep(1);};
用cout打印测试一下打印成功说明写入buffer成功了 等待进程少不了子进程exit后需要回收 //父进程close(pipefd[1]);//.....IPC code写通信代码//在pipefd[0]这个管道里写Reader(pipefd[0]);//等待进程缺少不了pid_t rid waitpid(id,nullptr,0);if(rid 0) return 3;//等待失败了close(pipefd[0]); 如何把消息发送/写入给父进程
用到了write 用write写入管道管道也是文件用strlen不用1不用管\0因为C语言规定\0结尾和文件没有关系wfd写入管道 //子进程
void Writer(int wfd)
{string s hello,I am child;pid_t self getpid();int number 0;char buffer[100];while(true){buffer[0] 0;//字符串清空只是为了提醒阅读代码的人我把这个数组当字符串了snprintf(buffer,sizeof(buffer),%s pid:%d %d\n,s.c_str(),self,number);//用write写入管道管道也是文件用strlen不用1不用管\0因为C语言规定\0结尾和文件没有关系wfd写入管道write(wfd,buffer,strlen(buffer));//cout buffer endl;sleep(1);};
}
父进程该怎么读取呢
用到了readfd是文件描述符从特定的文件描述符里读取放在这个buf里buf的长度是count 这里就需要考虑到\0因为buffer中需要\0 //父进程
void Reader(int rfd)
{char buffer[100];while(true){buffer[0] 0;//用sizeof是为了留个空间放\0ssize_t n read(rfd, buffer, sizeof(buffer));//sizeof!strlenif(n 0){//添加\0因为要放在buffer数组中读取buffer[n]0;cout father get a message[ getpid() ] buffer endl;}}
}
运行结果
也会发现为什么子进程sleep父进程不sleep父进程还是会跟着子进程sleep因为父子进程是要协同的 管道本质
通信是为了更好的发送变化的数据管道本质上是文件
所以必须要用到系统调用接口来访问管道其是由系统管理read和write
操作系统相当于中介
结论管道的特征
1具有血缘关系的进程进行进程间通信
2管道只能单向通信
3父子进程是会进程协同的同步与互斥的--保护管道文件的数据安全
4管道是面向字节流的
5管道是基于文件的而文件的生命周期是随进程的 再测试把子进程sleep去掉就是让子进程写快一点父进程sleep几秒就是让父进程读慢一点看有什么现象 管道的四种情况 测试管道大小
把c一直往管道里写把父进程中休眠50秒 结果差不多64kb 写端退了测试结果 结果是
读端正常读写端关闭读端就会读到0表明读到了文件pipe结尾不会被阻塞
read读取成功会返回读到的字符个数读到结尾返回0 读到结尾父进程也就可以停止读取了break后去把僵尸的子进程回收 break到这里 最后子进程会被waitpid回收 测试子进程一直写父进程读一会就退出
定义一个cnt控制退出的时间 这里也要修改一下加个sleep(5),观察close提前关闭 结果通过13号信号杀死 管道到的应用场景 都会变成一个进程 写一个进程池(pipe_use)
首先创建好文件 创建5个进程 channel通道的意思
cmdfd文件描述符
slaverid代表哪个子进程 把它放进vector容器里 思路步骤 管道创建
voidn假装使用一下要不然编译不过 创建父子进程 父进程写子进程读
子进程要读取就要关闭自己的写端父进程同理 子进程中的任务 子进程pid有了管道也有了就差在父进程添加字段了 先更改一下在class里构造一下 添加字段 测试一下结果文件描述符012是默认打开3是从管道里读4是写入管道 把初始化改造成函数 debug测试函数纯输入函数 第二步开始控制进程了想让子进程做什么 这里打印的rfd都是3正常吗文件描述符是可以被子进程继承的 父进程对应的写端拿到的是4-8子进程拿到的读端fd是3 改变一下直接从键盘0号描述符里读不从管道3里读了就没有管道的概念了slaver就不用传参了父进程通过管道写子进程通过标准输入读
用到了dup2将从pipefd[0]中读变成从0开始读 想让父进程固定的向管道里写入指定大小字节的内容必须读取四个字节四个字节四个字节的写和读这里的管道64kb
必须读取四个字节 如果父进程不给子进程发送数据呢阻塞等待! 开始控制子进程 生成一个随机数种子 可以随机选择任务和选择进程 cmd是任务码测试一下父进程控制子进程,父进程发送给子进程通过cmdcode连续 在Task.hpp里 要用到函数指针 main中的任务了就属于 再把任务装载进来 输出型参数用* 现在开始选择任务和进程 再把main中的任务弄成全局的 进行判断一下 测试 ,comcode和任创建的任务一致 这里的write是父进程进行写入向子进程发送子进程不得闲先写到管道里等得闲了再读 也可以轮询选择定义一个计数器弄再%等 整理一下控制代码这里是输入型参数只需要读 这样就可以轮询方式选择进程了不用随机了 结果 清理收尾
思路把所有文件的描述符都关掉 等待方式设置为0 read返回0就是失败了然后slaver就会调完 结束完就会exit直接退出 打印下更好显示 关闭文件描述符后sleep(10)秒 然后这10个子进程一瞬间都应该break然后最后到exit直接就退了10秒结束后父进程再回收他 测试时不弄死循环用cnt5秒后自动结束控制正常退出流程 测试结果 手动控制一下 定义一个select输入0就是退出了判断完后就走到了选择任务 然后直接把cmdcode改为选择的select-1是因为是从下标0开始的输入1就是0下标的 测试 bug的地方
这样会有一些bug一个子进程不是只有一个写端每一次子进程的创建都是有继承 这样会有一些bug一个子进程不是只有一个写端每一次子进程的创建都是有继承 按理说这样是对的可是这样就错了
因为下面两个红线还没有关掉它们进程了最开始的w 这样倒着回收是可以的 正确改法 修改一下 最后一个push_back的就都是父进程的写入fd 然后加一句这个红线的每创建子进程后都先把上一次父进程的读端fd关掉就可以了这里很妙因为vector一开始是空的 方便看 这里这样就可以了 管道已经完成 以上是匿名管道