linux篇第十一篇——进程间通信(管道+systemv共享内存)(代码片段)

呆呆兽学编程 呆呆兽学编程     2022-12-10     602

关键词:

⭐️ 本篇博客要给大家介绍一些关于进程间通信的一些知识。Linux下进程通信常见的几种方式,例如管道、共享内存等。

目录


🌏介绍

概念: 进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。IPC方法包括管道(PIPE)、消息排队、旗语、共用内存以及套接字(socket)(本篇博客只介绍共享内存和管道两种)。
通信目的:

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知某些或某个进程发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

如何实现通信?
要让两个不同的进程实现通信,前提条件是让它们看到同一份资源。所以要想办法让他们看到同一份资源,就需要采取一些手段,可以分为下面几种
通信方式分类:

  1. 管道
  • 匿名管道pipe
  • 命名管道
  1. System V IPC
  • System V 消息队列
  • System V 共享内存
  • System V 信号量
  1. POSIX IPC
  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

🌏管道

🌲认识管道

概念: 管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。它的特点是单向传输数据的,先进先出。
管道相信大家之前都知道一些。我们之前也会用到管道命令‘|’。例如:cat file.txt | head -1。
cat是一个进程,这个进程先处理,然后将处理后得到的标准输出到管道中,再由head进程通过标准输入将管道中的数据读出,再进行处理。

🌲匿名管道

概念: 匿名管道用于进程之间通信,这两个进程需要具有亲缘关系(父子进程等)。

🍯创建匿名管道——pipe

这里介绍一个系统调用接口——pipe。

功能: 创建一个匿名管道
函数原型:

#include <unistd>
int pipe(int pipefd[2])

参数:
fd:文件描述符数组,这是一个输出型参数,fd[0]表示读端,fd[1]表示写端
返回值:
创建管道成功返回0,失败返回-1

匿名管道创建原理:
调用pipe函数后,OS会在fd_array数组中分配两个文件描述符给管道,一个是读,一个是写,并把这两个文件描述符放到用户传进来的数组中,fd[0]代表管道读端,fd[1]代表管道写端。这样一个管道就创建好了。

实例演示:
实例1: 观察fd[0]和fd[1]的值

#include <stdio.h>
#include <unistd.h>

int main()

	int pipefd[2];
	int ret = pipe(pipefd);
	if (ret == -1)
	  // 管道创建失败
	  perror("make piep");
	  exit(-1);
	
	// 成功返回0
	// pipefd[0] 代表读端
	// pipefd[1] 代表写端
	printf("fd[0]:%d, fd[1]:%d\\n", pipefd[0], pipefd[1]);
	return0;

代码运行结果: 显然,pipefd这个数组里面放的是两个文件描述符

实例2: 尝试使用管道读写数据

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main()

	  int pipefd[2];
	  int ret = pipe(pipefd);
	  if (ret == -1)
	    // 管道创建失败
	    perror("make piep");
	    exit(-1);
	  
	
	  char buf[64] = "hello world";
	  // 写数据
	  write(pipefd[1], buf, sizeof(buf)/sizeof(buf[0]));
	  // 读数据
	  buf[0] = 0;// 清空buf
	  ssize_t s = read(pipefd[0], buf, 11);
	  buf[s] = '\\0';
	  printf("%s\\n", buf);
	  return 0;

代码运行结果如下: 可以看出,管道也可以读写数据,和文件使用方法是一致的

上面介绍的都是关于管道如何创建,接下来就要介绍如何使用管道进行通信。

🍯管道的本质

Linux下一切皆文件,看待管道,其实时可以像看待文件一样。且管道和文件使用方法是一致的。管道的生命周期随进程

🍯使用匿名管道进行通信

