进程通信(一)无名管道和有名管道

进程通信是指不同进程之间进行信息交换的过程。

《王道考研复习指导》提到,管道通信是一种特殊的消息传递方式。所谓的“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的共享文件,也称为pipe文件。发送进程(即写进程)通过字符流形式将大量数据写入管道(共享文件),而接收进程(即读进程)则从管道中读取数据。为了确保通信的顺畅,管道机制需要提供三种协调能力:互斥、同步和确认对方存在。接下来以Linux中的管道为例进行说明。在Linux系统中,管道是一种常用的通信机制。尽管管道本质上也是一种文件,但它与普通文件不同,管道能够克服使用文件通信的两个问题,具体表现为:1)管道大小受限。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区大小为4KB,这使得它不会像文件那样无限制增长。使用单一固定缓冲区可能会导致问题,如在写入管道时可能会填满缓冲区,发生这种情况时,write()调用将默认阻塞,等待某些数据被读取以释放空间。2)读进程可能比写进程运行得快。当所有当前数据已被读取时,管道变为空。在这种情况下,read()调用将默认设置为阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。注意:从管道读取数据是一次性操作,数据一旦被读取就会被丢弃,释放空间以便写入更多数据。管道只能进行半双工通信,即在某一时刻只能单向传输。若要实现父子进程之间的双向互动,需要使用两个管道。

在Linux操作系统下,可以使用ulimit -p命令查看默认管道的大小。

1.无名管道(匿名管道)

函数原型:int pipe(int pipefd[2])

参数:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端。即管道的两端,注意fd是传出参数。当程序调用pipe函数时,操作系统会创建内核缓冲区,fd作为传出参数以便操作内核缓冲区。

返回值:如果匿名缓冲区创建成功,返回值为0;否则,返回-1,并设置errno全局变量为相应的错误。

适用范围:有血缘关系的进程间通信,如父子进程、兄弟进程。

下面举例说明如何使用无名管道在父子进程间进行通信:

进程通信(一)无名管道和有名管道前文已提到,由于管道是半双工通信,即在某一时刻只能单向传输。

代码语言:C

#include#include#include#include#include

int main(){int fd[2]; //创建文件描述符数组,fd[0]对应管道读端,fd[1]对应管道写端int res = pipe(fd); //fd作为传出参数,以便对内核缓冲区进行操作assert(-1 != res);

pid_t pid = fork(); //创建子进程if(-1 == pid){    perror("fork()");    exit(0);}//子进程if(pid == 0){    close(fd[0]); //关闭读端    write(fd[1],"hello",5); //向内核缓冲区写入字符串hello}else{    close(fd[1]); //关闭写端    char buf[128] = {0};    read(fd[0],buf,sizeof(buf)); //将内核缓冲区的内容写入buf中    printf("%s n",buf); //将写入buf中的内容输出到屏幕上}

}

代码语言:C

gcc pipe.c -o pipe./pipehello

进程通信(一)无名管道和有名管道可以看出,使用匿名管道完成了父子进程的通信,子进程作为写进程输入信息hello,父进程作为读进程读取信息并输出到屏幕上。

有道小P 有道小P

有道小P,新一代AI全科学习助手,在学习中遇到任何问题都可以问我。

有道小P 64 查看详情 有道小P

2.有名管道

由于无名管道的限制,仅限于有血缘关系的进程间通信,因此当需要在无血缘关系的进程之间通信时,pipe就不能使用了。这时需要使用有名管道(fifo)。其特点包括:(1)在磁盘上存在一个文件,使用ls -l命令可以查看管道文件的类型为p。(2)这是一个伪文件,其大小永远为0。(3)在内核中有一个对应的缓冲区。(4)半双工的通信方式。

进程通信(一)无名管道和有名管道使用场景:(1)无血缘关系的进程间通信。

创建方式:(1)使用命令mkfifo 管道名

进程通信(一)无名管道和有名管道(2)调用函数mkfifo,函数原型为:int mkfifo(const char *pathname, mode_t mode);参数:路径和权限。

下面举例说明如何使用mkfifo实现两个无血缘关系进程之间的通信。简单描述:在A进程中向内核缓冲区输入字符串,输入end表示结束进程。在B进程中将内核缓冲区的内容保存到命令行参数argv[1]指定的文件中。A进程:fifoa.c

代码语言:C

#include

include

include

include

include

include

