精华内容
下载资源
问答
  • 共享内存
    千次阅读
    2022-03-16 20:42:14

    1 共享内存申请

    共享内存申请比较简单,这里采用的是Linux系统共享内存分配的函数实现的。

    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    
    ngx_int_t
    ngx_shm_alloc(ngx_shm_t *shm)
    {
        int  id;
    
        id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));
    
        if (id == -1) {
            ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                          "shmget(%uz) failed", shm->size);
            return NGX_ERROR;
        }
    
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);
    
        shm->addr = shmat(id, NULL, 0);
    
        if (shm->addr == (void *) -1) {
            ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
        }
    
        if (shmctl(id, IPC_RMID, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                          "shmctl(IPC_RMID) failed");
        }
    
        return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
    }
    
    
    void
    ngx_shm_free(ngx_shm_t *shm)
    {
        if (shmdt(shm->addr) == -1) {
            ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                          "shmdt(%p) failed", shm->addr);
        }
    }
    

    2 共享内存实现原理

    2.1 共享内存组织

    共享内存的管理工作是在:ngx_cycle内完成,主要是利用一个list进行组织。
    首先是ngx_list组织形式,采用的是将多个ngx_list_part单元串行组成的链表;同时每个ngx_list_part是由多个ngx_shm_zone_t组成构成。这样做的一个好处就是一次预先分配一个元素组,只有当第一个元素组用完才会申请新的元素组。可以达到减少频繁分配的问题。
    在这里插入图片描述
    这里需要展开对应ngx_shm_zone单元的介绍:
    首先是用户定义自己的数据结构并存入data中;第二部分是初始化函数,用户获取到ngx_shm_zone_t 共享内存单元的时候需要注册自己数据结构的初始化函数,其中zone返回是当前的共享内存的数据,而data返回的是上一次初始化的数据即历史数据;tag存放的是创建共享内存的模块;ngx_shm_t共享内存基本信息包括地址,大小,名称,log输出位置等。

    typedef struct {
        u_char      *addr;//共享内存地址
        size_t       size;//大小
        ngx_str_t    name;//名称
        ngx_log_t   *log;
        ngx_uint_t   exists;   /* unsigned  exists:1;  */
    } ngx_shm_t;
    typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);
    struct ngx_shm_zone_s {
        void                     *data;//用户数据
        ngx_shm_t                 shm; //共享内存结构体
        ngx_shm_zone_init_pt      init;//用户数据的初始化函数
        void                     *tag;//创建的模块
        ngx_uint_t                noreuse;  /* unsigned  noreuse:1; */
    };
    

    初始化函数具体调用位置是在创建完共享内存后即进行初始化ngx_init_cycle函数中进行:
    在这里插入图片描述

    2.2 slab共享内存管理机制

    slab内存分配方式是共享内存的管理机制,主要是利用最优选择的思路,当我们申请内存块时只会返回恰好符合请求大小的内存块。基本原理是把内存按照4k一页分成若干页,将每页放置不同大小的内存块,然后利用bitmap在页首进行标识内存块是否被使用。只需要变量bitmap查找空闲的内存块从而提高查找效率。
    在这里插入图片描述
    将存放不同大小的内存块的页面利用链表顺序串联,这样利用slots数组存放链表的首页。这样可以直接根据要分配内存大小进行寻址。
    在这里插入图片描述
    这里还有一个小操作提高查找空闲内存,当一个页面没有空闲的内存块时需要从队列中脱离变成一个单独的节点。

    2.3 slab与ngx_shm_zone_t 关系

    整体的初始化动作是在ngx_init_zone_pool函数进行:利用shm中addr存放slab_pool对象,并完成slab的初始化动作。

    ngx_slab_pool_t  *sp;
    sp = (ngx_slab_pool_t *) zn->shm.addr;
    sp->addr = zn->shm.addr;
    ngx_slab_init(sp);
    

    3 共享内存应用

    共享内存的使用场景,这里分析ssl模块的共享内存使用方法,可以推广到其他模块使用。
    首先是是进行共享内存的ngx_shm_zone_t分配,这是一个比较简单的操作,从共享内存返回一块空闲的shm_zone.

    ngx_shm_zone_t  *shm_zone;//共享内存单元
    ngx_conf_t *cf;//ngx配置
    ngx_str_t   name;//共享内存名称
    ngx_int_t    n;//分配共享内存的大小
    ngx_module_t  ngx_stream_ssl_module;//使用共享内存的模块,防止模块间重名
    scf->shm_zone = ngx_shared_memory_add(cf, &name, n,&ngx_stream_ssl_module);//从
    if (scf->shm_zone == NULL) {
         return NGX_CONF_ERROR;
     }
    //初始化函数设置
     scf->shm_zone->init = ngx_ssl_session_cache_init;
    

    初始化函数:data返回的是上一次分配的数据,如果存在直接返回。否则先取出slab_pool进程分配操作调用ngx_slab_alloc分配一个内存块大小,可以是数据结构。这里分配的是一个红黑树结构,分配完成后就可以直接初始化红黑树。

    ngx_int_t
    ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
    {
        size_t                    len;
        ngx_slab_pool_t          *shpool;
        ngx_ssl_session_cache_t  *cache;
    
        if (data) {
            shm_zone->data = data;
            return NGX_OK;
        }
    	//获取slab_pool
        shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
    
        if (shm_zone->shm.exists) {
            shm_zone->data = shpool->data;
            return NGX_OK;
        }
    	//为数据分配slab内存块
        cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
        if (cache == NULL) {
            return NGX_ERROR;
        }
    	//赋值到slab_pool管理
        shpool->data = cache;
        shm_zone->data = cache;
    	//红黑树数据初始化
        ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
                        ngx_ssl_session_rbtree_insert_value);
    	//队列初始化
        ngx_queue_init(&cache->expire_queue);
    	//设置log
        len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
    	//为log分配共享内存
        shpool->log_ctx = ngx_slab_alloc(shpool, len);
        if (shpool->log_ctx == NULL) {
            return NGX_ERROR;
        }
    	//写log
        ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
                    &shm_zone->shm.name);
    
        shpool->log_nomem = 0;
    
        return NGX_OK;
    }
    
    更多相关内容
  • 【需求描述】 1、共享内存保存信息 2、提供接口写入共享内存 3、提供接口获取共享内存 【编写语言:C】 【环境:linux】 1、写入内存一千万条数据 耗时:5.356秒 2、读取内存一千万条数据 耗时:1.449秒
  • C++共享内存进程间通信 FileMapping用于进程间快速通信
  • 共享内存

    千次阅读 2021-04-10 14:16:40
    1.共享内存 2.共享内存操作过程 3.共享内存函数 4.共享内存资源查看和删除 5.实现内存共享程序 6.总结

    1.共享内存

    共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

    共享内存示意图:
    在这里插入图片描述

    在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

    2.共享内存操作过程

    1.创建/打开共享内存
    2.将共享内存映射到进程的虚拟地址空间
    3.通过任意的内存操作,对共享内存进行操作
    4.解除映射关系(共享内存会记录当前的进程映射链接数)
    5.删除共享内存

    3.共享内存函数

    1.创建共享内存:
    在这里插入图片描述
    通过ftok生成的key值是唯一的。

    int shmget(key_t key, size_t size, int shmflg);
    
    • 参数:
      • key:这个共享内存段名字
      • size:共享内存大小,size大小一般为4096的整数倍
      • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
    • 返回值:
      成功返回一个非负整数,即该共享内存段的标识码;失败返回-1,
      这个返回值标识符是给用户使用的。而通过ftok创建的是系统识别的。

    2.删除共享内存
    shmctl函数:

    功能:用于控制共享内存
    原型
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    参数
    	shmid:由shmget返回的共享内存标识码
    	cmd:将要采取的动作(有三个可取值)
    	buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
    返回值:
    	成功返回0;失败返回-1
    

    下图是cmd的命令
    在这里插入图片描述
    3.将共享内存段连接到进程地址空间

    shmat函数:

    原型
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    参数
    shmid: 共享内存标识
    shmaddr:指定连接的地址
    shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
    返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
    

    说明:

    • shmaddr为NULL,核心自动选择一个地址
    • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
    • shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -(shmaddr % SHMLBA)
    • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

    4.共享内存段与当前进程取消关联

    shmdt函数

    原型
    int shmdt(const void *shmaddr);
    参数
    shmaddr: 由shmat所返回的指针
    返回值:成功返回0;失败返回-1
    注意:将共享内存段与当前进程脱离不等于删除共享内存段
    

    4.共享内存资源查看和删除

    ipcs -a 是默认的输出信息 打印出当前系统中所有的进程间通信方式的信息
    ipcs -m 打印出使用共享内存进行进程间通信的信息
    ipcs -q 打印出使用消息队列进行进程间通信的信息
    ipcs -s 打印出使用信号进行进程间通信的信息

    ipcs -m //查看共享内存
    

    在这里插入图片描述

    ipcrm -M shmkey: 移除用shmkey创建的共享内存段
    ipcrm -m shmid : 移除用shmid标识的共享内存段
    ipcrm -Q msgkey : 移除用msqkey创建的消息队列
    ipcrm -q msqid : 移除用msqid标识的消息队列
    ipcrm -S semkey : 移除用semkey创建的信号
    ipcrm -s semid : 移除用semid标识的信号

    ipcrm -m shmid //
    

    在这里插入图片描述

    5.实现内存共享程序

    共享内存,底层不提供任何同步与互斥机制。
    client端:

      1 #include <stdio.h>
        2 #include <sys/types.h>
        3 #include <sys/ipc.h>
        4 #include <sys/shm.h>
        5 #include "comm.h"
        6 #include <unistd.h>
        7 
        8 int main()
        9 {
       10   key_t k = ftok(PATHNAME, PROJ_ID);
    W> 11   printf("key:%p\n", k);
       12   
       13   int shmid = shmget(k, SIZE, IPC_CREAT);//唯一标识shmid,SIZE一般设为4096的整数倍
       14   
       15   if(shmid < 0){
       16     perror("shmget");
       17     return 1;
       18   }
       19 
       20   printf("shmid :%d\n",shmid);
       21 
       22   char *str = (char*)shmat(shmid, NULL, 0);//连接
       23   
       24   char c = 'a';
       25   for(; c <='z'; c++){
       26     str[c-'a'] = c;
       27     sleep(1);
       28   }
       29 
       30   shmdt(str);
       31 
       32   sleep(5);
       33   return 0;
       34 }  
    

    server端:

        1 #include <stdio.h>
        2 #include <sys/types.h>
        3 #include <sys/ipc.h>
        4 #include <sys/shm.h>
        5 #include "comm.h"
        6 #include <unistd.h>
        7 
        8 int main()
        9 {
       10   key_t k = ftok(PATHNAME, PROJ_ID);
    W> 11   printf("key:%p\n", k);
       12 
       13   int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);
       14 
       15   if(shmid < 0){
       16     perror("shmget");
       17     return 1;
       18   }
       19 
       20   printf("shmid :%d\n",shmid);
       21 
       22 
       23   char *str = (char*)shmat(shmid, NULL, 0);
       24 
       25   while(1){
       26     sleep(1);
       27     printf("%s\n", str);
       28   }
       29 
       30   shmdt(str);
       31 
       32   shmctl(shmid, IPC_RMID, NULL);                                                                                                                                                         
       33 
       34   return 0;
       35 }
    
    

    运行:
    在这里插入图片描述

    6.总结

    1. 内存共享,只需要发生一次拷贝,速度是进程通信之中最快的
    2. 内存共享之中的key是OS之中共享内存的唯一标识,shmid是用户层面的定位标识
    3. IPC资源的生命周期随内核,只要不删除,就一直存在于内核中,除非重启系统
    4. 共享内存只有在当前映射链接数为0时,才会真正被删除。
    展开全文
  • 之前在网上找的都是只能传递字符串的共享内存,转换成结构体格式经常出现无法读取的问题
  • 运用windwosAPI共享内存的使用实例
  • 进程间通信之共享内存

    千次阅读 2021-09-15 14:08:51
    共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间...

    目录

    一.共享内存实现进程间通信的原理

    二.管理共享内存的数据结构

    三.共享内存函数

    四.实现进程间通信


    接博客:进程间通信之管道

    一.共享内存实现进程间通信的原理

            共享内存实际是操作系统在实际物理内存中开辟的一段内存。

            共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。

            当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。

             要实现进程间通信需要两个进程能够看到同一块空间,系统开辟的共享内存就是两进程看到的同一资源。

            注意:共享内存实现进程间通信是进程间通信最快的。

    关于页表的补充:

            进程地址空间里有一个内核区域,它们也会在实际物理内存开辟空间,也会有页表与那块空间形成映射关系,这个页表叫做内核级页表。因为内核只有一个,所以每个进程都相同的。说明进程都共用实际物理内存上的内核空间。

            除内核空间以外的空间,与实际物理空间之间的页表,称为用户级页表。每个进程可能不同。

    二.管理共享内存的数据结构

            共享内存实现进程间通信不只仅限于两个进程之间,可以用于多个进程之间。并且系统中可能会有多个进程在进行多个通信。所以系统需要将这些通信的进程管理起来。如果不管理,操作系统怎么知道这块共享内存挂接了哪个进程等信息。

            如何管理?先描述和组织。

            查看内核源代码,可以看到系统描述共享内存的数据结构如下:

    /* Obsolete, used only for backwards compatibility and libc5 compiles */
    struct shmid_ds {
    	struct ipc_perm		shm_perm;	/* operation perms */
    	int			shm_segsz;	/* size of segment (bytes) *///共享内存空间大小
    	__kernel_time_t		shm_atime;	/* last attach time *///挂接时间
    	__kernel_time_t		shm_dtime;	/* last detach time *///取消挂接时间
    	__kernel_time_t		shm_ctime;	/* last change time *///改变时间
    	__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */
    	__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */
    	unsigned short		shm_nattch;	/* no. of current attaches *///进程挂接数
    	unsigned short 		shm_unused;	/* compatibility */
    	void 			*shm_unused2;	/* ditto - used by DIPC */
    	void			*shm_unused3;	/* unused */
    };

             描述共享内存的数据结构里保存了一个ipc_perm结构体,这个结构体保存了IPC(进程将通信)的关键信息。

    /* Obsolete, used only for backwards compatibility and libc5 compiles */
    struct ipc_perm
    {
    	__kernel_key_t	key;//共享内存的唯一标识符
    	__kernel_uid_t	uid;
    	__kernel_gid_t	gid;
    	__kernel_uid_t	cuid;
    	__kernel_gid_t	cgid;
    	__kernel_mode_t	mode; //权限
    	unsigned short	seq;
    };

    其中的key是共享内存的唯一标识。

    三.共享内存函数

    • ftok函数

           作用:算出一个唯一的key返回。

            参数:第一个是地址,第二个是至少8为的项目id,不能为0,两参数可以是任意值,但是要符合格式。

            返回值:ftok如果成功返回一个key值,如果失败返回-1。如果失败了再重新填写参数即可。 

            ftok中的参数可以随便填写,但是要符合格式,ftok只是利用参数,再运用一套算法,算出一个唯一的key值返回。这个key值可以传给共享内存参数,作为struct ipc_perm中唯一标识共享内存的key。

            ftok函数并没有涉及内核层面。

    #pragma once
    
    #define PATHNAME "./"
    #define PROJ_ID 0x666
    
     #include<stdio.h>          
     #include<sys/types.h>     
     #include<sys/ipc.h>   
     #include"com.h"      
                           
     int main(){      
       key_t k = ftok(PATHNAME,PROJ_ID);    
       if(k==-1){              
         perror("ftok error");     
         return 1;    
       }             
       printf("ftok : %d\n",k);     
     
       return 0;       
    } 
    
    • shmget函数

    作用:创建一个共享内存 

    参数:

            key:为共享内存的名字,一般是ftok的返回值。

            size:共享内存的大小,以page为单位,大小为4096的整数倍。

            shmflg:权限标志,常用两个IPC_CREAT和IPC_EXCL,一般后面还加一个权限,相当于文件的权限。

                                    IPC_CREAT:创建一个共享内存返回,已存在打开返回

                                    IPC_EXCL:配合着IPC_CREAT使用,共享内存已存在出错返回。

                                    使用:IPC_CREAT | IPC_EXCL | 0666

    返回值:

            成功返回一个非负整数,即共享内存的标识码,失败返回-1。

            为什么已经有一个key来标识共享内存,还需要一个返回值来标识共享内存?因为key是内核级别的,供内核标识,shmget返回值是用户级别的,供用户使用的。

    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/shm.h>
    #include"com.h"
    
    int main(){
    	key_t k = ftok(PATHNAME, PROJ_ID);
    	if (k == -1){
    		perror("ftok error");
    		return 1;
    
    	}
    
    	printf("ftok : %d\n", k);
    
    	int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666);
    	if (shmid == -1){
    		perror("shmget error");
    		return 1;
    
    	}
    	printf("shmid : %d\n", shmid);
    	return 0;
    
    }

            我们发现当进程创建了一个共享内存,没有释放,进程结束后,共享内存还在,所以第二次执行程序,会报错(报错是因为IPC_EXCL)。

            这里得出一个结论:IPC(进程将通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget创建的共享内存要释放掉,不然会内存泄漏。

            可以用命令行来释放共享内存:ipcrm -m shmid(shmget返回值)

             也可以用下面的函数来释放共享内存

    • shmctl函数

    作用:用于控制共享内存

    参数:        shmid:共享内存的标识

                       cmd:以什么方式来控制共享内存。IPC_RMID是释放共享内存

                       buf:指向一个共享内存的数据结构 。struct shmid_ds

    返回值:成功返回0,失败返回-1。

      1 #include<stdio.h>
      2 #include<sys/types.h>
      3 #include<sys/ipc.h>
      4 #include<sys/shm.h>
      5 #include"com.h"
      6 
      7 int main(){
      8   key_t k = ftok(PATHNAME,PROJ_ID);//获取一个唯一标识符key
      9   if(k==-1){
     10     perror("ftok error");
     11     return 1;
     12   }
     13 
     14   printf("ftok : %d\n",k);
     15 
     16   int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666);//创建共享内存
     17   if(shmid == -1){
     18     perror("shmget error");
     19     return 1;
     20   }
     21   printf("shmid : %d\n",shmid);
     22 
     23   int sh = shmctl(shmid,IPC_RMID,NULL);//删除共享内存
     24   if(sh == -1){                                                                                                                                          
     25     perror("shmctl");
     26     return 1;
     27   }
     28   return 0;
     29 }

     现在就可以执行多次:

    •  shmat函数

    作用:使创建的共享内存与调用该函数进程的进程地址空间参数关联。

    参数:

            shmid:共享内存的标识,shmget的返回值。

            shmaddr:指定进程地址空间连接的地址。如果设置为null,默认让系统定要关联的地址。

            shmflg: 权限,常见有两个SHM_RDONLY(只读)和SHM_REMAP(重新映射一个进程地址空间没这样shmaddr不能为空)。设为0,系统默认。

     返回值:

            返回映射到进程地址空间共享区的开始地址。

    • shmdt函数

     作用:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间。

    参数:

            shmaddr:共享内存映射到进程地址空间的地址。shmat返回值。

    返回值:

            成功返回0,失败返回-1

    shmat和shmdt要一起使用才起作用。

      1 #include<stdio.h>                                                        
      2 #include<unistd.h>
      3 #include<sys/types.h>
      4 #include<sys/ipc.h>
      5 #include<sys/shm.h>
      6 #include"com.h"
      7 
      8 int main(){
      9   key_t k = ftok(PATHNAME,PROJ_ID);
     10   if(k==-1){
     11     perror("ftok error");
     12     return 1;
     13   }
     14 
     15   printf("ftok : %d\n",k);
     16   int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666);
     17   if(shmid == -1){
     18     perror("shmget error");
     19     return 1;
     20   }
     21   printf("shmid : %d\n",shmid); 
     22   //与进程地址空间产生关联
     23   char *str = (char *)shmat(shmid, NULL, 0);
     24   //10秒后删除关联
     25   sleep(10);
     26   shmdt(str);
     27 
     28   //int sh = shmctl(shmid,IPC_RMID,NULL);
     29   //if(sh == -1){      
     31   //  return 1;
     32   //}
     33   return 0;
     34 }               

     程序10秒钟删除共享内存与进程地址空间的关联:

    四.实现进程间通信

            实现进程间通信步骤:

    1. 创建共享内存
    2. 共享内存关联进程
    3. 删除共享内存与进程的关联
    4. 释放共享内存

    使用到上面的函数。

    实现一个用户client与服务器server之间的简单通信。

    需要client和server都和共享内存关联。client端不创建共享内存,不释放共享内存,server端创建共享内存,并且释放共享内存。

      1 #pragma once
      2 
      3 #define PATHNAME "./"
      4 #define PROJ_ID 0x666
      5 
      6 #define SIZE 4096 

    client代码:写入端

     1 #include<stdio.h>                                                        
      2 #include<unistd.h>
      3 #include<sys/types.h>
      4 #include<sys/ipc.h>
      5 #include<sys/shm.h>
      6 #include"com.h"
      7 
      8 int main(){
      9   key_t k = ftok(PATHNAME,PROJ_ID);
     10   if(k==-1){
     11     perror("ftok error");
     12     return 1;
     13   }
     14   //不创建共享内存,只是为了得到shmid
     15   int shmid = shmget(k, SIZE, 0);
     16   if(shmid == -1){
     17     perror("shmget error");
     18     return 1;
     19   }
     20   //与进程地址空间产生关联
     21   char *str = (char *)shmat(shmid, NULL, 0);
     22   char c='a';
     23   for(;c<='z';c++){
     24     str[c-'a']=c;
     25     sleep(5);
     26   }
     27   //删除关联
     28   shmdt(str);
     29   //不用释放共享内存,服务器端释放
     30 
     31   return 0;
     32 }   

    server代码:读出端

     1 #include<stdio.h>                                                        
      2 #include<unistd.h>
      3 #include<sys/types.h>
      4 #include<sys/ipc.h>
      5 #include<sys/shm.h>
      6 #include"com.h"
      7 
      8 int main(){
      9   key_t k = ftok(PATHNAME,PROJ_ID);
     10   if(k==-1){
     11     perror("ftok error");
     12     return 1;
     13   }
     14   //服务器关不要加IPC_EXCL,已存在不要保存,直接返回
     15   int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666);
     16   if(shmid == -1){
     17     perror("shmget error");
     18     return 1;
     19   }
     20   //与进程地址空间产生关联
     21   char *str = (char *)shmat(shmid, NULL, 0);
     22   //读数据
     23   while(1){
     24     printf("%s\n",str);
     25     sleep(1);
     26   }
     27   //删除关联
     28   shmdt(str);
     29   int sh = shmctl(shmid,IPC_RMID,NULL);
     30   if(sh == -1){
     31     perror("shmctl");
     32     return 1;
     33   }
     34   return 0;
     35 }         
    

    代码输出,每隔5秒写端往共享内存中写一个字符,读端每个一秒读一下数据。

    这里有一个问题:因为写端比读端慢,相比较于管道,当写端比读端慢时,没有写入时,读端要进入阻塞状态,但是上面发现使用共享内存的读端一直在读,并没有阻塞。

    这里得出一个结论:共享内存实现的进程间通信底层不提供任何同步与互斥机制。如果想让两进程很好的合作起来,在IPC里要有信号量来支撑。

            注意:两进程之间用通过同样的规则即ftok参数要一样,来获取同样的key值,然后在创建共享内存时,用这个key值唯一标识共享内存,所以两进程时通过同样的key值来实现看到同一份资源。

    补充:

    展开全文
  • 介绍共享内存的定义,及其共享内存的创建函数和释放函数

    匿名管道和命名管道都是基于文件的进程间通信,SystemV方案是在OS层面专门为进程间通信设计的一个方案,然后通过系统调用(system call)给用户提供通信接口

    SystemV方案包含三种:共享内存、消息队列、信号量

    下面要说的就是共享内存的创建和释放


    目录

    一、什么是共享内存??

    1、共享内存的定义

    2、共享内存的特点

    二、使用共享内存的准备和收尾工作

    三、shmget函数(shared memory get)

    1、 参数解析

    (1) 第一个参数 key

    (2) 第二个参数 size

    (3) 第三个参数 shmget

    2、返回值解析

    3、使用shmget函数

    4、共享内存的生命周期

    四、shmctl(shared memory control)

    1、参数解析

    2、返回值解析

    3、使用shmctl 函数

    五、为什么共享内存的ID是0,1,2 ... ? 


    一、什么是共享内存??

    1、共享内存的定义

    由于进程通信的本质是要让两个不同的进程看到同一份资源,我们可以在物理内存上开辟一块空间,这块空间被称为共享内存,然后让这两个进程通过某种方式都能访问到这块内存,这样的话,两个进程之间就可以通信了

    2、共享内存的特点

    第一,和创建进程类似,进程被创建的时候,会被分配一个pid来标识这个进程的唯一性,同时也方便OS管理这些进程,因此共享内存在被创建的时候,会被分配一个“ID”来标识唯一性

    第二,共享内存可以允许存在多个,为了区分这些共享内存,我们上面引入了“ID”的概念,但是要如何让两个进程连上同一个共享内存呢??

    就好比,我要和人solo(通信),我创建了一个房间(共享内存),这个房间就有了房间号(共享内存的ID),是个人都能进这个房间,根本没法通信,所以我们要设置房间密码。因此为了通信,我们需要两样东西,一个是房间号,一个是房间密码

    二、使用共享内存的准备和收尾工作

    当我们需要使用共享内存时,我们要做的准备工作是:

                    — 通过某种调用,在内存中开辟一块空间(shmget)

                    — 通过某种调用,让两个进程挂接到这个新开辟的空间上(shmat)

    当我们不需要使用共享内存时,我们需要做的收尾工作是:

                    — 断开进程和共享内存之间的关联(shmdt)

                    — 释放共享内存(shmctl)

    每一步都对应着一个系统调用接口,下面要说的就是这四个系统调用接口

    三、shmget函数(shared memory get)

    这是共享内存的创建函数,调用以后会向内核申请内存,但是需要注意的是,共享内存是以“页”为单位的,一页是4KB = 4096bytes,所以一般建议申请共享内存的大小是4KB的整数倍!

    如果申请了4097个字节,那么OS会给你4096*2个字节的空间

    1、 参数解析

    下面是shmget函数的声明以及要用到的头文件

    (1) 第一个参数 key

    第一个参数是唯一标识编号,也就是前面说到的房间密码,这个是由用户自己设置的,一般是通过ftok函数来,也可以自己随意设置一个整数

     ftok函数的第一个参数:路径名

     ftok函数的第二个参数:项目ID

    关于这个函数无需想的太复杂,简单来说就是,从路径名中取出一部分,然后再从ID中取出一部分,最后再把两部分组合一下形成一个整数,我们就把这个整数当作“房间密码”

    注意:ftok被不同进程调用,只要路径名和ID是一样的,生成的整数就是一样的

    (2) 第二个参数 size

     第二个参数是开辟共享内存的大小,一般建议是4KB的整数倍(原因详见第三部分开头)

    (3) 第三个参数 shmget

    第三个参数是创建共享内存的方式以及设置权限

     类似于write函数的第二个参数,属于位运算输入

    IPC_CREAT:可以单独使用,如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内

                            存的ID;如果已经存在,则沿用已有的共享内存,函数返回值是已有的共享内存的

                            ID

    IPC_EXCL:无法单独使用,要配合IPC_CREAT使用,即 IPC_CREAT | IPC_EXCL

    IPC_CREAT | IPC_EXCL:如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内

                                            存的ID;如果已经存在,则报错

    IPC_CREAT | IPC_EXCL | 0664:开辟共享内存的同时,设置共享内存的访问权限

    2、返回值解析

    如果共享内存开辟成功,则返回共享内存的ID(即房间号);否则返回 -1.

    3、使用shmget函数

    下面我们在使用shmget函数的同时要验证一下上面红色的注意事项

    和之前的命名管道通信一样,继续使用server.c 和 client.c 来通信,公共数据放在common.h

     现在我们可以来看一下测试的结果,我们会发现服务端和客户端使用的密码是一样的,这也就验证了上面红色部分

     我们在命令行输入下面的指令查看创建好的共享内存

    ipcs                     #显示SystemV方案的全部,即共享内存、消息队列、信号量
    ipcs -m                  # -m 显示共享内存
    ipcs -q                  # -q 显示共享消息队列
    ipcs -s                  # -s 显示共享信号量

    4、共享内存的生命周期

    从上面的测试结果,我们会发现,进程退出以后,共享内存依然存在,换到我们之前的匿名管道,管道的生命周期是随进程的,进程退出以后,管道也就不存在了

    共享内存不属于任何进程,不归进程管理,除非调用释放函数,否则要不要释放是OS决定的,因此共享内存的生命周期是随内核的!!

    释放的方式有三种:

    - 关机

    - 调用释放共享内存的函数 shmctl

    - 命令行释放

    ipcrm -m 1             # ipcrm -m 共享内存的id

    四、shmctl(shared memory control)

    这是控制共享内存的函数,目前我们只需要知道,怎么通过这个函数释放共享内存即可

    1、参数解析

    下面是共享内存控制函数的函数声明及需要的头文件

    第一个参数是 共享内存的id

    第二个参数是 控制指令(我们选择删除共享内存的那个指令)

    第三个参数是 shmid所指向的共享内存的地址,既然空间被释放以后,地址就赋值为null

    2、返回值解析

    释放成功则返回 0;释放失败返回 -1

    3、使用shmctl 函数

    我们在原本的server.c中加入下面的内容

     我们发现,共享内存最终被释放了

    五、为什么共享内存的ID是0,1,2 ... ? 

    我们观察上面的图,会发现shmid是 0,1,2这样的整数,和前面的文件一样,很容易联想到数组的下标。这确实是数组的下标,但是这个数组中存放着什么呢??

    最开始也说了,共享内存以结构体的形式被管理着,消息队列和信号量也是如此,下面依次是共享内存、消息队列、信号量的结构体。

    首先需要明确的是,上面这些共享内存的结构体shmid_ds、消息队列的结构体msgid_ds、信号量的结构体semid_ds都不是关键,关键的是这个ipc_perm

    其次,每种资源都嵌套了ipc_perm结构体,我们只要知道了 各自ipc_perm的地址,就能通过C++切片寻找到每种资源的地址

    因此,对每种资源结构体的管理就变成了对ipc_perm的管理 ——》 这里的shmid就是这个存储ipc_perm结构体地址的数组下标

    展开全文
  • Linux_System V进程通信_共享内存+命令查看共享内存

    千次阅读 多人点赞 2021-11-11 19:32:57
    共享内存原理2共享内存建立过程①申请共享内存shmget函数(sys/ipc.h -sys/shm.h)(创建共享内存)保证共享内存的唯一性ftok函数的代码(sys/types.h sys/ipc.h)shmflg:创建共享内存的选项。申请共享内存代码命令查看...
  • 本文首先介绍了众所周知的共享内存用户态API,然后介绍了相关的内核主要数据结构,并逐一分析了shmget、shmat、数据访问、shmdt的内核实现及数据结构之间的动态关系,从数据的关联图即可一窥共享内存的实现机制。
  • 往小了说,golang建议使用channel来共享信息而不是使用共享内存,这是一种优雅的方式,避免了数据同步带来的繁琐和低效。 往大了说,本质上还是让资源去调度请求,而不是让请求去调度资源。 资源就那么多,所有请求...
  • qt共享内存

    千次阅读 2021-07-02 15:36:35
    查看此文,共享内存,相信大家都知道进程间通信,即IPC(Inter-Process Communication),进程间通信是指两个进程的数据之间产生交互。而共享内存是实现进程通信的方式之一,其他通信方式可看下图: 查看各通信方式...
  • 想必在Linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题,你的程序在运行时占用了多少内存(物理内存)? 通常我们可以通过top命令查看进程占用了多少内存。这里我们可以看到VIRT、RES...
  • QT间进程通信之共享内存

    热门讨论 2013-12-09 15:42:50
    Qt提供了一种安全的共享内存的实现QSharedMemory,以便在多线程和多进程编程中安全的使用。比如说QQ的聊天的客户端,这里有个个性头象,当点击QQ音乐播放器的时候,启动QQ音乐播放(启动一QQ音乐播放器的进程)这时...
  • Linux共享内存

    千次阅读 2022-02-04 22:56:58
    文章目录一、使用流程使用步骤1.ftok函数生成键值2.shmget函数创建共享存储空间并返回一个共享存储标识符3.shmat函数获取第一个可用共享内存空间的地址4.shmdt函数进行分离5.shmctl函数对共享内存进行控制编程验证...
  • 文章目录systemV 标准共享内存通信原理共享内存的理解共享内存的创建--shmget函数查看共享内存和释放共享内存方式挂在共享内存--shmat函数去挂在共享内存共享内存使用的基本框架逻辑代码共享内存完成进程之间通信...
  • 进程间通信之共享内存 shared memory ) 1 效率最高 2 存在竞态 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) ...
  • 进程间通信-共享内存

    千次阅读 2022-04-23 13:40:45
    共享内存 共享内存的理解 共享内存是System V版本的一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。...
  • 继续以“以通信方式共享内存,不要以共享内存方式通信”这句话展开。 从云原生开始。 云原生是面向微服务的架构,而消息传递是微服务交互的媒介,每个工人都接触过关于消息队列的概念,正是消息支撑了云原生微服务。 ...
  • 易语言共享内存检测重复运行模块源码,共享内存检测重复运行模块,L_重复运行,L_写共享内存,L_读共享内存,检测内存,Z_共享内存_CreateFileMappingA,Z_共享内存_OpenFileMappingA,Z_共享内存_MapViewOfFile,Z_共享内存_...
  • 共享内存实现原理

    千次阅读 2021-01-19 15:17:03
    共享内存的使用实现原理   nmap函数要求内核创建一个新的虚拟存储器区域,最好是从地址start开始的一个区域,并将文件描述符fd指定对象的一个连续的片(chunk)映射到这个新的区域。    SHMMNI为128,表示系统中...
  • Linux之共享内存

    千次阅读 2022-02-24 09:46:56
    Linux之共享内存
  • Linux下进程间通信 之 共享内存

    千次阅读 2021-12-14 17:19:05
    一、共享内存的概念 共享内存是在两个或多个正在运行的进程之间共享内存区域的一种进程间的通信方式(就是 允许两个或多个不相关的进程访问同一个逻辑内存)。是 IPC中最快捷的通信方式,因为共享内存 方式的通信...
  • Android系统共享内存

    千次阅读 2020-11-18 10:42:58
    1.共享内存简介 共享内存是进程间通讯的一种方式,通过映射一块公共内存到各自的进程空间来达到共享内存的目的。 通常进程内存空间是4G,这个大小是由内存指针长度决定的,如果指针长度32位,那么地址最大编号为0...
  • linux实现共享内存同步的四种方法

    千次阅读 2021-05-11 12:29:01
    共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。它是IPC对象的一种。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有...
  • 本文介绍共享内存的概念及使用。进程间通信(IPC,InterProcess Communication) 是指在不同进程之间传播或交换信息,现在linux使用的进程间通信方式有:(1)管道(pipe)和命名管道(FIFO) (2)信号(signal) ...
  • CUDA加速——共享内存介绍及其应用

    千次阅读 2021-09-26 01:10:49
    一旦共享内存被定义并指定大小,系统将给所有线程块都分配相同大小的共享内存,比如定义一个大小为8 bytes的unsigned char型共享内存,那么所有线程块都会被分配一个8 bytes的unsigned char型共享内存。如下图所示...
  • 共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU...
  • 共享内存_shmget

    千次阅读 2021-10-07 20:00:06
    文章目录共享内存创建共享内存映射共享内存删除共享内存父子进程共享内存共享内存通信 共享内存   共享内存是被多个进程共享的一部分物理内存。一个进程向共享内存写入了数据,共享这个内存区域的所有进程就可以...
  • 一、引言 之前的管道学习中,我们利用命名管道生成了两个.fifo的文件,如果用户不懂技术,以为这个文件没用就可能把它们删除了。...共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,094,049
精华内容 437,619
关键字:

共享内存