匿名管道是提供给有亲缘关系两个进程进行通信的。所以我们可以在创建管道之后通过fork函数创建子进程,这样父子进程就看到同一份资源,且父子进程都有这个管道的读写文件描述符。我们可以关闭父进程的读端,关闭子进程的写端,这样子进程往管道里面写数据,父进程往管道里面读数据,这样两个进程就可以实现通信了。
具体过程如下:

  1. 父进程创建管道

  2. foek创建子进程

  3. 关闭父进程的写端,子进程的读端

    实例演示: 子进程每隔1秒往管道里面写数据,父进程每隔1秒往管道里读数据

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main()

  int pipefd[2];
  int ret = pipe(pipefd);
  if (ret == -1)
    // 管道创建失败
    perror("make piep");
    exit(-1);
  
  pid_t id = fork();
  if (id < 0)
    perror("fork failed");
    exit(-1);
  
  else if (id == 0)
    // child
    // 关闭读端
    close(pipefd[0]);
    const char* msg = "I am child...!\\n";
    //int count = 0;
    // 写数据
    while (1)
      ssize_t s = write(pipefd[1], msg, strlen(msg));
      printf("child is sending message...\\n");
      sleep(1);
    
  
  else
    // parent
    close(pipefd[1]);
    char buf[64];
    while (1)
      ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);
      if (s > 0)
        buf[s] = '\\0';// 字符串后放一个'\\0'
        printf("father get message:%s", buf);
      
      else if (s == 0)
        // 读到文件结尾  写端关闭文件描述符 读端会读到文件结尾
        printf("father read end of file...\\n ");
      
      sleep(1);
    
  

  return 0;

代码运行结果如下:

🍯匿名管道的读写规则

在这里我们分四种情况来进行研究:

  1. 写端速度小于读端速度,管道大部分时间内为空,即读条件不满足 让子进程每5秒写一次,父进程每1秒读一次,观察现象
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()

  int pipefd[2];
  int ret = pipe(pipefd);
  if (ret == -1)
    // 管道创建失败
    perror("make piep");
    exit(-1);
  
  pid_t id = fork();
  if (id < 0)
    perror("fork failed");
    exit(-1);
  
  else if (id == 0)
    // child
    // 关闭读端
    close(pipefd[0]);
    const char* msg = "I am child...!\\n";
    //int count = 0;
    // 写数据
    while (1)
      ssize_t s = write(pipefd[1], msg, strlen(msg));
      sleep(5);// 管道大部分时间是空的,读条件不满足时,读端处于阻塞状态
      printf("child is sending message...\\n");
    
  
  else
    // parent
    close(pipefd[1]);
    char buf[64];
    //int count = 0;
    while (1)
      ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);
      if (s > 0)
        buf[s] = '\\0';// 字符串后放一个'\\0'
        printf("father get message:%s", buf);
      
      else if (s == 0)
        // 读到文件结尾  写端关闭文件描述符 读端会读到文件结尾
        printf("father read end of file...\\n ");
      
      sleep(1);
    
  

  return 0;

代码运行结果如下: 读端处于阻塞

总结: 当读条件不满足时,读端进程会处于阻塞,从task_struct会从运行队列调到等待队列,知道有数据来,才会转移到运行队列中。

  1. 写端速度大于读端速度,管道大部分时间内是满的,即写调整不满足 让子进程一直,父进程每5秒读一次,观察现象
    这里这方核心代码,在上面那个例子的代码进行了一定改造
pid_t id = fork();
if (id < 0)
  perror("fork failed");
  exit(-1);

else if (id == 0)
  // child
  // 关闭读端
  close(pipefd[0]);
  const char* msg = "I am child...!\\n";
  //int count = 0;
  // 写数据
  while (1)
    ssize_t s = write(pipefd[1], msg, strlen(msg));
    printf("child is sending message...\\n");
  

else
  // parent
  close(pipefd[1]);
  char buf[64];
  //int count = 0;
  while (1)
    ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);
    if (s > 0)
      buf[s] = '\\0';// 字符串后放一个'\\0'
      printf("father get message:%s", buf);
    
    else if (s == 0)
      // 读到文件结尾  写端关闭文件描述符 读端会读到文件结尾
      printf("father read end of file...\\n ");
      sleep(5);// 管道大部分时间都是满的,写条件不满足时,写端处于阻塞状态
    
  

代码运行结果如下: 写端写了一会后,管道满了,此时写端处于阻塞状态