int main(){int fd = open("FIFO",O_WRONLY); //默认是阻塞的if(fd == -1){int n = mkfifo("FIFO",0664); //创建管道文件FIFOif(-1 == n){perror("mkfifo");exit(0);}fd = open("FIFO",O_WRONLY);}assert(-1 != fd);printf("open success!n");while(1){printf("please input:");char buf[128] = {0};fgets(buf,128,stdin);if(strncmp(buf,"end",3) == 0) //输入end用以结束进程{break;}write(fd,buf,strlen(buf) - 1); //strlen(buf)-1减去回车符占用的一个字节长度,否则输出到屏幕上的值为乱码}close(fd);}

B进程:fifob.c

代码语言:C

#include

include

include

include

include

include

int main(int argc,char *argv[]){int fd = open("FIFO",O_RDONLY); //默认是阻塞的if(fd == -1){perror("open");exit(0);}assert(-1 != fd);int file_fd = open(argv[1],O_WRONLY|O_CREAT,0664);if(file_fd == -1){perror("open");exit(0);}char buf[128] = {0};while(read(fd,buf,128) > 0){write(file_fd,buf,strlen(buf));memset(buf,0,128);}close(fd);close(file_fd);}

代码语言:C

gcc fifoa.c -o fifoagcc fifob.c -o fifob./fifoa./fifob a.txt

A、B进程必须同时执行,否则被执行的进程会出现阻塞。这体现了管道机制中的一条要求,必须确认对方的存在,否则就会阻塞,等待对方的到来。

进程通信(一)无名管道和有名管道进程通信(一)无名管道和有名管道在A进程中输入字符串:

进程通信(一)无名管道和有名管道查看由B进程保存的a.txt文件:

进程通信(一)无名管道和有名管道正是我们在A进程中输入的字符串。

以上就是进程通信(一)无名管道和有名管道的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/466031.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月8日 05:27:33
下一篇 2025年11月8日 05:28:19

相关推荐

  • 优化字符串查找:内存映射 vs. 数据库查询

    在Go服务器应用开发中,经常会遇到需要对接收到的字符串进行验证的场景,例如验证字符串是否存在于数据库中。针对高并发的HTTP请求,如何高效地进行字符串查找是一个关键问题。通常有两种策略:一是每次请求都执行SQL查询;二是将所有字符串预先加载到内存中的Map,然后通过Map进行快速查找。选择哪种策略取…

    2025年12月16日
    000
  • SWIG-Go在Windows上调用C++ DLL:32位兼容性限制与实践指南

    本文详细阐述了在Windows环境下利用SWIG-Go调用C++ DLL的完整流程,涵盖了从接口定义、SWIG文件生成、Visual Studio构建DLL到Go语言绑定库创建及程序调用的所有步骤。重点聚焦于解决实践中可能遇到的adddynlib: unsupported binary format…

    2025年12月16日
    000
  • Go语言中通过字符编码向字符串追加字符的正确方法

    本教程详细介绍了在Go语言中如何通过字符编码(如八进制、十六进制或Unicode)向字符串追加字符。针对常见的错误用法,文章强调了Go语言对转义序列严格的语法要求,特别是对于空字符、十六进制xXX和UnicodeuXXXX等,并提供了正确的代码示例及官方规范链接,帮助开发者避免常见陷阱。 Go语言字…

    2025年12月16日
    000
  • SWIG Go与C++ DLL在Windows上的集成:64位兼容性挑战解析

    本教程深入探讨了在Windows环境下,使用SWIG将Go语言与C++ DLL进行集成的常见问题,特别是针对64位系统的兼容性挑战。文章详细分析了尝试在64位Windows上构建和运行Go调用SWIG生成的C++ DLL时可能遇到的adddynlib: unsupported binary form…

    2025年12月16日
    000
  • Go语言中len()函数与切片/数组的正确用法

    本文旨在纠正Go语言初学者在使用切片或数组时,将len函数误用为对象方法的常见错误。len是Go的内置函数,而非切片或数组的方法,应以len(x)形式调用。正确理解其用法是进行数据结构操作,如遍历和查找元素的基础。 Go语言中len()函数的常见误区 在go语言的初学者,特别是那些有其他编程语言(如…

    2025年12月16日
    000
  • Go语言中生成UUID的规范方法与最佳实践

    本文旨在探讨Go语言中生成全局唯一标识符(UUID)的正确方法。针对手动生成UUID可能遇到的问题,我们将解析其位操作原理,并强烈推荐使用Google官方维护的github.com/google/uuid库,通过简洁的代码实现高效、可靠的UUID生成,避免潜在的错误和不一致性。 理解UUID及其版本…

    2025年12月16日
    000
  • Go语言教程:正确获取切片和数组的长度——len函数与方法的区别

    本文旨在纠正Go语言初学者在获取切片或数组长度时常犯的错误。Go语言中,len是一个内置函数,而非切片或数组类型的方法。文章将详细解释为何尝试调用x.len()会引发编译错误,并演示如何通过正确使用len(x)来准确获取数据结构的长度,确保代码的正常运行和逻辑的实现。 Go语言中len函数的常见误用…

    2025年12月16日
    000
  • Go语言中切片Map的正确初始化与结构体实践

    本文深入探讨Go语言中常见的“assignment to entry in nil map”运行时错误,特别是在处理Map切片时。我们将详细解释该错误发生的原因,并提供两种有效的解决方案:一是通过显式初始化切片中的每个Map,二是在更复杂的场景下采用Go语言推荐的结构体(struct)来组织数据。通…

    2025年12月16日
    200
  • Go 应用 Debian 打包指南

    本文详细介绍了如何将 Go 应用程序打包成 Debian 格式,重点关注 Go 静态链接的特性及其对打包流程的影响。文章首先探讨了传统 debuild 工具的挑战,随后深入阐述了现代且推荐的 dh-golang 方法,通过示例代码展示了关键配置,并涵盖了打包所需的核心文件及注意事项,旨在提供一套清晰…

    2025年12月16日
    000
  • Golang依赖管理工具安装与配置示例

    Go Modules从Go 1.11起成为官方依赖管理工具,取代GOPATH模式。通过go mod init初始化项目生成go.mod文件,导入包后运行go build自动下载依赖并更新go.mod和go.sum。推荐设置GO111MODULE=on以启用模块支持。使用go get添加或升级依赖,如…

    2025年12月16日
    600
  • Golang自定义异常类型与接口结合实践

    通过定义结构化错误类型并结合接口标记语义类别,Go语言可实现清晰的错误处理。首先创建实现error接口的自定义错误结构体(如BusinessError),携带错误码和详情;接着定义标识性接口(如ValidationError)对错误分类,让特定错误类型实现对应接口;在业务逻辑中返回这些自定义错误,并…

    2025年12月16日
    400
  • 解析带命名空间的 XML 节点:Go 语言实践教程

    本文档旨在帮助 Go 开发者理解如何使用 encoding/xml 包解析包含命名空间的 XML 数据。通过一个解析 GPX 文件的实际案例,详细讲解了如何正确定义结构体字段的 XML 标签,以便能够准确提取嵌套在命名空间中的数据。本文档提供可运行的代码示例,方便开发者快速上手并解决类似问题。 理解…

    2025年12月16日
    000
  • Go语言中特殊的包名问题及解决方案

    在Go语言开发中,导入自定义子包时,有时会遇到类似“imported and not used”或“undefined”的编译错误。 出现这些错误并不一定是代码真的未使用或者未定义,而很可能是由于包名定义与导入路径不一致导致的。 简单来说,Go编译器依赖于 package 声明来识别包,如果声明的包…

    2025年12月16日
    000
  • Go与C++ DLL互操作:SWIG在Windows平台上的兼容性考量与实践

    本文深入探讨了在Windows环境下使用SWIG将Go语言与C++ DLL集成的挑战,特别是当遇到“adddynlib: unsupported binary format”错误时。核心问题在于SWIG在Windows上对Go语言的DLL绑定,其官方兼容性主要集中在32位系统。文章提供了详细的集成流…

    2025年12月16日
    000
  • Go 语言中带接收器方法与函数类型转换的演进

    在Go语言中,将一个带接收器的方法直接赋值给一个普通函数类型曾是一个挑战,早期版本需要通过匿名函数进行封装。Go 1.1引入了“方法值”的概念,极大地简化了这一过程,允许开发者直接将绑定了特定接收器的方法赋值给兼容的函数类型,从而提升了代码的简洁性和可读性。 理解带接收器的方法与函数类型 在Go语言…

    2025年12月16日
    200
  • 深入理解Go语言中的指针与方法接收器

    Go语言在处理指针和方法接收器时,引入了两项便利的自动转换机制。当方法定义为值接收器时,编译器会自动生成一个对应的指针接收器方法;反之,当方法定义为指针接收器,而调用方使用值类型变量时,Go会自动获取变量地址进行调用。这些机制使得在许多场景下,无论使用值类型还是指针类型调用方法,都能得到相同的结果,…

    2025年12月16日
    300
  • Go语言中带接收者方法的函数引用:从匿名函数到方法值

    本教程探讨了Go语言中如何将带接收者的方法作为函数值进行引用。在Go 1.1之前,这通常需要通过匿名函数封装实现;而Go 1.1引入的“方法值”特性,允许直接将特定实例的方法绑定为函数,极大地简化了代码并提升了表达力。 Go中的方法与函数类型 在go语言中,方法是与特定类型关联的函数。它们通过在函数…

    2025年12月16日
    000
  • Go语言UUID生成:官方库与最佳实践

    本文深入探讨Go语言中生成全局唯一标识符(UUID)的最佳实践。我们将分析手动实现UUID的潜在问题,特别是其中涉及的位操作,并强烈推荐使用Google官方的github.com/google/uuid库,提供详细的使用示例,确保生成的UUID符合RFC标准,易于集成且高效可靠。 理解UUID及其结…

    2025年12月16日
    300
  • Golang Builder建造者模式对象构建实践

    Builder模式通过链式调用解决多字段结构体创建的可读性问题,如User示例中NewUserBuilder().SetName(“Alice”).SetAge(28).Build()清晰构建对象,支持灵活设置与校验,提升代码维护性与类型安全。 在Go语言开发中,当一个结构体…

    2025年12月16日
    000
  • Go语言指针与方法接收器:深入解析自动行为

    本文深入探讨Go语言中指针与方法接收器的自动处理机制。Go编译器能够智能地为值接收器方法生成指针接收器版本,并自动为值类型变量获取地址以调用指针接收器方法。理解这些自动行为对于编写高效、健壮的Go代码至关重要,尤其是在选择值接收器或指针接收器时,需要考虑性能、数据修改和代码意图。 理解Go语言中的指…

    2025年12月16日
    000

发表回复

登录后才能评论
关注微信