管道的用法

本文围绕Linux系统展开,介绍了fork用法,重点讲解进程间通信方式。阐述匿名管道,它是内存级文件,用于父子进程通信,是单向半双工模式,还介绍了pipe原理与使用。同时说明了命名管道,可用于不相关进程通信,介绍其创建、打开规则及使用示例,最后对比了两者区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、fork 的用法

fork 返回值 c++

在C++中,fork 是一个来自 Unix/Linux 系统的系统调用,用于创建一个与现有进程几乎完全相同的新进程。fork 的主要特点是它会返回两次,一次返回在父进程中,一次返回在子进程中。在父进程中,fork 返回新创建子进程的进程ID;而在子进程中,fork 返回0。如果fork失败,则在父进程中返回-1,并设置errno

以下是一个简单的C++示例,演示如何使用 fork

生成的结果如下:

二、什么是匿名管道 

匿名管道是linux中一种非常古老进程间通信方式,本质上就是一个内存级的文件。

一般用于父子进程间通信。概念上就是父进程与子进程共同使用一个管道文件来传输数据。

虽然父子进程都有对管道的读和写功能,但在使用时只能读或者写,因此管道是单向通信,半双工模式。

三.如何使用匿名管道

(一).pipe原理

参数是长度为2的整形数组,pipefd[0]代表读端文件描述符,pipefd[1]代表写端文件描述符。

返回值是int,创建成功返回0,失败返回-1,同时记录进errno。 

pipe的使用原理上,就是首先父进程创建一个管道文件,但同时赋予管道文件两个文件描述符。

一个是以读方式打开即pipefd[0],另一个是以写方式打开即pipefd[1]。

(二).pipe使用
以下面代码为例:

父进程使用write接口将字符串给管道,子进程从管道中接收字符串并打印。

同时,子进程的read系统接口会阻塞,直到父进程往管道中写完数据,read一次性将此时管道内数据读取完并清空管道。

当父进程关闭管道后,若管道中还有数据时read函数会一次性读取完并在下一次读取时返回0,没有数据时直接返回0。
 
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<unistd.h>
  4 #include<string>
  5 #include<assert.h>
  6 using namespace std;
  7 int main()
  8 {
  9   int pfd[2] = { 0 };
 10   int ret = pipe(pfd);
 11   assert(ret == 0);
 12   printf("main:%d\n",getpid());
 13   pid_t id = fork();
 14   assert(id >= 0);
 15  if (id < 0)
 16   {
 17         printf("Failed to fork\n");
 18         return -1;
 19   }
 20  else if(id == 0)
 21   {
 22          close(pfd[1]);
 23           //关闭写端
 24          char GetStr[1024] = { 0 };
 25           ssize_t i = read(pfd[0], GetStr, sizeof GetStr);//接收数据
 26          GetStr[i] = '\0';
 27          cout << GetStr << endl;
 28         printf("son:%d\n",getpid());
 29         //cout << getpid()<< endl;
 30          exit(0);
 31    }
 32   else
 33   {
 34         printf("parent:%d\n",getpid());
 35 
 36   }
 37      //父进程
 38   printf("main second:%d\n",getpid());
 39   close(pfd[0]);
 40   char str[1024] = "hello world";
 41   write(pfd[1], str, sizeof str);//发送数据
 42 
 43   return 0;
 44 }

输出结果如下:

四、命名管道 

1.什么是命名管道?

匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。

2.怎么创建命名管道(函数)?

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

mkfifo filename

命名管道也可以从程序里创建,相关函数有 :

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *filename,mode_t mode);

filename:命名管道的名字

mode:权限值

返回值:成功返回0,失败返回-1

3.命名管道的打开规则

如果当前打开操作是为读而打开FIFO时:

  • O_NONBLOCK (disable):阻塞直到有相应进程为写而打开该FIFO
  • O_NONBLOCK (enable):立刻返回成功

如果当前打开操作是为写而打开FIFO时 :

  • O_NONBLOCK (disable):阻塞直到有相应进程为读而打开该FIFO
  • O_NONBLOCK (enable):立刻返回失败,错误码为ENXIO

4.怎么使用命名管道?命名管道有什么用?(例子)

