匿名管道和命名管道(代码剖析)

 

目录

 一、进程间通信(IPC):

二、管道 来源于生活

三、匿名管道:在本地机器上可以实现父进程与子进程之间的通信

匿名管道的使用:

操作接口:

管道特性:

 管道符代码实现:

 命名管道(Named Pipes)

接口:

命名管道独有的特性:

演示:

 写入端代码:

读入端代码:


 一、进程间通信(IPC):

是什么?        操作系统为用户提供的几种进程间通信方式,让进程之间能够进行通信,

为什么?        进程之间无法直接通信,而需要操作系统提供通信方式(进程之间具有独立性

                       一个进程在访问一个数据的时候都是通过地址来进行访问的,然而进程中的地

                       址都是虚拟地址,经过页表映射之后访问屋里内存。

                       因此如果想要给另一个进程传递一个数据,就得把空间地址传递给其他进程

怎么做?        操作系统通过提供进程间通信方式,就是给多个需要通信的进程之间建立一个关联

                        :能够共同访问的一块内存

 进程间不同的几种通信方式:管道、共享内存、消息队列、信号量。

二、管道 来源于生活

作用:用于实现资源(数据)传输

特性:半双工通信——可以选择方向的单向通信(同一时间不能同时即发送又访问)

本质:内核中的一块缓冲区(内核空间中的一块内存,多个进程通过访问同一个缓冲区来进行通信)

分类:

        匿名管道:没有描述符,不能被其他进程找到,只能具有亲缘关系的进程间通信

        命名管道:有描述符,可以被其他进程找到,因此可以用于同一主机上的任意进程间通信

        管道自带互斥和同步。

互斥:通过管道的唯一访问来保证进程运行的安全性

         管道的读写操作在不超过一个buf 4096个字节大小时,保证原子性

         原子性:不可分割特性        原子操作:一个操纵不会被打断

同步:通过对资源进行条件限制,来保证进程访问的合理性

        例如:管道没有数据,read会阻塞,管道数据满了,write会阻塞。

三、匿名管道:在本地机器上可以实现父进程与子进程之间的通信

不支持跨网络之间的俩个进程的通信(本地连接)

②仅实现亲缘关系(父子进程)间通信,而不是俩个任意的进程

③只提供单向通信,如果俩个进程访问这个文件,进程1给文件中写入数据,那么进程2只能读取文件。

④管道是基于字节流来进行通信的

匿名管道的使用:

匿名管道主要用于本地父进程和子进程之间的通信,在父进程中的话,首先是要创建一个匿名管道,在创建匿名管道成功后,可以获取到对这个匿名管道的读写句柄,然后父进程就可以向这个匿名管道中写入数据和读取数据了,但是如果要实现的是父子进程通信的话,那么还必须在父进程中创建一个子进程,同时,这个子进程必须能够继承和使用父进程的一些公开的句柄,为什么呢?因为在子进程中必须要使用父进程创建的匿名管道的读写句柄,通过这个匿名管道才能实现父子进程的通信,所以必须继承父进程的公开句柄。同时在创建子进程的时候,必须将子进程的标准输入句柄设置为父进程中创建匿名管道时得到的读管道句柄,将子进程的标准输出句柄设置为父进程中创建匿名管道时得到的写管道句柄。然后在子进程就可以读写匿名管道了。

操作接口:

        int pipe(int pipefd[2]);

                功能:创建一个管道,并通过参数返回管道的俩个操作句柄

                参数:pipefd——具有俩个整形元素的数组,内部创建管道会将描述符存储在数组中

                        pipefd[0]——用于从管道中读取数据

                        pipefd[1]——用于向管道中写入数据

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

                              创建匿名管道,一定要在创建子进程之前。

管道特性:

1、管道中如果没有数据,则read从管道中读取数据就会被阻塞,直到有数据了,读取到数据之后才返回。 

编译结果发现读端也进入打印,等待三秒写端进行读入之后读端继续读数据,说明读端一直在等待管道中的数据被写入。

2、如果管道中数据满了,则write继续向管道中写入数据会阻塞,直到管道中有剩余空间才行(有数据被取出去)

3、管道的所有读端被关闭,则继续向管道中写入数据会导致进程崩溃退出

   13   if(ret < 0)
   14   {
   15     perror("pipe error");
   16     return -1;
   17   }
   18 
   19   int pid1 = fork(); //创建子进程1之前管道已经存在,因此子进程1也有句柄
   20   if(pid1 == 0)
   21   {
   22     // child1
   23     sleep(3);
W> 24     char* data = "i am father\n";
   25     printf("开始打印\n");
   26     int ret = write(pipefd[1], data, strlen(data));
   27     if(ret < 0)
   28     {
   29       perror("write error\n");
   30     }
   31     printf("write over\n");
   32     exit(0);//为了能让子进程1退出,不继续向下
   33   }
   34 
   35   // 能走下来的只有父进程
   36   int pid2 = fork(); // 创建子进程2之前管道已经存在,因此子进程2也有句柄                           
   37   if(pid2 == 0)
   38   {
   39     close(pipefd[0]); // 关闭读端
   40     exit(0);
   41     while(1)
   42     {
   43       sleep(1);   
   44       printf("现在老二已经醒了,开始接受数据");
   45       char buf[1024] = {0};                                    
   46       int ret = read(pipefd[0], buf, 1023);
            if(ret == 0)
   49       {
   50         printf("所有写端已经被关闭!\n");
   51         break;
   52       }      
   53 
   54       printf("老二收到了老大的警告:%s\n",buf);                
   55     }
   56     exit(0);//为了让子进程2退出,不继续向下  
   57 
   58   }
   59 
   60   close(pipefd[0]); // 关闭所有的读端
   61   wait(NULL);
   62   wait(NULL);
   63   return 0;
   64 }

