美文网首页
进程通信:共享内存,套接字

进程通信:共享内存,套接字

作者: wayyyy | 来源:发表于2021-06-30 17:45 被阅读0次

    进程通信方式:共享内存

    共享内存.png

    共享内存本质上就是每个进程将虚拟地址空间指向共享内存块中,当一个进程往一个共享内存快中写入了数据,共享这个内存区域的所有进程就可用都看到其中的内容。

    mmap
    void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
    
    返回错误类型:
    1 EACCES:访问出错
     2 EAGAIN:文件已被锁定,或者太多的内存已被锁定
     3 EBADF:fd不是有效的文件描述词
     4 EINVAL:一个或者多个参数无效
     5 ENFILE:已达到系统对打开文件的限制
     6 ENODEV:指定文件所在的文件系统不支持内存映射
     7 ENOMEM:内存不足,或者进程已超出最大内存映射数量
     8 EPERM:权能不足,操作不允许
     9 ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
    10 SIGSEGV:试着向只读区写入
    11 SIGBUS:试着访问不属于进程的内存区
    
    • start
      映射区的开始地址

    • length
      映射区的长度

    • port
      期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

      1 PROT_EXEC :页内容可以被执行
      2 PROT_READ :页内容可以被读取
      3 PROT_WRITE :页可以被写入
      4 PROT_NONE :页不可访问
      
    • flags
      指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体。

       1 MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
       2 MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
       3 MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
       4 MAP_DENYWRITE //这个标志被忽略。
       5 MAP_EXECUTABLE //同上
       6 MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
       7 MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
       8 MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
       9 MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
      10 MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
      11 MAP_FILE //兼容标志,被忽略。
      12 MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
      13 MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
      14 MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
      
    • fd
      有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1

    • offset
      被映射对象内容的起点

    mmap 在 write 和 read 时发生了什么?
    write:
    1、进程(用户态)将需要写入的数据直接copy到对应的mmap地址(内存copy)
    2、若mmap地址未对应物理内存,则产生缺页异常,由内核处理
    3、若已对应,则直接copy到对应的物理内存
    4、由操作系统调用,将脏页回写到磁盘(通常是异步的)

    read:


    image.png

    mmap要比普通的read系统调用少了一次copy的过程。

    参考文章:
    认真分析mmap:是什么 为什么 怎么用
    https://www.jianshu.com/p/755338d11865

    shmget
    • API
      • 创建共享内存
        #include <sys/ipc.h>
        #include <sys/shm.h>
        
        int shmget(key_t key, size_t size, int shmflg);
            返回值:成功返回共享内存的id,失败返回-1;
        
        • key:和上面介绍的信号量的semget函数的参数key一样;
        • size:表示要申请的共享内存的大小,一般是4k的整数倍;
        • flags:IPC_CREATIPC_EXCL 一起使用,则创建一个新的共享内存,否则返回-1。IPC_CREAT单独使用时返回一个共享内存,有就直接返回,没有就创建。
      • 挂接函数
        void *shmat(int shmid);
            返回值:返回这块内存的虚拟地址;
        
        shmat的作用是将申请的共享内存挂接在该进程的页表上,是将虚拟内存和物理内存相对应;
      • 去挂接函数
        int shmdt(const void *shmaddr);
            返回值:失败返回-1;
        
        shmdt的作用是去挂接,将这块共享内存从页表上剥离下来,去除两者的映射关系
      • 删除共享内存
        int shmctl(int shmid,int cmd,const void* addr);
        
        shmctl用来设置共享内存的属性。当cmd是IPC_RMID时可以用来删除一块共享内存。

    共享内存类似消息队列和信号量,它的生命周期也是随内核的,除非用命令才可以删除该共享内存。

    ipcs -m //查看创建的共享内存的个数
    ipcrm -m shm_id //删除共享内存
    
    • 示例
      #ifndef __COMM__
      #define __COMM__
      #include<stdio.h>
      #include<sys/types.h>
      #include<sys/ipc.h>
      #include<sys/shm.h>
      #include<unistd.h>
      
      #define PATHNAME "."
      #define PROCID 0x6666
      #define SIZE 4096*1
      
      int CreatShm();
      int GetShm();
      int DestroyShm(int shm_id);
      static int CommShm(int flag);
      
      #endif
      
      #include"comm.h"
      
      int CreatShm()
      {
          return CommShm(IPC_CREAT | IPC_EXCL | 0666);
      }
      
      static int CommShm(int flag)
      {
          key_t key = ftok(PATHNAME, PROCID);
          if (key < 0) {
              perror("ftok");
              return -1;
          }
          int shm_id = shmget(key, SIZE, flag);
          if (shm_id < 0)  {
              perror("shmget");
              return -2;
          }
          return shm_id;
        }
      
      int GetShm()
      {
          return CommShm(IPC_CREAT);
      }
      
      int DestroyShm(int shm_id)
      {
          int ret = shmctl(shm_id, IPC_RMID, NULL);
          if (ret < 0) {
              perror("shmctl");
              return -1;
          }
          return 0;
      }
      
      #include"comm.h"
      
      void testserver()
      {
          int shm_id = CreatShm();
          printf("shm_id=%d\n", shm_id);
          char *mem = (char *)shmat(shm_id, NULL, 0);
          while (1) {
              sleep(1);
              printf("%s\n", mem);
          }
          shmdt(mem);
          DestroyShm(shm_id);
      }
      
      int main()
      {
          testserver();
          return 0;
      }
      
      #include"comm.h"
      
      void testclient()
      {
          int shm_id = GetShm();
          char *mem = (char *)shmat(shm_id, NULL, 0);
          int index = 0;
          while(1) {
              sleep(1);
              mem[index++] = 'A';
              index %= (SIZE - 1);
              mem[index] = '\0';
          }
          shmdt(mem);
          DestroyShm(shm_id);
      }
      
      int main()
      {
          testclient();
          return 0;
      }
      

    进程通信方式:套接字

    • 域套接字

    • TCP套接字

    相关文章

      网友评论

          本文标题:进程通信:共享内存,套接字

          本文链接:https://www.haomeiwen.com/subject/gbwrultx.html