• 管道实现进程间通信之命名管道


    进程间通信管道篇之命名管道

    1.1命名管道

    问题引出:为什么有了匿名管道还需要命名管道呢?

    因为匿名管道是只用于具有亲缘关系的,例如父子进程间通信可以用匿名管道,但是如果要使得毫无关系的两个进程间进行通信,匿名管道就派不上用场了,就需要用命名管道来实现。命名管道是一种特殊的文件,它可以使的进程之间共享同一块资源,通过对该资源的读写来实现通信。命名管道解除了管道只能在具有亲缘关系的进程间使用的限制。

    1.2命名管道的创建

    • 第一种:通过命令行方式进行创建

      mkfifo +"文件名"方式

    在这里插入图片描述

    • 第二种:通过代码方式( 调用函数mkfifo() )创建

      定义:

      第一个参数是要创建的管道文件的路径(当前路径下的名称),第二个参数是权限的设置 一般都是0xxx的形式

    在这里插入图片描述

    ​ 返回值:如果创建成功返回0 不成功返回-1 可以以此来判断是否创建成功
    在这里插入图片描述

    例:

    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    int main()
    {
        if(makfifo("myfifo",0644)<0)
        {
            printf("mkfifo fail!\n");
            return -1;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3命名管道和匿名管道的区别

    • 命名管道由mkfifo函数创建,打开用open
    • 匿名管道由pipe创建并打开
    • 命名管道和匿名管道之间的唯一区别在于它们创建和打开的方式不同,创建和打开之后它们具有相同的语义

    ​ 只读打开用open(“文件名”,O_RDONLY);

    ​ 只写打开用open(“文件名”,O_WRONLY);

    ​ 打开还有O_CREAT

    ​ 要实现不同的打开方式,使用 或 | 将上面的混合使用即可

    代码演示(用命名管道实现进程间通信)

    //两个.c 文件 client.c  serve.c  一个充当客户端 一个充当服务端 引用同一个头文件serve.h
    
    //serve.h
    #include<stdio.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<string.h>
    #define File_Name "fifo"
    
    -----------------------------------
    //serve.c
        
    #include"sever.h"
    int main()
    {
      if(mkfifo(File_Name,0644)<0)
      {
        perror("mkfifo");
        return -1;
      }
      //通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
    
      //接下来就是通过对管进行读写实现两个进程间的通信了
     int fd = open(File_Name,O_RDONLY);//打开文件 只读
     if(fd < 0)
     {
       perror("open");
       return -2;
     }
    
      char srt[128];
      while(1)
      {
        srt[0]=0;//清空文件
        int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
        if(s>0)//还没有读完的情况
        {
          srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0 
          printf("client send message# %s\n",srt);
        }
        else if(s==0)
        {
          printf("client quit!\n");
          break;
        }
        else
        {
          //说明读取错误 返回的是-1
          perror("read");
          break;
        }
      }
    
      close(fd);//打开了文件最后要将其关闭
      return 0;
    }
    
    ----------------------------------------
    //client.c
    
    #include"serve.h"
    int main()
    {
    
      //先打开管道文件
      int fd=open(File_Name,O_WRONLY);
      if(fd<0)
      {
        perror("open");
        return -1;
      }
    
      //打开了文件后就要从键盘读取数据 然后把读到的数据写到管道中去
      char msg[128];
      while(1)
      {
        msg[0]=0;
        printf("please Enter# ");
        fflush(stdout);//上面的打印没有带回车 全缓冲  所以要手动刷新缓冲区
        int ret=read(0,msg,sizeof(msg)-1);
        if(ret>0)
        {
          msg[ret]=0;
          write(fd,msg,strlen(msg));
        }
      }
      close(fd);
      
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    在这里插入图片描述

    2.1 通过进程间通信将客户端发送的字符串转化为命令让服务端执行

    代码:(改变的额只有服务端的代码,就是创建了一个子进程 然后用进程替换 把客户端发的字符当成命令来执行 只有非常小的改动)

    #include"serve.h"
    #include<sys/wait.h>
    #include<stdlib.h>
    int main()
    {
      if(mkfifo(File_Name,0644)<0)
      {
        perror("mkfifo");
        return -1;
      }
      //通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
    
      //接下来就是通过对管进行读写实现两个进程间的通信了
     int fd = open(File_Name,O_RDONLY);//打开文件 只读
     if(fd < 0)
     {
       perror("open");
       return -2;
     }
    
      char srt[128];
      while(1)
      {
        srt[0]=0;//清空文件
        int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
        if(s>0)//还没有读完的情况
        {
          srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0 
         // printf("client send message# %s\n",srt);
         printf("client send command/message# %s\n",srt);
         fflush(stdout);
           // 改变的地方  /
          int pid = fork();//创建子进程来完成输入的命令
          if(pid==0)
          {
            //child
            execlp(srt,srt,NULL);//进程替换
            exit(1);//子进程退出
          }
          waitpid(pid,NULL,0);//阻塞式等待子进程退出
          
        }
        else if(s==0)
        {
          printf("client quit!\n");
          break;
        }
        else
        {
          //说明读取错误 返回的是-1
          perror("read");
          break;
        }
      }
    
      close(fd);//打开了文件最后要将其关闭
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    在这里插入图片描述

    补充:

    这里的进程间通信通过管道实现 其中的数据是不涉及磁盘的 也就是说数据没有被刷新到磁盘里,而是在内存中!!!!

    在这里插入图片描述

    3.1通过进程间通信传输数据设计一个计算器

    代码:(改动的也只有服务端)

    #include"serve.h"
    #include<sys/wait.h>
    #include<stdlib.h>
    int main()
    {
      if(mkfifo(File_Name,0644)<0)
      {
        perror("mkfifo");
        return -1;
      }
      //通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
      //接下来就是通过对管进行读写实现两个进程间的通信了
     int fd = open(File_Name,O_RDONLY);//打开文件 只读
     if(fd < 0)
     {
       perror("open");
       return -2;
     }
    
      char srt[128];
      while(1)
      {
        srt[0]=0;//清空文件
        int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
        if(s>0)//还没有读完的情况
        {
          srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0 
        printf("client send message# %s\n",srt);
          char* st=srt;
         int flag=0;
         while(*st)//找运算符号
         {
           switch(*st)
           {
              case'+':
                flag=1;
                break;
              case'-':
                flag=2;
                break;
              case'*':
                flag=3;
                break;
              case'/':
                flag=4;
                break;
              case'%':
                flag=5;
                break;
           }
           st++;
         }
         
         int a,b,c;
          const char* str=" +-*/%";
         if(flag)//如果找到了运算符号就进行拆分子串
         {
          char* s1=strtok(srt,str);
          char* s2=strtok(NULL,str);
          a=atoi(s1);//字符转数字
          b=atoi(s2);
          switch(flag)//计算
          {
          case 1:
            c=a+b;
            break;
          case 2:
            c=a-b;
            break;
          case 3:
            c=a*b;
            break;
          case 4:
            c=a/b;
            break;
          case 5:
            c=a%b;
            break;
         }
          printf("%d %c %d = %d\n",a,str[flag],b,c);//打印
          }
        }
        else if(s==0)
        {
          printf("client quit!\n");
          break;
        }
        else
        {
          //说明读取错误 返回的是-1
          perror("read");
          break;
        }
      }
      close(fd);//打开了文件最后要将其关闭
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    在这里插入图片描述

    4.1 通过进程间通信实现进程间文件的互传

    代码:

    //客户端
    #include"serve.h"
    int main()
    {
    
      //先打开管道文件
      int fd=open(File_Name,O_WRONLY);
      if(fd<0)
      {
        perror("open");
        return -1;
      }
      int fd1=open("file.txt",O_RDONLY);//打开文件 从管道中读取file.txt的数据 再往file-bk.txt里面写就可以实现文件的拷贝了
      if(fd1<0)
      {
        perror("open file.txt");
        return -2;
      }
    
      //打开了文件后就要从键盘读取数据 然后把读到的数据写到管道中去
      char msg[128];
      while(1)
      {
        msg[0]=0;
        dup2(fd1,0);//重定向fd为0 也就是相当于把stdin关了 再创建file-bk 实现了从标准输入中读入数据就是从file-bk中读入数据
        ssize_t ret=read(0,msg,sizeof(msg));
        if(ret==sizeof(msg))
        {
         // msg[ret-1]=0;//设置\0使得文件可以在输出的时候可以结束
          write(fd,msg,ret);//把数据写到新打开的文件中去
        }
        else if(ret<sizeof(msg))
        {
          write(fd,msg,ret);//把不足期望的大小的数据写到文件中
          printf("read end of file!\n");
          break;
        }
        else{
          printf("read error!\n");
          break;
        }
      }
      close(fd);
      close(fd1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    //服务端
    #include"serve.h"
    #include<sys/wait.h>
    #include<stdlib.h>
    int main()
    {
      if(mkfifo(File_Name,0644)<0)
      {
        perror("mkfifo");
        return -1;
      }
      //通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
    
      //接下来就是通过对管进行读写实现两个进程间的通信了
     int fd = open(File_Name,O_RDONLY);//打开文件 只读
     if(fd < 0)
     {
       perror("open");
       return -2;
     }
    
     int fd1=open("file-bk.txt",O_CREAT|O_WRONLY,0644);//以只读的方式打开已经存在的file.txt文件 一定是可以打开的 不用判断
      char srt[128];
      while(1)
      {
        srt[0]=0;
        int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
        if(s>0)//还没有读完的情况
        {
        //  write(fd1,srt,sizeof(srt));
            write(fd1,srt,s);//不是sizeof(srt)
        }
        else if(s==0)
        {
          printf("client quit!\n");
          break;
        }
        else
        {
          //说明读取错误 返回的是-1
          perror("read");
          break;
        }
      }
    
      close(fd);//打开了文件最后要将其关闭
      close(fd1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述

    总结

    总结:命名管道其实就是一个文件,只不过是操作系统对其进行了加工处理,使其具有特定的功能,这个管道文件就是在毫无关系的进程间共享的,使得毫无关系的进程能看到同一份资源,进程对该资源进行文件读写,实现信息的交互,达到通信的效果!!!(只要创建出了命名管道,接下来的操作就是对文件的读写处理了)

  • 相关阅读:
    Java 面试题整理成 985 道细分 26 个部分,接连下成功面上阿里!
    MTK 关机充电时充电IC正常,电池正常充电,但是充电动画一直显示0%
    Android 系统源码目录frameworks/base/packages和packages/apps下的APP区别
    基于MATLAB的函数拟合
    Linux内核分析(六)--处理器调度基本准则和实现介绍
    黄金投资新手指南:投资现货黄金怎样开户?
    线程之相关知识点总结
    列表、元组和字典
    6.26CF模拟赛E:价格最大化题解
    C【分支语句和循环语句】
  • 原文地址:https://blog.csdn.net/xbhinsterest11/article/details/125481224