崩溃退出

4、管道的所有写端被关闭,则read从管道中读取完所有数据后,将不在阻塞,而是返回0;

        可以通过read的返回值,来决定什么时候停止从管道读取数据

      

 

 管道符代码实现:

    #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/wait.h>
  5 
  6 int main()
  7 {
  8   int pipefd[2] = {0};
  9 
 10   int ret = pipe(pipefd);
 11   if(ret < 0)
 12   {                                                                                                  
 13     perror("pipe error");
 14     return -1;
 15   }
 16 
 17   pid_t ps_pid = fork();
 18   if(ps_pid == 0)
 19   {
 20     close(pipefd[0]);
 21     dup2(pipefd[1], 1); // 将标准输出重定向到管道写入端,操作标准输出就等于操作管道写入端
 22     execlp("ps", "ps", "-ef", NULL);
 23     exit(-1);
 24   }
 25   pid_t grep_pid = fork();
 26   if(grep_pid == 0)
 27   {
 28     close(pipefd[1]);
 29     dup2(pipefd[0], 0); // 将标准输入重定向到管道读取端,操作标准输入就等于操作管道读取段
 30     execlp("grep", "grep", "pipe", NULL);
 31     exit(-1);
 32   }
 33   close(pipefd[0]);
 34   close(pipefd[1]);
 35   waitpid(ps_pid, NULL, 0);
 36   waitpid(grep_pid, NULL, 0);
 37   return 0;
 38   //wait(NULL);
 39   //wait(NULL);
 40 }

 命名管道(Named Pipes)

命名管道可在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。

服务器建立命名管道时给它指定一个名字(标识符),任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

 

接口:

        int mkfifo(char *pathname, mode_t mode)

                pathname: 管道文件名称

                mode: 管道文件的访问权限

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

命名管道独有的特性:

若以只写的方式打开管道文件,则会阻塞,直到管道被任意进程以读的方式打开

若以只读的方式打开管道文件,则会阻塞,直到管道被任意进程以只写的方式打开。

因为一个管道如果不构成同时读写具备,就没有必要开辟缓冲区

演示:

发生阻塞无法继续运行,

 新建一个会话端口打开写入端进行写入,那么之前的读入端口就会进行读取数据

 左图为写入端,右下角图为读入端口。

 

 接着我关闭写端:                          管道特性:所以写端被关闭 read返回一个0,打印写端被关闭

 那么我们之前看到的所以匿名管道具有的特点命名管道也具有。

 写入端代码:

    #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<fcntl.h>
  6 #include<errno.h>
  7 #include<sys/stat.h>
  8 
  9 int main()
 10 {
 11   umask(0);// 将当前进程的文件权限掩码设置为0
 12   int ret = mkfifo("./test.fifo",0664);
 13   if(ret < 0 && errno != EEXIST)  // errno是一个全局变量
 14   {
 15     perror("mkfifo error");
 16     return -1;
 17   }
 18                                                                                                                                                                                                              
 19   int fd = open("./test.fifo", O_WRONLY); // 管道文件这里打开
 20   if(fd < 0)
 21   {
 22     perror("open error");
 23     return -1;
 24   }
 25 
 26   while(1)
 27   {
 28     printf("小明:");
 29     fflush(stdout);
 30     char buf[1024] = {0};
 31     scanf("%s",buf);
 32 
 33     int ret = write(fd, buf, strlen(buf));
 34     if(ret < 0)
 35     {
 36       perror("write error");
 37       close(fd);
 38       return -1;
 39     }
 40   }
 41   close(fd);
 42   return 0;
    }

读入端代码:

      #include<stdio.h>
  2   #include<unistd.h>
  3   #include<stdlib.h>
  4   #include<string.h>
  5   #include<fcntl.h>
  6   #include<errno.h>
  7   #include<sys/stat.h>
  8 
  9    int main()
 10   {
 11     umask(0);// 将当前进程的文件权限掩码设置为0
 12     int ret = mkfifo("./test.fifo",0664);
 13     if(ret < 0 && errno != EEXIST)  // errno是一个全局变量
 14    {
 15       perror("mkfifo error");
 16       return -1;
 17     }
 18 
 19    int fd = open("./test.fifo", O_RDONLY); // 管道文件这里打开                                                                                                                                               
 20    if(fd<0)
 21    {
 22      perror("open error");
 23      return -1;
 24    }
 25    while(1)
 26    {
 27      char buf[1024] = {0};
 28      int ret = read(fd, buf, 1023);
 29      if(ret < 0)
 30      {
 31        perror("write error");
 32        close(fd);
 33        return -1;
 34      }else if(ret == 0)
 35      {
 36        printf("所有写端被关闭!\n");
 37        close(fd);
 38        return -1;
 39      }
 40      printf("小明:%s\n", buf);
 41 
 42    }
        close(fd);
        return 0;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
管道泄漏是指管道系统中的流体从其中的一处或多处泄漏出去,这可能会导致流体的浪费、环境污染,甚至可能造成安全事故。解决管道泄漏问题的一种方法是使用MATLAB进行检测和修复。 在MATLAB中,可以使用以下方法实现对管道泄漏进行检测和修复。 1. 检测泄漏: 首先,读取实时传感器数据或模拟数据,将其存储在MATLAB中。然后,使用信号处理技术,如时频分析或小波分析,对数据进行预处理和滤波,以消除噪声干扰。接下来,使用算法或模型来检测异常信号,例如峰值检测、能量检测或统计分析。当异常信号超过阈值时,即可判断出可能存在泄漏。 2. 定位泄漏: 一旦检测到泄漏,需要进一步确定泄漏位置。在MATLAB中,可以利用机器学习方法或者数值模拟进行泄漏位置的预测。通过收集不同位置的泄漏样本数据,使用分类或回归算法进行训练和测试,可以建立一个准确的泄漏位置预测模型。 3. 修复泄漏: 根据泄漏位置的确定,可以采取适当的措施进行修复。修复方法包括更换损坏部件、重新安装密封件或加压测试以验证修复效果。在MATLAB中,可以使用可视化工具,如Simulink,来模拟和验证修复方法的有效性。 综上所述,MATLAB可以提供一种全面的方案来检测和修复管道泄漏问题。通过对实时传感器数据或模拟数据的处理和分析,可以准确地检测到泄漏的存在,并利用机器学习等方法对泄漏位置进行预测。然后,采取适当的修复措施来解决问题。这一方案能够提高泄漏问题的诊断和修复效率,降低环境和安全风险。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值