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

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

作者: 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套接字

相关文章

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

    进程通信方式:共享内存 共享内存本质上就是每个进程将虚拟地址空间指向共享内存块中,当一个进程往一个共享内存快中写入...

  • Linux知识

    Linux进程间通信方式有:消息队列,命名管道,信号量,共享内存,Berkeley套接字 等 临界区...

  • 005:多线程

    进程 1:运行时的(runtime)应用程序。2:进程之间的内存不共享。3:进程间的通信使用socket(套接字)...

  • linux内核编程-IPC进程间通信

    进程间通信方式 方式管道(使用简单),信号量,信号(开销比较小),共享映射区(共享内存),消息队列,套接字(sok...

  • 传输层中的UDP、TCP

    1. 传输层做了些什么? 2. 进程与进程之间的通信 同一台设备下进程间通信的方式有:Unix域套接字共享内存 不...

  • Go:Unix域套接字

    关于同一个Linux主机上的进程之间的进程间通信(IPC)方式,有多个选择:例如FIFO、管道、共享内存、套接字等...

  • Binder的介绍与原理分析

    介绍 1,首先进程间的通信方式:管道、消息队列、共享内存、信号量、信号、socket套接字、信号等2, binde...

  • IPC机制

    1,跨进程通信,有几种方式,AIDL,socket套接字,contentProvider,messager,共享文...

  • 8.ipc

    进程间通信 Linux中的进程间通信主要有:管道、FIFO、消息队列、信号量、共享存储以及网络IPC中的套接字。 ...

  • 线程-进程间通信(操作系统、java、android)最全总结!

    大纲 操作系统进程间通信 进程的通信机制主要有:管道、有名管道、消息队列、信号量、共享空间、信号、套接字。 操作系...

网友评论

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

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