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

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

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.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;
}

在这里插入图片描述

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;
}

在这里插入图片描述

补充:

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

在这里插入图片描述

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;
}

在这里插入图片描述

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;
}

//服务端
#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;
}

在这里插入图片描述

总结

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值