processA.cc
  1 #include "fifo.h"
  2 using namespace std;
  3 bool MakeFifo()
  4 {
  5     int n = mkfifo(FIFONAME, 0644);
  6     if(n < 0)
  7     {
  8         cout << "mkfifo fail" << endl;
  9         return false;
 10     }
 11     return true;
 12 }
 13 
 14 int main()
 15 {
 16     //创建命名管道
 17     if(!MakeFifo())
 18     {
 19         cerr << "mkfifo fail" << endl;
 20         return 1;
 21     }
 22     //打开fifo文件写
 23     int wfd = open(FIFONAME, O_WRONLY);
 24     if(wfd < 0)
 25     {
 26         cerr << "open fifo fail" << endl;
 27         return 2;
 28     }
 29     cout << "Process A is sending a message to process B" << endl;
 30     char message[] = "i am process A";
 31     write(wfd, message, sizeof(message));
 32 
 33     return 0;
 34 } 


  1 #include "fifo.h"
  2 using namespace std;
  3  
  4 int main()
  5 {
  6     //打开fifo文件写
  7     int rfd = open(FIFONAME, O_RDONLY);
  8     if(rfd < 0)
  9     {
 10         cerr << "open fifo fail" << endl;
 11         return 2;
 12     }
 13     cout << "receives messages from process A: ";
 14     char message[30];
 15     read(rfd, message, sizeof(message));
 16 
 17     cout << message << endl;
 18 
 19     return 0;
 20 }
 fifo.h

 1 #include <sys/types.h>
  2 #include <sys/stat.h>
  3 #include <iostream>
  4 #include <cassert>
  5 #include <fcntl.h>
  6 #include <unistd.h>
  7 
  8 #define FIFONAME  "fifo"

五、匿名管道和命名管道的区别  

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

### C语言中使用有名管道进行进程间通信的方法 有名管道(Named Pipe),也称为FIFO,是一种特殊的文件类型,用于实现无亲缘关系的进程之间的通信。它通过文件系统中的一个特殊文件来提供数据的单向流动[^3]。下面详细介绍其使用方法和实现步骤。 #### 1. 创建有名管道 有名管道可以通过 `mkfifo` 系统调用来创建。此函数需要两个参数:管道的路径名和权限模式。一旦创建成功,该有名管道会作为一个特殊文件出现在文件系统中。 示例代码: ```c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> int main() { // 创建名为 "test" 的有名管道 if (mkfifo("test", 0666) == -1) { perror("mkfifo"); exit(1); } printf("有名管道创建成功\n"); return 0; } ``` #### 2. 打开有名管道 有名管道可以像普通文件一样被打开。根据需求可以选择只读、只写或可读可写的方式。需要注意的是,以阻塞方式打开时,读端会等待写端的连接,反之亦然[^4]。 示例代码(写端程序): ```c #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { int fd = open("test", O_WRONLY); // 以只写方式打开有名管道 if (fd == -1) { perror("open"); exit(1); } const char *msg = "Hello from writer process"; write(fd, msg, strlen(msg) + 1); // 写入数据到有名管道 close(fd); return 0; } ``` #### 3. 数据传输 在有名管道中,数据的传输是通过 `read` 和 `write` 系统调用来完成的。当一个进程向有名管道写入数据后,另一个进程可以从该管道中读取这些数据[^3]。 示例代码(读端程序): ```c #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { int fd = open("test", O_RDONLY); // 以只读方式打开有名管道 if (fd == -1) { perror("open"); exit(1); } char buffer[1024]; read(fd, buffer, sizeof(buffer)); // 从有名管道读取数据 printf("收到的数据是:%s\n", buffer); close(fd); return 0; } ``` #### 4. 关闭有名管道 当通信完成后,应关闭文件描述符并删除有名管道文件,以释放资源。 示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main() { unlink("test"); // 删除有名管道文件 printf("有名管道已删除\n"); return 0; } ``` ### 注意事项 - **阻塞与非阻塞**:默认情况下,打开有名管道是阻塞的。如果希望以非阻塞方式打开,可以在 `open` 函数中加入 `O_NONBLOCK` 标志[^4]。 - **信号处理**:如果读进程退出,写进程尝试写入数据时会收到 `SIGPIPE` 信号。 - **权限控制**:创建有名管道时指定的权限模式会影响哪些进程可以访问该管道。 ### 总结 有名管道是一种简单而有效的进程间通信机制,尤其适用于没有亲缘关系的进程之间。通过创建、打开、读写和关闭有名管道,可以实现不同进程之间的数据交换[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值