总结: 当写条件不满足时,写端处于阻塞状态

  1. 关闭写端 让写端先写5秒,然后关闭写端,观察现象
// child
// 关闭读端
close(pipefd[0]);
const char* msg = "I am child...!\\n";
int count = 0;
// 写数据
while (1)
  ssize_t s = write(pipefd[1], msg, strlen(msg));
  printf("child is sending message...\\n");
  
  printf("CHILD: %d\\n", count++);
  if (count == 5)
    close(pipefd[1]);
    exit(-1);
 
  sleep(1);

// parent
close(pipefd[1]);
char buf[64];
while (1)
  ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);
  if (s > 0)
    buf[s] = '\\0';// 字符串后放一个'\\0'
    printf("father get message:%s", buf);
    sleep(5);// 管道大部分时间都是满的,写条件不满足时,写端处于阻塞状态
  
  else if (s == 0)
    // 读到文件结尾  写端关闭文件描述符 读端会读到文件结尾
    printf("father read end of file...\\n ");
   

代码运行结果如下: 3s后,关闭写端,读端会读到文件结尾

总结: 如果关闭写端,读端进程会读到文件结尾

  1. 关闭读端 5秒后关闭读端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()

  int pipefd[2];
  int ret = pipe(pipefd);
  if (ret == -1)
    // 管道创建失败
    perror("make piep");
    exit(-1);
  

  pid_t id = fork();
  if (id < 0)
    perror("fork failed");
    exit(-1);
  
  else if (id == 0)
    // child
    // 关闭读端
    close(pipefd[0]);
    const char* msg = "I am child...!\\n";
    // int count = 0;
    // 写数据
    while (1)
      ssize_t s = write(pipefd[1], msg, strlen(msg));
      printf("child is sending message...\\n");
      
      sleep(1);
    
  
  else
    // parent
    close(pipefd[1]);
    char buf[64];
    int count = 0;
    while (1)
      ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);
      if (s > 0)
        buf[s] = '\\0';// 字符串后放一个'\\0'
        printf("father get message:%s", buf);
        //sleep(5);// 管道大部分时间都是满的,写条件不满足时,写端处于阻塞状态
      
      else if (s == 0)
        // 读到文件结尾  写端关闭文件描述符 读端会读到文件结尾
        printf("father read end of file...\\n ");
      
      sleep(1);
      if (count++ == 3)
        close(pipefd[0]);// 读端关闭文件描述符,写端进程后序会被操作系统直接杀掉,没有进程读,写时没有意义的
        break;
      
    
    int status;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0)
      // 等待成功
      printf("child exit singal is %d\\n", status&0x7f);
    
    else
      // 等待失败
      perror<

linux进程间通信(代码片段)

文章目录进程间通信介绍进程间通信的概念进程间通信的目的进程间通信的本质进程间通信的分类管道什么是管道匿名管道匿名管道的原理pipe函数匿名管道使用步骤管道读写规则管道的特点管道的四种特殊情况管道的大小命名... 查看详情

linux入门进程间的通信(代码片段)

进程间通信进程间通信介绍进程间通信目的进程间通信发展进程间通信分类管道什么是管道匿名管道用fork来共享管道的原理站在内核角度-深度理解管道站在内核角度-管道的本质管道读写规则管道特点命名管道创建一个命名管道... 查看详情

linux系统进程间通信方式:管道

本文详细介绍了匿名管道和有名管道这两个用于进程间通信的方式,并总结了他们的特点和使用场景;也通过示例演示了两个进程如何通过管道进行通信。本文详细介绍了匿名管道和有名管道这两个用于进程间通信的方式,并总... 查看详情

[linux]进程间通信介绍管道

0.进程间通信介绍0.1通信背景在之前我们学习进程时知道进程具有独立性,所以进程间交互数据的成本就变得非常高。进程之间为什么也进行进程间通信,这就需要谈谈进程间通信的目的了。但是进程具有独立性不是彻底独立,... 查看详情

linux-进程间通信(代码片段)

