• Linux进程间通信


    什么是进程间通信

    就是两个进程为了完成某个工作而需要互相交流,互相告知双方进度,结果。
    原理:先让A,B进程都可以看到一份公共资源,一个往里面写,一个从里面读。
    进程是具有独立性的,所以进程和进程之间无法通过交互访问地址来完成,
    所以这份资源是由OS提供的内核缓冲区,让两个进程去访问这份公共资源。

    通信方式的种类
    管道(生命周期随进程)
    匿名管道pipe(重点掌握)
    命名管道(重点掌握)
    System V IPC(生命周期随内核)
    System V 消息队列(了解学习)
    System V 共享内存(重点掌握)
    System V 信号量(了解学习)

    匿名管道

    名字就告诉我们了,没有名字,那如何让两个进程看到同一份资源呢?
    通过父子继承的方式,子进程以父进程的PCB为模板创建自己的PCB,从而也关联了父进程的打开文件(匿名管道)
    也就是说:匿名管道仅仅限于由血缘关系的父子进程。而且必须在子进程创建之前去创建匿名管道

    在这里插入图片描述

    写一个简单的匿名管道通信,只供解释原理。
    但是呢,还有很多种特殊的情况:
    1,读端和写段都在未关闭的情况下,他们之间会相互阻塞。读端不读,读的少,写端会等。写端不写,写的慢,读端会等。
    2,如果关闭读端,写端会受到13号信号。因为都不读了,写端没有意义了。
    3,如果写端关闭,读端会读完缓冲区的内容,并且read()最后返回0

    #include 
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 int main()
      7 {
      8      int fd_arr[2] = {0};
      9 
     10      //创建成功后, 0是读端,1是写端
     11      if(pipe(fd_arr) < 0)
     12      {
     13        perror("pipe");
     14        exit(-1);
     15      }
     16 
     17      if(fork() == 0)
     18      {
     19        //子进程 去写,关闭相应的读端
     20        close(fd_arr[0]);
     21        const char* ch = "hello chen";                                                                                                        
     22        write(fd_arr[1],ch,strlen(ch));
     23      }
     24      else
     25      {
     26        //父进程,去读,关闭相应的写端
     27        close(fd_arr[1]);
                char ch[64] = {0};
     29        ssize_t s = read(fd_arr[0],ch,64);
     30        ch[s] = 0;
     31 
     32        printf("child to father::%s\n",ch);
     33        
     34      }
     35 
     36 
     37 
     38      return 0;
     39 }
    ~
    
    
    
    • 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

    在这里插入图片描述

    命名管道

    由于匿名管道仅仅限于有血缘关系的父子进程,所以引入了命名管道,它可以用于主机上任何进程的交换。
    它是一种特殊的文件。匿名管道和命名管道本质原理是相同的:都是OS提供内核缓冲区
    1,而且命名管道的文件名仅仅是缓冲区的标识符,如果正在通信的进程,就算删除了也不受影响
    2,大小永远是0(因为不会把数据刷新到管道文件里面),还有管道文件的作用仅仅是缓冲区的标识符。

    通信原理:
    在这里插入图片描述

    让两个进程分别包含通一个.h头文件,通过宏定义让其做到看同一份资源的目的。

     #include 
      2 #include <sys/stat.h>
      3 #include <fcntl.h>
      4 #include <stdlib.h>
      5 #include <stdio.h>
      6 #include <unistd.h>
      8 #define MYFIFO "./myfifo  //各自包含头文件,就做到了拿到管道文件的文件名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    一个进程以写的方式打开管道文件

        #include "commond.h"
      3 int main()
      4 {
      5     int fd = open(MYFIFO,O_WRONLY);
      6     if(fd < 0)
      7     {
      8       perror("opne fail");
      9       exit(-1);
     10     }
     11 
     12     while(1)
     13     {
     14        printf("girlfriend say:#");
     15        fflush(stdout);
     16 
     17        char ch[64] = {0};
     18        read(1,ch,64);                                                                                                                        
     19        write(fd,ch,64);
     20     }
     21 
     22     return 0;
     23 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    一个进程以读方式打开管道文件

         #include "commond.h"2 
      3 int main()
      4 {
      5      umask(0); // 文件权限 = default & (~umask)6 
      7      if(mkfifo(MYFIFO,0666) < 0)   //创建管道文件
      8      {
      9        perror("mkfifo");
     10        exit(-1);
     11      }
     12     
     13      //我们只需要读就可以了      
           int fd = open(MYFIFO,O_RDONLY);
     15      if(fd < 0)
     16      {
     17        perror("errror");
     18        exit(-1);
     19      }
     20     
     21      while(1)
     22      {                                                                                                                                       
     23          char ch[64] = {0};
     24          ssize_t s = read(fd,ch,sizeof(ch));
     25          if(s < 0)
     26          {
     27            perror("read fail");
                    exit(-1);
     29          }
     30          else if(s == 0)
     31          {
     32            printf("girl go away\n");
     33            break;
     34          }
     35          else
     36          {
     37            ch[s-1] = 0; //会把\n也读进去38            
                   printf("girlfriend say to boy:%s",ch);
     39          }
     40      }
     41 
     42 
     43      close(fd);
     44      return 0;
     45 }
    
    • 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

    在这里插入图片描述

    共享内存

    通信速度最快
    会存在多个,进程A和B,C和D,,,,都要通信,意味着共享内存会存在多份。
    OS如何管理呢? 先描述后组织,对共享内存的描述肯定是在结构体里面。
    A和B,C和D如何保证能准确看到对应的共享内存呢? 所以共享内存的结构体肯定封装了一个唯一标识符。

    因为共享内存是OS提供的,所以它的生命周期是随内核的。如何删除共享内存呢?函数调用(最终会去调用系统调用),OS重启
    在这里插入图片描述

    如何让进程通过共享内存来交互呢?
    1,通过某种系统调用,在内存中创建一份内存空间。
    2,通过某种调用,让要通信的进程挂接到这份空间上。
    3,通信完成后去掉挂接。
    4,释放共享内存。

    先看看申请共享内存的系统调用

    在这里插入图片描述
    其他的共享内存操作:
    在这里插入图片描述
    .h头文件

     1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <fcntl.h>
      4 #include <stdlib.h>
      5 #include <stdio.h>
      6 #include <unistd.h>
      7 #include <sys/ipc.h>
      8 #include <sys/shm.h>                                                                                                                         
      9 
     10 
     11 #define MYPATH "./"
     12 #define PR0_ID 0x11223344
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    server.c

       #include "commond.h"
      2 
      3 
      4 int main()
      5 {
      6 
      7      key_t key = ftok(MYPATH,PR0_ID); //生成OS层面共享内存的标识符
      8      if(key < 0)
      9      {
     10         perror("ftok");
     11         exit(-1);
     12      }
     13      
     14      //printf("key::%d\n",key);
     15      
     16      //生成共享内存,生成全新的,并且指明权限(类似于文件)
     17      int shmid = shmget(key,4096,IPC_CREAT | IPC_EXCL | 0664);
     18      if(shmid < 0)
     19      {
     20        perror("shmget");
     21        exit(-1);
     22      }                                                                                                                                       
     23 
     24     //挂接
     25      char* shmsg = (char*)shmat(shmid,NULL,0);
     26      //printf("挂接成功\n");
     27      sleep(15);
            //读出共享内存的数据
     30      while(1)                                                                                                                                
     31      {
     32        printf("%s\n",shmsg);
     33        sleep(1);
     34      }
     35     
     36      
     37      //去挂接
     38      shmdt(shmsg);
     39 
     40      //删除共享内存
     41      shmctl(shmid,IPC_RMID,NULL);
     42      return 0;
     43 }
    
    
    • 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

    client.c

        #include "commond.h"
      2 
      3 
      4 int main()
      5 {
      6     //获得相同的共享内存标识符
      7     key_t key = ftok(MYPATH,PR0_ID);
      8     if(key < 0)
      9     {
     10       perror("ftok");
     11       exit(-1);
     12     }
     13 
     14     int shmid = shmget(key,4096,IPC_CREAT);//如果存在则返回已经存在的
     15 
     16     //挂接
     17     char* shmsg = (char*)shmat(shmid,NULL,0);
     18     sleep(10); 
     19 
     20     //写入共享内存的数据
     21     char c = 'A';
     22     while(c <= 'Z')                                                                                                                          
     23     {
     24         shmsg[c-'A'] = c;
     25         c++;
     26         shmsg[c-'A'] = 0;
     27         sleep(2);
       } 
     29 
     30     //去挂接
     31     shmdt(shmsg);
     32 
     33     //我们不需要去删除
     34 
     35     return 0;
     36 }
    
    
    
    • 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

    在这里插入图片描述

  • 相关阅读:
    mac下使用jadx反编译工具
    浅谈Jmeter性能测试流程
    CSS 3 五光十色的变色龙动画的制作
    MySQL面试题合集
    数字机器人如何更好的助力智慧政务?这里或许有你想要的答案
    安装mmcv及GPU版本的pytorch及torchvision
    内网渗透-隧道代理转发
    从-99打造Sentinel高可用集群限流中间件
    QGC地面站使用教程
    图文详细解决IDEA使用Debug模式启动项目一直转圈圈跑起不来(亲测可以)
  • 原文地址:https://blog.csdn.net/CL2426/article/details/127539803