进程间通信进程间通信介绍进程间通信目的进程间通信发展进程间通信分类管道匿名管道匿名管道特点匿名管道读写规则命名管道创建一个命名管道命名管道的打开规则用命名管道实现server&client通信systemV共享内存共享内存共... 查看详情

linux-进程间通信(代码片段)

进程间通信进程间通信介绍进程间通信目的进程间通信发展进程间通信分类管道匿名管道匿名管道特点匿名管道读写规则命名管道创建一个命名管道命名管道的打开规则用命名管道实现server&client通信systemV共享内存共享内存共... 查看详情

linux之进程间通信——管道

目录进程间是怎么通信的管道匿名管道匿名管道是如何实现进程间通信内核角度理解匿名管道管道的读写规则 情景1情景2 情景3​ 情景4命名管道创建命名管道命令行方式创建函数创建命名管道的应用场景 场景1 场景2场景3进... 查看详情

linux之进程间通信(代码片段)

进程间通信文章目录进程间通信进程间通信的目的进程间通信分类管道什么是管道匿名管道pipe函数用fork来共享管道原理站在文件描述符角度理解管道管道读写规则管道的特性命名管道systemV共享内存共享内存的基本原理申请共享... 查看详情

[os-linux]详解linux的进程间通信1------管道(代码片段)

本文详解了Linux中进程间通信,包括了进程间通信的介绍,匿名管道和命名管道。目录一、进程间通信的介绍1.进程间通信目的2.进程间通信分类二、管道1.管道是什么2.匿名管道  (1)pipe (2)实现 (3)用... 查看详情

[os-linux]详解linux的进程间通信1------管道(代码片段)

本文详解了Linux中进程间通信,包括了进程间通信的介绍,匿名管道和命名管道。目录一、进程间通信的介绍1.进程间通信目的2.进程间通信分类二、管道1.管道是什么2.匿名管道  (1)pipe (2)实现 (3)用... 查看详情

linux进程间通信--使用命名管道

在前一篇文章—— Linux进程间通信--使用匿名管道 中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关的的... 查看详情

linux学习:进程间通信—管道

1、进程间通信当中一种比較简单的方法是管道操作/*============================================================================Name:Test.cAuthor:wangchuanVersion:Copyright:YourcopyrightnoticeDescription:HelloWorldinC,Ansi-styl 查看详情

linux的进程间通信-管道

Linux的进程间通信-管道版权声明:本文章内容在非商业使用前提下可无需授权任意转载、发布。转载、发布请务必注明作者和其微博、微信公众号地址,以便读者询问问题和甄误反馈,共同进步。微博ID:orroz微信公众号:Linux... 查看详情

linux进程间通信--使用匿名管道

在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值。这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据。一... 查看详情

linux进程间通信(代码片段)

等你穿过暴风雨,你就不再是原来的你。linux进程间通信管道匿名管道匿名管道的创建命名管道命名管道的创建管道的读写规则管道的特点SYSTEMV共享内存共享内存原理共享内存示意图共享内存函数ftokshmgetshmatshmdtshmctl进程间... 查看详情

linux进程间通信(代码片段)

等你穿过暴风雨,你就不再是原来的你。linux进程间通信管道匿名管道匿名管道的创建命名管道命名管道的创建管道的读写规则管道的特点SYSTEMV共享内存共享内存原理共享内存示意图共享内存函数ftokshmgetshmatshmdtshmctl进程间... 查看详情

linux进程间通信机制(ipc机制)-管道

...途是不一样的。无名管道PIPE:主要用于具有亲缘关系的进程之间的通信,无名管道的通信是单向的,只能由一段到另外一段;无名管道是临时性的,完成通信后将自动消失。一般采用先创建无名管道,再创建子进程,使子进程... 查看详情

linux-进程间通信-命名管道

1、命名管道的特点:(1)是管道,可用于非血缘关系的进程间的通信(2)使用命名管道时,梁金成需要用路径表示通道。(3)命名管道以FIFO的文件形式存储于文件系统中。(FIFO:总是按照先进先出的原则工作,第一个被写入... 查看详情