精华内容
下载资源
问答
  • 共享内存使用方法

    2020-08-08 16:33:12
    使用共享内存的一般步骤: (1)获取共享内存的ID; (2)将共享内存映射到本进程虚拟内存空间的某个区域; (3)不再使用时,接触映射; (4)不需要时,删除它。 注意:创建的共享内存是物理内存,必须要映射到本...

    共享内存

    使用共享内存的一般步骤:
    (1)获取共享内存的ID;
    (2)将共享内存映射到本进程虚拟内存空间的某个区域;
    (3)不再使用时,接触映射;
    (4)不需要时,删除它。
    注意:创建的共享内存是物理内存,必须要映射到本进程的内存空间才能使用。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    IPC_STAT获得的属性信息查找课本或其他资料

    代码演示:

    p1发送信息给p2

    int main()
    {
    	int shmid;
    	key_t key = ftok("/home/gec",1000); 
    	//申请共享内存
    	shmid=shmget(key,1024,IPC_CREAT|IPC_EXCL|0777); //新建共享内存
    	if(shmid==-1)
    	{
    		//进一步分析错误原因
    		if(errno==EEXIST) //共享内存已经存在
    		{
    			shmid=shmget(key,1024,0777); //打开共享内存
    		}
    		else //其他错误
    		{
    			perror("申请失败!\n");
    			return -1;
    		}
    	}
    	
    	//映射得到首地址
    	char *p=shmat(shmid,NULL,0);
    	if(p==NULL)
    	{
    		perror("映射失败!\n");
    		return -1;
    	}
    	
    	//通过共享内存发送信息给p2
    	while(1)
    	{
    		printf("请输入要发送给p2的信息!\n");
    		scanf("%s",p);
    	}
    	//shmctl(shmid,IPC_RMID,NULL);//删除共享内存
    }
    

    p2接收p1信息:

    int main()
    {
    	int shmid;
    	key_t key = ftok("/home/gec",1000); 
    	//申请共享内存
    	shmid=shmget(key,1024,IPC_CREAT|IPC_EXCL|0777); //新建共享内存
    	if(shmid==-1)
    	{
    		//进一步分析错误原因
    		if(errno==EEXIST) //共享内存已经存在
    		{
    			shmid=shmget(key,1024,0777); //打开共享内存
    		}
    		else //其他错误
    		{
    			perror("申请失败!\n");
    			return -1;
    		}
    	}
    	
    	//映射得到首地址
    	char *p=shmat(shmid,NULL,0);
    	if(p==NULL)
    	{
    		perror("映射失败!\n");
    		return -1;
    	}
    
    	//访问p1存放到共享内存中的信息
    	while(1)
    	{
    		printf("共享内存中p1发送过来的信息是:%s\n",p);
    		shmdt(p);//读完数据,解除映射
    	}	
    }
    

    获取共享内存属性信息:

    #include "myhead.h"
    
    /*
    	获取共享内存的属性信息
    */
    int main()
    {
    	int shmid;
    	key_t key = ftok("/home/gec",1000); 
    	//申请共享内存
    	shmid=shmget(key,1024,IPC_CREAT|IPC_EXCL|0777); //新建共享内存
    	if(shmid==-1)
    	{
    		//进一步分析错误原因
    		if(errno==EEXIST) //共享内存已经存在
    		{
    			shmid=shmget(key,1024,0777); //打开共享内存
    		}
    		else //其他错误
    		{
    			perror("申请失败!\n");
    			return -1;
    		}
    	}
    	
    	//定义结构体变量,用于存放获取到的属性信息
    	struct shmid_ds myds;
    	//获取你刚才申请成功的那个共享内存的属性信息
    	shmctl(shmid,IPC_STAT,&myds);
    	printf("大小是:%ld\n",myds.shm_segsz);
    	printf("键值是:%x\n",myds.shm_perm.__key);
    	printf("权限是:%o\n",myds.shm_perm.mode);
    	
    }
    
    展开全文
  • 在查阅了大量例程后,发现共享内存的建立一般有两种,一种是使用shmget()函数,另一种是使用shmopen()。大致的说一下两种建立方法的流程。 shmget() 首先先申请内存大小,之后在将id和地址位置进行映射,即: ...

    前言

    作为一个刚刚完成研究生阶段学习初入职场的小白菜,而且还是从机械电子工程专业转到软件工程师这样一个小的转行,在刚刚进行工作的3个月,犹如填鸭式的学习了很多之前写代码并不会用到的功能。在碰到的很多问题之后,就上百度搜索应该怎么解决,发现基本都是在CSDN上有很多现成的解决办法,并且也能给很多启发。因此,在实际上解决了问题之后,希望能够记录下一些问题的出现的原因,以及相应的我所解决问题的办法。开始慢慢写这类博客吧,一方面是记录一下成长的过程,另一方面也是希望能够给以后同样碰到这类问题的人有个启示吧。目前碰到的都是一些很基础的问题,大神勿喷。(话说这个博客编辑界面和shareLaTeX好像啊)

    多进程的建立

    多进程的建立在这就简单的说一下,就是使用fork函数。
    pid_t pid;
    pid=fork();
    if(pid<0){
    子进程创建失败
    }
    if(pid==0){
    子进程运行程序
    }else{
    父进程运行程序
    }
    就是一个很基本的fork()函数运用,并没有碰到啥问题,也就不展开说了。

    共享内存的建立

    在查阅了大量例程后,发现共享内存的建立一般有两种,一种是使用shmget()函数,另一种是使用shmopen()。大致的说一下两种建立方法的流程。

    1. shmget()
      首先先申请内存大小,之后在将id和地址位置进行映射,即:
      shmget()->shmat()
      在这里插入图片描述
      图片来自于百度百科。
      第一个参数key_t key我觉得就相当于一个标识,相当于给这块共享内存的唯一代号。我使用的就是一个简单的强制类型转换,例如(key_t)1234。当然在寻找例程以及解决之后要说的问题的过程中,发现在创建key的过程中有些需要注意的点,而且为了唯一性,同样可以在这个位置填写IPC_PRIVATE。
      第二个参数就是设置共享内存的大小。这里就是我之后要说的问题的关键点了。第一次申请的共享内存的大小决定了他的上限,并且只要你没有进行过删除这段共享内存的操作,这段共享内存会一直存在。要想申请这段共享内存成功需要确认几点:内存大小比能申请的最小值大且比最大值小,若以该键值申请过共享内存了,之后所申请的内存大小要比第一次开的内存大小要只小不大。
      第三个参数就模式标志了,这个在上图中已经解释的很清楚了。
      当完成了shmget之后,例如:
      int shmid;
      shmid=shmget((key_t)1234, 4096,0666|IPC_CREAT|IPC_EXCL);
      if(shmid==-1){
      //创建失败,该共享内存已存在
      shmid=shmget((key_t)1234,4096,0666|IPC_CREAT);
      if(shmid==-1){
      perror("share memory creat failed. ");
      exit(EXIT_FAILURE);
      }
      }
      perror会打印出上一个错误发生的原因。原因的描述如上图所示。

    在得到id之后,就需要将id和内存地址进行相互映射了,这个操作就是使用shmat()
    在这里插入图片描述
    上图来自百度百科
    可以指定地址,也可以让内核自己决定一个合适的地址位置,建立如下:
    void shm = NULL;
    shm=shmat(shmid, (void
    ) 0 ,0);
    if(shm==(void*)-1){
    perror(“shmat failed”);
    exit(EXIT_FAILURE);
    }
    这样就完成了共享内存的建立以及映射了。
    之后就可以通过memcpy,strcpy等等方法将需要写进共享空间的内容写进去。

    1. shmopen()
      在项目中,并没有用这个方法去建立共享内存,那也就给一个相关的例程:
      https://blog.csdn.net/maopig/article/details/16920907
      这个因为没有研究使用过,就不详细说了。

    问题产生

    我目前这个项目,需要一个进程连续不断的读取数据,数据的上传速度差不多是1000Hz,需要另一个进程对得到的数据进行处理。每一次的数据处理需要有一定的数据量,比如说需要有300个数据,才能进行一次的数据处理。本来想尝试单进程的,但是发现单进程的话数据实时性有很大的问题,因此最后决定使用多进程。因为之前是学习机电的,就算是要写软件写的是单片机的裸机程序,连线程的概念都很薄弱更别说多进程了,因此就直接硬着头皮上了。然后就发现在开共享内存空间的时候,这个内存大小怎么都开不大,每一次在size那个参数里写比较大的数,程序就进判断,然后exit with failure了。本来以为是因为系统设置的问题,上网查了一下怎么去看系统的允许的最大最小共享内存大小值。
    这里插一句,要插看共享内存能开的最大最小值的命令是:
    cat /proc/sys/kernel/shmmax
    cat /proc/sys/kernel/shmmni
    然后发现最小值是4096,最大值是18446744073692774399
    我需要开的内存大小也就是10k左右,比最大值小得多,在范围里啊,怎么就申请不了呢?然后就是不停地上网搜原因。最后根据shmget()函数的机理,终于明白是怎么回事了。

    问题解决

    会出现这个问题,是因为,我以一个键值已经申请过一块共享内存了,在第一次做这个操作的时候,开的内存很小大概就255,就这一下决定了这块内存空间的上限,之后再想要加大这块内存空间就是不可能的事情了,除非换一个键值。
    通过命令 ipcs -m去查看我内存管理,果然是有一块已申请的内存空间。那就只能把这个内存给删除了。
    可以直接通过命令行去删除
    ipcrm -m id号
    这样之后再申请共享内存的时候就不会报错了

    结论

    没有学习过相关的内容,这样摸着石头过河,真的是心累啊。不过好歹还有点资料以及前人的经验可以借鉴。

    展开全文
  • 共享内存

    千次阅读 2012-11-15 18:48:11
    简介  共享内存是一种非常重要且常用的进程间通信方式,相对于其它IPC机制,因其速度最快、效率最高,被广泛...System V IPC 为UNIX平台上的共享内存应用制定了统一的API标准,从而为在UNIX/Linux平台上进行跨

    原文:http://blog.csdn.net/thinksoftstudio/article/details/5515435

    简介

      共享内存是一种非常重要且常用的进程间通信方式,相对于其它IPC机制,因其速度最快、效率最高,被广泛应用于各类软件产品及应用开发中。System V IPC 为UNIX平台上的共享内存应用制定了统一的API标准,从而为在UNIX/Linux平台上进行跨平台开发提供了极大的便利;开发人员基于一套基本相同的源代码,便可开发出同时支持AIX、Solaris、HP-UX、Linux等平台的产品。

      然而,各个平台对System V 标准的API在实现上各有差异,由此对相关应用开发带来影响,甚至引入难以调试的问题。本文将结合作者在Tivoli产品开发中的实际经验,对这些平台相关的问题,以及具有共性的问题,逐一进行分析,并提出解决方法。

    1. System V共享内存概述

      System V 进程间通信(IPC)包括3种机制:消息队列、信号量、共享内存。消息队列和信号量均是内核空间的系统对象,经由它们的数据需要在内核和用户空间进行额外的数据拷贝;而共享内存和访问它的所有应用程序均同处于用户空间,应用进程可以通过地址映射的方式直接读写内存,从而获得非常高的通信效率。

    System V 为共享内存定义了下列API接口函数:

    # include <sys/types.h>
    # include <sys/ipc.h>
    # include <sys/shm.h>
    key_t  ftok(const char *pathname, int proj_id);
    int   shmget(key_t key, int size, int shmflg);
    void*  shmat(int shmid, const void *shmaddr, int shmflg);
    int   shmdt(void *shmaddr);
    int   shmctl(int shmid, int cmd, struct shmid_ds *buf);

     

    ftok

    函数用于生成一个键值:key_t key,该键值将作为共享内存对象的唯一性标识符,并提供给为shmget函数作为其输入参数;ftok 函数的输入参数包括一个文件(或目录)路径名:pathname,以及一个额外的数字:proj_id,其中pathname所指定的文件(或目录)要求必须已经存在,且proj_id不可为0

    shmget

    函数用于创建(或者获取)一个由key键值指定的共享内存对象,返回该对象的系统标识符:shmid

    shmat

    函数用于建立调用进程与由标识符shmid指定的共享内存对象之间的连接;

    shmdt

    函数用于断开调用进程与共享内存对象之间的连接;

    shmctl

    函数用于对已创建的共享内存对象进行查询、设值、删除等操作;

    2. ftok的陷阱

      根据pathname指定的文件(或目录)名称,以及proj_id参数指定的数字,ftok函数为IPC对象生成一个唯一性的键值。在实际应用中,很容易产生的一个理解是,在proj_id相同的情况下,只要文件(或目录)名称不变,就可以确保ftok返回始终一致的键值。然而,这个理解并非完全正确,有可能给应用开发埋下很隐晦的陷阱。因为ftok的实现存在这样的风险,即在访问同一共享内存的多个进程先后调用ftok函数的时间段中,如果pathname指定的文件(或目录)被删除且重新创建,则文件系统会赋予这个同名文件(或目录)新的i节点信息,于是这些进程所调用的ftok虽然都能正常返回,但得到的键值却并不能保证相同。由此可能造成的后果是,原本这些进程意图访问一个相同的共享内存对象,然而由于它们各自得到的键值不同,实际上进程指向的共享内存不再一致;如果这些共享内存都得到创建,则在整个应用运行的过程中表面上不会报出任何错误,然而通过一个共享内存对象进行数据传输的目的将无法实现。

      AIX、Solaris、HP-UX均明确指出,key文件被删除并重建后,不保证通过ftok得到的键值不变,比如AIX上ftok的man帮助信息即声明:

      Attention: If the Path parameter of the ftok subroutine names a file that has been removed while keys still refer to it, the ftok subroutine returns an error. If that file is then re-created, the ftok subroutine will probably return a key different from the original one.

      Linux没有提供类似的明确声明,但我们可以通过下面的简单例程test01.c,得到相同的印证:

    #include <stdio.h>
    #include <sys/ipc.h>
    void main(int argc, char* argv[])
    {
        if (argc !=2 ) {
        printf("Usage: %s KeyFilen e.g. %s /tmp/mykeyfilen", argv[0], argv[0]);
        return;
      }
      printf("Key generated by ftok: 0x%xn", ftok(argv[1], 1));
    }

      将上述例程在Red Hat Enterprise Linux AS release 4平台上编程成可执行程序test01,并且通过touch命令在 /tmp目录下创建一个新文件mykeyfile,然后为该文件生成键值:

    # touch /tmp/mykeyfile
    # ./test01 /tmp/mykeyfile
    Key generated by ftok: 0x101000b

      然后,将/tmp/mykeyfile删除,并且通过vi命令重新创建该文件,再次生成键值:

    # ./test01 /tmp/mykeyfile
    Key generated by ftok: 0x1010017

      我们可以看到,虽然文件名称都是 /tmp/mykeyfile,并未改变,但由于中间发生了文件删除并重新创建的操作,前后两次所得到的键值已经不再相同。

      避免此类问题最根本的方法,就是采取措施保证pathname所指定的文件(或目录)在共享内存的使用期间不被删除,不要使用有可能被删除的文件;或者干脆直接指定键值,而不借助ftok来获取键值。

    3. AIX中shmat的问题

      AIX系统中,System V各类进程间通信机制在使用中均存在限制。区别于其它UNIX操作系统对IPC机制的资源配置方式,AIX使用了不同的方法;在AIX中定义了 IPC 机制的上限, 且是不可配置的。就共享内存机制而言,在4.2.1及以上版本的AIX系统上,存在下列限制:

      对于64位进程,同一进程可连接最多268435456个共享内存段;

      对于32位进程,同一进程可连接最多11个共享内存段,除非使用扩展的shmat;

      上述限制对于64位应用不会带来麻烦,因为可供连接的数量已经足够大了;但对于32位应用,却很容易带来意外的问题,因为最大的连接数量只有11个。在某些事件触发的多线程应用中,新的线程不断地为进行事件处理而被创建,这些线程如果都需要去连接特定的共享内存,则极有可能造成该进程连接的共享内存数量超过11个,事实上同时拥有几十个甚至上百个处理线程的应用并不少见。一旦超个这个限制值,则所有后续的处理线程都将无法正常工作,从而导致应用运行失败。

      下面的例程test02.c演示了这个问题,为了精简代码,它反复连接的是同一个共享内存对象;实际上,无论所连接的共享内存对象是否相同,该限制制约的是连接次数:

    #include <stdio.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #define  MAX_ATTACH_NUM 15
    void main(int argc, char* argv[])
    {
      key_t    mem_key;
      long    mem_id;
      void*    mem_addr[MAX_ATTACH_NUM];
      int     i;
      if ( ( mem_key = ftok("/tmp/mykeyfile", 1) ) == (key_t)(-1) ) {
          printf("Failed to generate shared memory access key, ERRNO=%dn",
          errno);
          goto MOD_EXIT;
      }
      if ( ( mem_id = shmget(mem_key, 256, IPC_CREAT) ) == (-1) ) {
          printf("Failed to obtain shared memory ID, ERRNO=%dn", errno);
          goto MOD_EXIT;
      }
      for ( i=1; i<=MAX_ATTACH_NUM; i++ ) {
        if ( ( mem_addr[i] = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )
          printf("Failed to attach shared memory, times [%02d], errno:%dn", i,
          errno);
        else
          printf("Successfully attached shared memory, times [%02d]n", i);
      }
    MOD_EXIT:
      shmctl(mem_id, IPC_RMID, NULL);
    }

      在AIX系统上,我们将其编译为test02,并运行,可以看到如下输出:

    Successfully attached shared memory, times [01]
    Successfully attached shared memory, times [02]
    Successfully attached shared memory, times [03]
    Successfully attached shared memory, times [04]
    Successfully attached shared memory, times [05]
    Successfully attached shared memory, times [06]
    Successfully attached shared memory, times [07]
    Successfully attached shared memory, times [08]
    Successfully attached shared memory, times [09]
    Successfully attached shared memory, times [10]
    Successfully attached shared memory, times [11]
    Failed to attach shared memory, times [12], errno:24
    Failed to attach shared memory, times [13], errno:24
    Failed to attach shared memory, times [14], errno:24
    Failed to attach shared memory, times [15], errno:24

      说明超出11个连接之后,所有后续的共享内存连接都将无法建立。错误码24的定义是EMFILE,AIX给予的解释是:

      The number of shared memory segments attached to the calling process exceeds the system-imposed limit。

      解决这个问题的方法是,使用扩展的shmat;具体而言就是,在运行相关应用之前(确切地说,是在共享内存被创建之前),首先在shell中设置EXTSHM环境变量,通过它扩展shmat,对于源代码本身无需作任何修改:

    export EXTSHM=ON

      值得注意的是,虽然设置环境变量,在程序中也可通过setenv函数来做到,比如在程序的开始,加入下列代码:

    setenv("EXTSHM", "ON", 1);

      但实践证明这样的方法在解决这个问题上是无效的;也就是说唯一可行的办法,就是在shell中设置EXTSHM环境变量,而非在程序中。

      在AIX上配置32位DB2实例时,也要求确保将环境变量 EXTSHM 设为 ON,这是运行 Warehouse Manager 和 Query Patroller 之前必需的操作:

    export EXTSHM=ON
    db2set DB2ENVLIST=EXTSHM
    db2start

      其原因即来自我们刚刚介绍的AIX中32位应用连接共享内存时,存在最大连接数限制。这个问题同样普遍存在于AIX平台上Oracle等软件产品中。

    4. HP-UX中shmget和shmat的问题

    4.1 32位和64位应用兼容问题

      在HP-UX平台上,如果同时运行32位应用和64位应用,而且它们访问的是一个相同的共享内存区,则会遇到兼容性问题。

      在HP-UX中,应用程序设置IPC_CREAT标志调用shmget,所创建的共享内存区,只可被同类型的应用所访问;即32位应用程序所创建的共享内存区只可被其它的32位应用程序访问,同样地,64位应用程序所创建的共享内存区只可被其它的64位应用程序访问。

      如果,32位应用企图访问一个由64位应用创建的共享内存区,则会在调用shmget时失败,得到EINVAL错误码,其解释是:

    A shared memory identifier exists for key but is in 64-bit address space and the process performing the request has been compiled as a 32-bit executable.

      解决这一问题的方法是,当64位应用创建共享内存时,合并IPC_CREAT标志,同时给定IPC_SHARE32标志:

    shmget(mem_key, size, 0666 | IPC_CREAT | IPC_SHARE32)

      对于32位应用,没有设定IPC_SHARE32标志的要求,但设置该标志并不会带来任何问题,也就是说无论应用程序将被编译为32位还是64位模式,都可采用如上相同的代码;并且由此解决32位应用和64位应用在共享内存访问上的兼容性问题。

    4.2 对同一共享内存的连接数限制

      在HP-UX上,应用进程对同一个共享内存区的连接次数被限制为最多1次;区别于上面第3节所介绍的AIX上的连接数限制,HP-UX并未对指向不同共享内存区的连接数设置上限,也就是说,运行在HP-UX上的应用进程可以同时连接很多个不同的共享内存区,但对于同一个共享内存区,最多只允许连接1次;否则,shmat调用将失败,返回错误码EINVAL,在shmat的man帮助中,对该错误码有下列解释:

      shmid is not a valid shared memory identifier, (possibly because the shared memory segment was already removed using shmctl(2) with IPC_RMID), or the calling process is already attached to shmid.

      这个限制会对多线程应用带来无法避免的问题,只要一个应用进程中有超过1个以上的线程企图连接同一个共享内存区,则都将以失败而告终。

      解决这个问题,需要修改应用程序设计,使应用进程具备对同一共享内存的多线程访问能力。相对于前述问题的解决方法,解决这个问题的方法要复杂一些。

      作为可供参考的方法之一,以下介绍的逻辑可以很好地解决这个问题:

      基本思路是,对于每一个共享内存区,应用进程首次连接上之后,将其键值(ftok的返回值)、系统标识符(shmid,shmget调用的返回值)和访问地址(即shmat调用的返回值)保存下来,以这个进程的全局数组或者链表的形式留下记录。在任何对共享内存的连接操作之前,程序都将先行检索这个记录列表,根据键值和标志符去匹配希望访问的共享内存,如果找到匹配记录,则从记录中直接读取访问地址,而无需再次调用shmat函数,从而解决这一问题;如果没有找到匹配目标,则调用shmat建立连接,并且为新连接上来的共享内存添加一个新记录。

      记录条目的数据结构,可定义为如下形式:

    typedef struct _Shared_Memory_Record
    {
      key_t  mem_key; // key generated by ftok()
      int   mem_id;  // id returned by shmget()
      void*  mem_addr;// access address returned by shmat()
      int   nattach; // times of attachment
    } Shared_Memory_Record;

      其中,nattach成员的作用是,记录当前对该共享内存区的连接数目;每一次打开共享内存的操作都将对其进行递增,而每一次关闭共享内存的操作将其递减,直到nattach的数值降到0,则对该共享内存区调用shmdt进行真正的断开连接。

      打开共享内存的逻辑流程可参考如下图:

    图一

    图二

      Solaris系统中的shmdt调用,在原型上与System V标准有所不同,

    Default
    int shmdt(char *shmaddr);

      即形参shmaddr的数据类型在Solaris上是char *,而System V定义的是void * 类型;实际上Solaris上shmdt调用遵循的函数原型规范是SVID-v4之前的标准;以Linux系统为例,libc4和libc5 采用的是char * 类型的形参,而遵循SVID-v4及后续标准的glibc2及其更新版本,均改为采用void * 类型的形参。

      如果仍在代码中采用System V的标准原型,就会在Solaris上编译代码时造成编译错误;比如:

    Error: Formal argument 1 of type char* in call to shmdt(char*)
    is being passed void*.

      解决方法是,引入一个条件编译宏,在编译平台是Solaris时,采用char * 类型的形参,而对其它平台,均仍采用System V标准的void * 类型形参,比如:

    #ifdef _SOLARIS_SHARED_MEMORY
      shmdt((char *)mem_addr); 
    #else
      shmdt((void *)mem_addr);
    #endif

    5. 通过shmctl删除共享内存的风险

      当进程断开与共享内存区的连接后,一般通过如下代码删除该共享内存:

    shmctl(mem_id, IPC_RMID, NULL); 

      从HP-UX上shmctl函数的man帮助,我们可以看到对IPC_RMID操作的说明:

      IPC_RMID Remove the shared memory identifier specified by shmid from the system and destroy the shared memory segment and data structure associated with it. If the segment is attached to one or more processes, then the segment key is changed to IPC_PRIVATE and the segment is marked removed. The segment disappears when the last attached process detaches it.

      其它UNIX平台也有类似的说明。关于shmctl的IPC_RMID操作,其使用特点可简述如下:

      如果共享内存已经与所有访问它的进程断开了连接,则调用IPC_RMID子命令后,系统将立即删除共享内存的标识符,并删除该共享内存区,以及所有相关的数据结构;

      如果仍有别的进程与该共享内存保持连接,则调用IPC_RMID子命令后,该共享内存并不会被立即从系统中删除,而是被设置为IPC_PRIVATE状态,并被标记为"已被删除";直到已有连接全部断开,该共享内存才会最终从系统中消失。

      于是,存在这样的一种状态:

      N个进程(进程1至进程N)已经与某共享内存区连接;

      进程1已完成对此共享内存的操作,断开连接后,调用shmctl的IPC_RMID子命令,企图删除该共享内存;

      由于进程2至进程N仍保持与该共享内存的连接,因此在它们全部断开连接之前,这个共享内存区毫无疑问地会依然存在。

      此时,如果有其它的进程(比如第N+1号进程)想建立对这个共享内存的连接,是否能够成功呢?

      类似的状态,在Windows上同样存在,只是程序借助的API有所不同,比如通过CreateFileMapping函数创建共享内存,通过MapViewOfFile函数建立连接,通过UnmapViewOfFile函数断开连接,通过CloseHandle函数删除共享内存等。在Windows上,对此问题的回答是肯定的;也就是说,只要共享内存依然存在,则进程总是可以建立对它的连接,而无论之前是否有进程对其执行过删除操作。

      然而,对于包括AIX、Solaris、HP-UX等在内的UNIX平台,答案却是否定的!这也正是本节所讨论的使用shmctl中的风险所在;通过以下test03.P1.c和test03.P2.c两个例程,我们可以很直观地得到答案:

      test03.P1.c: 创建共享内存,并建立连接,保持10秒后(在此期间,test03.P2将反复连接、并删除该共享内存),断开连接,并最后再次尝试连接以验证该共享内存是否已被真正删除;

      test03.P2.c: 反复连接由test03.P1创建的共享内存,并在期间通过shmctl的IPC_RMID 子命令删除该共享内存,以观察共享内存被执行删除操作之后,在被彻底销毁之前是否还能接受连接;

    /******* test03.P1.c ********/
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    int main(int argc, char* argv[])
    {
      key_t      mem_key;
      long     mem_id;
      void*     mem_addr;
      int      isAttached = 0;
      mem_key = ftok("/tmp/mykeyfile", 1);
      mem_id = shmget(mem_key, 256, IPC_CREAT);
      if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) ) 
        printf("%s, Failed to attach shared memory, errno:%dn", argv[0], errno);
      else {
        isAttached = 1;
        printf("%s, +.Successfully attached shared memoryn", argv[0]);
        }
      /* sleep 10 seconds, to wait test03.P2 to run */
      sleep(10);
      if (isAttached) {
        // Attention: the following line should be "shmdt((char *)mem_addr);" if
        on Solaris
        shmdt((void *)mem_addr);
        printf("%s, -.Successfully detached shared memoryn", argv[0]);
      }
      /* try to attach the shared memory which has been removed! */
      if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )
        printf("%s, Failed to attach the removed shared memory, errno:%dn",
        argv[0], errno);
       return 0;
    }
    /******* test03.P2.c ********/
    #include <stdio.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    int main(int argc, char* argv[])
    {
      key_t  mem_key;
       long  mem_id;
      void* mem_addr;
      int  i, isAttached;
      mem_key = ftok("/tmp/mykeyfile", 1);
      mem_id = shmget(mem_key, 0, 0);
      // repeated attaching & detaching
      for (i=1; i<10; i++) {
        isAttached = 0;
        if ( ( mem_addr = (void *)shmat(mem_id, 0, 0) ) == (void *)(-1) )
          printf("%s, Failed to attach shared memory, times [%02d],
          errno:%dn",argv[0], i, errno);
          else {
            isAttached = 1;
           printf("%s, +.Successfully attached shared memory, times
          [%02d]n",argv[0], i);
        }
          if (isAttached) {
           // Attention: the following line should be "shmdt((char
          *)mem_addr);", if on Solaris
          shmdt((void *)mem_addr);
            printf("%s, -.Successfully detached, times [%02d]n", argv[0], i);
          }
        // purposely remove the shared memory at times [5]
        if (i==5) {
          shmctl(mem_id, IPC_RMID, NULL);
          printf("%s, *.Remove executed, times [%02d], errno=%dn",
          argv[0], i, errno);
        }
      }
      return 0;
    }

      上述程序均可在AIX、HP-UX、Linux平台上编译通过;在Solaris平台上只需按注释提示的要求,将shmdt的参数强制为char *类型也可编译通过(第5节中已介绍过)。

      将test03.P1.c、test03.P2.c各自编译为可执行程序test03.P1、test03.P2,并通过下面的shell脚本:runtest,运行它们:

    #!/bin/sh
      ./test03.P1&
      sleep 2
      ./test03.P2

      在Linux平台(Red Hat 8.0)上的运行结果如下:

    [root@localhost tmp]# ./runtest
    ./test03.P1, +.Successfully attached shared memory
    ./test03.P2, +.Successfully attached shared memory, times [01]
    ./test03.P2, -.Successfully detached, times [01]
    ./test03.P2, +.Successfully attached shared memory, times [02]
    ./test03.P2, -.Successfully detached, times [02]
    ./test03.P2, +.Successfully attached shared memory, times [03]
    ./test03.P2, -.Successfully detached, times [03]
    ./test03.P2, +.Successfully attached shared memory, times [04]
    ./test03.P2, -.Successfully detached, times [04]
    ./test03.P2, +.Successfully attached shared memory, times [05]
    ./test03.P2, -.Successfully detached, times [05]
    ./test03.P2, *.Remove executed, times [05], errno=0
    ./test03.P2, +.Successfully attached shared memory, times [06]
    ./test03.P2, -.Successfully detached, times [06]
    ./test03.P2, +.Successfully attached shared memory, times [07]
    ./test03.P2, -.Successfully detached, times [07]
    ./test03.P2, +.Successfully attached shared memory, times [08]
    ./test03.P2, -.Successfully detached, times [08]
    ./test03.P2, +.Successfully attached shared memory, times [09]
    ./test03.P2, -.Successfully detached, times [09]
    [root@localhost tmp]# ./test03.P1, -.Successfully detached shared memory
    ./test03.P1, Failed to attach the removed shared memory, errno:22

      根据运行结果,我们可以看到,在Linux平台上,即便对共享内存执行了删除操作(在第5次连接之后,test03.P2进程调用了shmctl的IPC_RMID删除操作),只要该共享内存依然存在(test03.P1进程保持着连接,因此共享内存不会被立即删除),则它仍然是可连接的(test03.P2进程的第6到第9次连接均是成功的)。

      然而,在AIX、HP-UX、Solaris平台上的运行结果却不同于Linux:

    # ./runtest
    ./test03.P1, +.Successfully attached shared memory
    ./test03.P2, +.Successfully attached shared memory, times [01]
    ./test03.P2, -.Successfully detached, times [01]
    ./test03.P2, +.Successfully attached shared memory, times [02]
    ./test03.P2, -.Successfully detached, times [02]
    ./test03.P2, +.Successfully attached shared memory, times [03]
    ./test03.P2, -.Successfully detached, times [03]
    ./test03.P2, +.Successfully attached shared memory, times [04]
    ./test03.P2, -.Successfully detached, times [04]
    ./test03.P2, +.Successfully attached shared memory, times [05]
    ./test03.P2, -.Successfully detached, times [05]
    ./test03.P2, *.Remove executed, times [05], errno=0
    ./test03.P2, Failed to attach shared memory, times [06], errno:22
    ./test03.P2, Failed to attach shared memory, times [07], errno:22
    ./test03.P2, Failed to attach shared memory, times [08], errno:22
    ./test03.P2, Failed to attach shared memory, times [09], errno:22
    # ./test03.P1, -.Successfully detached shared memory
    ./test03.P1, Failed to attach the removed shared memory, errno:22

      根据结果,可以发现,test03.P2进程的第6到第9次连接都是失败的,也就说明,在AIX、HP-UX、Solaris平台上一旦通过shmctl对共享内存进行了删除操作,则该共享内存将不能再接受任何新的连接,即使它依然存在于系统中!

      而且,上面的运行结果,也证明了,对共享内存进行了删除操作之后,当已有的连接全部断开,该共享内存将被系统自动销毁(运行结果的最后一行,说明该共享内存已经不存在了)。

      本节的目的在于说明,在AIX、HP-UX、Solaris平台上调用shmctl的IPC_RMID删除操作,是存在潜在风险的,需要足够的谨慎。

      如果,可以确知,在删除之后不可能再有新的连接,则执行删除操作是安全的;

      否则,在删除操作之后如仍有新的连接发生,则这些连接都将失败!

    6. 结论

      对共享内存的操作,往往是产品或者应用中数据传输的基础,对其可靠性和性能至关重要;而且作为底层的IPC机制,相关代码具有不易调试的特点,由其造成的问题往往关键却不容易解决。

      本文从应用实现的角度上,对在UNIX/Linux平台上使用共享内存可能会遇到的问题,进行了全面的介绍和分析,并给出了解决方法或建议,可供相关的应用开发人员参考。


    展开全文
  • Actor 并发模型 & "不要通过共享内存来通信,而应该通过通信来共享内存" Actors编程模型  Actors模型首先是由Carl Hewitt在1973定义, 由Erlang OTP (Open Telecom Platform) 推广,其 消息...

    Actor 并发模型 & "不要通过共享内存来通信,而应该通过通信来共享内存"

    Actors编程模型

      Actors模型首先是由Carl Hewitt在1973定义, 由Erlang OTP (Open Telecom Platform) 推广,其

    消息传递更加符合面向对象的原始意图。

    • 隔离计算实体
    • "Share nothing"
    • 没有任何地方同步
    • 异步消息传递
    • 不可变的消息 (消息模型类似mailbox / 消息队列)

       传统多数流行的语言并发是基于多线程之间的共享内存,使用同步方法防止写争夺,Actors使用消息模型,每个Actor在同一时间处理最多一个消息,可以发送消息给其他Actors,保证了单独写原则 。从而巧妙避免了多线程写争夺。

    PS:此外,可以去 http://s3.amazonaws.com/four.livejournal/20091117/jsconf.pdf 看下 Node.js 是为了解决什么问题而诞生的,然后,再看下许式伟的《Go语言编程》一书中第4章(并发编程)以更好地理解"不要通过共享内存来通信,而应该通过通信来共享内存"。


    问题:

    看了坛里几篇actor的几篇文章,可是不能很好的理解,需要大家共同指导讨论下

    1.actor并发模型的应用场景?
    2.actor的原理?思维方式改变?
    3.actor最简单的demo?

    解答:

    1. actor并发模型的应用场景?
    适合有状态或者称可变状态的业务场景,如果用DDD术语,适合聚合根,具体案例如订单,订单有状态,比如未付款未发货,已经付款未发货,已付款已发货,导致订单状态的变化是事件行为,比如付款行为导致订单状态切换到"已经付款未发货"。
    如果知晓GOF设计模式的状态模式,就更好理解有态概念。

    2. actor的原理?思维方式改变?
    行为导致状态变化,行为执行是依靠线程,比如用户发出一个付款的请求,服务器后端派出一个线程来执行付款请求,携带付款的金额和银行卡等等信息,当付款请求被成功完成后,线程还要做的事情就是改变订单状态,这时线程访问订单的一个方法比如changeState。
    如果后台有管理员同时修改这个订单状态,那么实际有两个线程共同访问同一个数据,这时就必须锁,比如我们在changeState方法前加上sychronized这样同步语法。
    使用同步语法坏处是每次只能一个线程进行处理,如同上厕所,只有一个蹲坑,人多就必须排队,这种情况性能很低。
    如何避免锁?
    避免changeState方法被外部两个线程同时占用访问,那么我们自己设计专门的线程守护订单状态,而不是普通方法代码,普通方法代码比较弱势,容易被外部线程hold住,而我们设计的这个对象没有普通方法,只有线程,这样就变成Order的守护线程和外部访问请求线程的通讯问题了。
    Actor采取的这种类似消息机制的方式,实际在守护线程和外部线程之间有一个队列,俗称信箱,外部线程只要把请求放入,守护线程就读取进行处理。
    这种异步高效方式是Actor基本原理,以ERlang和Scala语言为主要特征,他们封装得更好,类似将消息队列微观化了。
    我个人认为要使用好Actor,还是要树立自己对有态和无态的敏感性,这是几年前我宣传EJB的有态和无态Bean强调的一点,如果没有这个敏感性,按照DDD第一找出聚合根的分析方法也能抓住重点。
    当然这些思维的前提是抛弃数据库中心思维,不要老是想着把状态存在数据库中,然后用SQL不断修改,这是很低效的,也是有锁,比Java等语言的同步锁性能更差
    以我个人来说经历的步骤如下:
    1.用数据表一个字段来表示状态,比如1表示已付款未发货,2表示已付款已发货,然后用户来一个请求用SQL修改。
    2.用ORM实现,比如Hibernate JPA来修改状态,虽然不用SQL了,但是Hibernate的悲观锁和乐观锁也让人抓狂。
    3.彻底抛弃数据库,直接在内存缓存中进行修改,使用Java的同步锁,性能还是不够,吞吐量上不去。
    4.Actor模型。

    问题:

    如果这两个线程在一个jvm里还行,可是遇到分布式的情况咋办啊。。?尤其是spring提倡的那种分布式

    解答:

    这是可伸缩的,Actor对象(聚合根)本身有类似IO进出,就像信箱有收件箱和发件箱一样,Actor可以通过发件箱向一个MQ等消息总线发送消息就可实现分布式。
    发件箱的线程队列实现可借助OneToOneConcurrentArrayQueue,只有一个订阅者和发布者,发布者是Actor自己,订阅者是一个Socket端口或MQ消息总线的发布者。http://www.jdon.com/45504
    分布式网络是否可靠?http://www.jdon.com/45519
    区分不变与可变,也就是区分无态和有态,也就是区分实体(可变)与值对象(不变),是统领一切的编程法门(无论是多线程还是分布式)。


    扩展阅读:

    Actor的原理:
    先从著名的c10k问题谈起。有一个叫Dan Kegel的人在网上(http://www.kegel.com/c10k.html)提出:现在的硬件应该能够让一台机器支持10000个并发的client。然后他讨论了用不同的方式实现大规模并发服务的技术,归纳起来就是两种方式:一个client一个thread,用blocking I/O;多个clients一个thread,用nonblocking I/O或者asynchronous I/O。目前asynchronous I/O的支持在Linux上还不是很好,所以一般都是用nonblocking I/O。大多数的实现都是用epoll()的edge triggering(传统的select()有很大的性能问题)。这就引出了thread和event之争,因为前者就是完全用线程来处理并发,后者是用事件驱动(注意:这里的事件驱动不是领域驱动开发中的事件驱动,只是术语名称相同而已)来处理并发。当然实际的系统当中往往是混合系统:用事件驱动来处理网络时间,而用线程来处理事务。由于目前操作系统(尤其是Linux)和程序语言的限制(Java/C/C++等),线程无法实现大规模的并发事务。一般的机器,要保证性能的话,线程数量基本要限制几百(Linux上的线程有个特点,就是达到一定数量以后,会导致系统性能指数下降,参看SEDA的论文)。所以现在很多高性能web server都是使用事件驱动机制,比如nginx,Tornado,node.js等等。事件驱动几乎成了高并发的同义词,一时间红的不得了。
    其实线程和事件,或者说同步和异步之争早就在学术领域争了几十年了。1978年有人为了平息争论,写了论文证明了用线性的process(线程的模式)和消息传递(事件的模式)是等价的,而且如果实现合适,两者应该有同等性能。当然这是理论上的。针对事件驱动的流行,2003年加大伯克利发表了一篇论文叫“Why events are a bad idea (for high-concurrency servers)”,指出其实事件驱动并没有在功能上有比线程有什么优越之处,但编程要麻烦很多,而且特别容易出错。线程的问题,无非是目前的实现的原因。一个是线程占的资源太大,一创建就分配几个MB的stack,一般的机器能支持的线程大受限制。针对这点,可以用自动扩展的stack,创建的先少分点,然后动态增加。第二个是线程的切换负担太大,Linux中实际上process和thread是一回事,区别就在于是否共享地址空间。解决这个问题的办法是用轻量级的线程实现,通过合作式的办法来实现共享系统的线程。这样一个是切换的花费很少,另外一个可以维护比较小的stack。他们用coroutine和nonblocking I/O(用的是poll()+thread pool)实现了一个原型系统,证明了性能并不比事件驱动差。
    那是不是说明线程只要实现的好就行了呢。也不完全对。2006年还是加大伯克利,发表了一篇论文叫“The problem with threads”。线程也不行。原因是这样的。目前的程序的模型基本上是基于顺序执行。顺序执行是确定性的,容易保证正确性。而人的思维方式也往往是单线程的。线程的模式是强行在单线程,顺序执行的基础上加入了并发和不确定性。这样程序的正确性就很难保证。线程之间的同步是通过共享内存来实现的,你很难来对并发线程和共享内存来建立数学模型,其中有很大的不确定性,而不确定性是编程的巨大敌人。作者以他们的一个项目中的经验来说明,保证多线程的程序的正确性,几乎是不可能的事情。首先,很多很简单的模式,在多线程的情况下,要保证正确性,需要注意很多非常微妙的细节,否则就会导致deadlock或者race condition。其次,由于人的思维的限制,即使你采取各种消除不确定的办法,比如monitor,transactional memory,还有promise/future,等等机制,还是很难保证面面俱到。以作者的项目为例,他们有计算机科学的专家,有最聪明的研究生,采用了整套软件工程的流程:design review, code review, regression tests, automated code coverage metrics,认为已经消除了大多数问题,不过还是在系统运行4年以后,出现了一个deadlock。作者说,很多多线程的程序实际上存在并发错误,只不过由于硬件的并行度不够,往往不显示出来。随着硬件的并行度越来越高,很多原来运行完好的程序,很可能会发生问题。我自己的体会也是,程序NullPointerException,core dump都不怕,最怕的就是race condition和deadlock,因为这些都是不确定的(non-deterministic),往往很难重现。
    那既然线程+共享内存不行,什么样的模型可以帮我们解决并发计算的问题呢。研究领域已经发展了一些模型,目前越来越多地开始被新的程序语言采用。最主要的一个就是Actor模型。它的主要思想就是用一些并发的实体,称为actor,他们之间通过发送消息来同步。所谓“Don’t communicate by sharing memory, share memory by communicating”。Actor模型和线程的共享内存机制是等价的。实际上,Actor模型一般通过底层的thread/lock/buffer 等机制来实现,是高层的机制。Actor模型是数学上的模型,有理论的支持。另一个类似的数学模型是CSP(communicating sequential process)。早期的实现这些理论的语言最著名的就是erlang和occam。尤其是erlang,所谓的Ericsson Language,目的就是实现大规模的并发程序,用于电信系统。Erlang后来成为比较流行的语言。
    类似Actor/CSP的消息传递机制。Go语言中也提供了这样的功能。Go的并发实体叫做goroutine,类似coroutine,但不需要自己调度。Runtime自己就会把goroutine调度到系统的线程上去运行,多个goroutine共享一个线程。如果有一个要阻塞,系统就会自动把其他的goroutine调度到其他的线程上去。
    一些名词定义:
    1. Processes, threads, green threads, protothreads, fibers, coroutines: what's the difference?
    • Process: OS-managed (possibly) truly concurrent, at least in the presence of suitable hardware support. Exist within their own address space.
    • Thread: OS-managed, within the same address space as the parent and all its other threads. Possibly truly concurrent, and multi-tasking is pre-emptive.
    • Green Thread: These are user-space projections of the same concept as threads, but are not OS-managed. Probably not truly concurrent, except in the sense that there may be multiple worker threads or processes giving them CPU time concurrently, so probably best to consider this as interleaved or multiplexed.
    • Protothreads: I couldn't really tease a definition out of these. I think they are interleaved and program-managed, but don't take my word for it. My sense was that they are essentially an application-specific implementation of the same kind of "green threads" model, with appropriate modification for the application domain.
    • Fibers: OS-managed. Exactly threads, except co-operatively multitasking, and hence not truly concurrent.
    • Coroutines: Exactly fibers, except not OS-managed.
    Coroutines are computer program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing more familiar program components such as cooperative tasks, iterators, infinite lists and pipes.
    • Continuation: An abstract representation of the control state of a computer program.
    A continuation reifies the program control state, i.e. the continuationis a data structure that represents the computational process at a given point in the process' execution; the created data structure can be accessed by the programming language, instead of being hidden in the runtime environment. Continuations are useful for encoding other control mechanisms in programming languages such as exceptions, generators, coroutines, and so on.
    The "current continuation" or "continuation of the computation step" is the continuation that, from the perspective of running code, would be derived from the current point in a program's execution. The term continuations can also be used to refer to first-class continuations, which are constructs that give a programming language the ability to save the execution state at any pointand return to that point at a later point in the program.(yield keywork in some languages, such as c# or python)
    •Goroutines: They claim to be unlike anything else, but they seem to be exactly green threads, as in, process-managed in a single address space and multiplexed onto system threads. Perhaps somebody with more knowledge of Go can cut through the marketing material.


    Actor模型的提出和实现主要是为了解决限制服务端应用高并发的难题:过度依靠操作系统内核所提供的API来实现高并发。这种内核API在高并发下会产生大量线程调度,过多用户态与内核的Context切换会使系统性能线性下降。

    Actor模型的实现关键有两点: 
    1. 基于运行时环境自己实现并发实体调度(例如Coroutine),从而避免大量的内核API调用,那么Context切换也可以自然避免。
    2. 避免使用共享内存来实现信息共享(上文有描述)。


    作者:banq,sinaID49811 

    出处:http://www.jdon.com/45516

    展开全文
  • c++ 创建共享内存

    千次阅读 2011-12-29 14:28:12
    建立共享内存区: HANDLE hmap; usb_info p_lizhi; hmap = ::CreateFileMapping((HANDLE)-1,NULL,PAGE_READWRITE,0,sizeof(struct USB_info),_T("share")); if(hmap != NULL && GetLastError() == ERROR_...
  • Linux共享内存常见问题分析

    千次阅读 2014-02-22 22:19:15
    前言 这个是接上篇,本来是记录在一...System V 共享内存问题 shmget创建共享内存问题 shmget函数用来创建一个新的,或者访问一个已存在的共享内存区。 #include #include int shmget(key_t key, size_t size, int
  • 一、发送端创建共享区域,并写入内容: //1 先删除之前创建的 ...//2 创建共享内存段 shared_memory_object shm(create_only, "MySharedMemory_Name", read_write); //3 设置共享内存大小 shm.truncate...
  • 使用docker的过程中,发现docker中有很多关于内存的命令,例如: -m="10g",docker的运行内存,按照官方文档描述,应该是对应于实际机器的物理内存,因此在设置的时候,肯定是要比本机物理内存小的;单位可以是b/k...
  • Java文件映射共享内存

    千次阅读 2015-03-26 00:30:15
    通常这些是要被频繁读写的,所以用C/C++的话通常的做法是使用共享内存划分出一大块内存,然后把它又分成同样大小的小块,用其中的一块或几块来保存一个UID和其所对应的数据,并配合一些索引和分配、回收块的算法等等...
  • 共享内存的系统调用

    2014-06-30 21:55:30
    当进程不再需要共享内存时,可以使用函数shmdt释放共享内存映射,其原型如下: #include  int shmdt(const void *shmaddr); 函数shmdt释放进程在地址shmaddr处映射的共享内存,参数shmaddr必须为函数...
  • 共享内存学习小结(二)

    千次阅读 2010-06-18 11:50:00
    这是一篇来自IBM工程师对共享内存的应用心得,其工作的OS是基于IBM自己的AIX。简介共享内存是一种非常重要且常用的进程间通信方式,相对于其它IPC机制,因其速度最快、效率最高,被广泛应用于各类软件产品及应用开发...
  • Kubernetes中Pod间共享内存方案

    千次阅读 2019-07-14 08:14:03
    在高并发业务下,尤其还是以C/C++代码实现的基础组件,经常会使用共享内存通信机制来追求高性能,本文给出了Kubernetes Pod间Posix/SystemV共享内存方式的折中方案,以牺牲一定的安全性为代价,请知悉。当然,如果...
  • 共享内存,管道,socket)分布式:socket是进程间通信唯一选择 进程间通信的方式有很多,常见的有信号,信号量,消息队列,管道,共享内存,和socket等,这里我们主要讨论管道,共享内存,和socket,其他的比较...
  • 多进程间通信常用的技术手段包括共享内存、消息队列、信号量等等,Linux系统下自带的ipcs命令是一个极好的工具,可以帮助我们查看当前系统下以上三项的使用情况,从而利于定位多进程通信中出现的通信问题。...
  • 因为第二个参数是零,(参见shmat函数说明),所以每次at的时候系统都会连向一个未被使用的地址,因为我们每次at之后都没有shmdt,所以系统会认为这块内存是被占用,所以当又一次shmat的时候系统会重新分配一块内存...
  • 本文中对共享内存的实现采用了系统V的机制,我们的重点在于通过信号量来完成对不同进程间共享内存资源的一致性访问,共享内存的具体方法请参见相关资料,这里不再赘述。 首先我们先实现最简单的共享内存,一个...
  • <br />简介 共享内存是一种非常重要且常用的进程间通信方式,相对于其它IPC机制,因其速度最快、效率最高,被广泛应用于各类软件产品及应用开发中。System V IPC 为UNIX平台上的共享内存应用制定了统一的...
  • winserver物理内存使用不到一半就频繁爆内存

    千次阅读 多人点赞 2020-08-21 01:59:08
    使用的物理内存内存使用的区别?提交、专用、工作集的区别?已提交又是什么东西? 如何配好winserver内存线上一直存在一个问题,内存无法最大利用化,经常出现服务崩溃问题。 winserver上有16g的物理内存使用不...
  • 内存共享(False Sharing)

    万次阅读 2015-04-11 10:24:29
    博主注:在考虑优化多线程并发的内存使用场景时, 由于CPU缓存机制不尽相同, 建议至少确保有128字节距离, 一般通过设置不使用哑元(dummy)或者跨区分配来避免命中同一缓存行, 以减少不同处理器由于缓存行相同造成的缓存...
  • 线程之间的同步是通过共享内存来实现的,你很难来对并发线程和共享内存来建立数学模型,其中有很大的不确定性,而不确定性是编程的巨大敌人。作者以他们的一个项目中的经验来说明,保证多线程的程序的正确性,几乎是...
  • Java虚拟机内存调优经验

    千次阅读 2018-02-27 09:09:39
    转载自 http://www.cnblogs.com/jackyrong/archive/2010/01/21/1653163.html 尊重别人的劳动成果就是尊重自己...对象的堆内存由称为垃圾回收器 的自动内存管理系统回收。 堆由两部分组成: 其中eden+fromspace+t...
  • 进程之间的地址空间是独享的,而线程是共享进程的地址空间,线程的资源比进程小,创建线程比创建进程快,线程间切换快,线程间通信快,线程资源利用率好. 下面做个补充: 1,线程挂则可能导致进程挂,稳定性差。对长...
  • LWIP使用经验

    万次阅读 多人点赞 2014-05-17 11:46:38
    LWIP使用经验 介绍了LWIP的工作原理,移植到RTOS,重点讲解了TCP知识,总结了使用LWIP的经验,最后给出常见问题与解答。
  • 在计算机科学中,虚假共享是一种性能降低的使用模式,它可能出现在具有由高速缓存机制管理的最小资源块大小的分布式一致高速缓存的系统中。当系统参与者将定期尝试访问,将永远不会被另一方改变数据,但这些数据共享...
  • Redis内存使用优化与存储

    万次阅读 2016-04-12 14:24:26
    redis内部使用一个redisObject对象来表示所有的key和value。redisObject最主要的信息如下图所示:  type代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis内部的存储方式,比如:type=string...
  • 排查springboot内存占用过高问题 所需命令: ps命令:Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。 top命令:Linux命令。可以查看实时的CPU使用情况...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 101,648
精华内容 40,659
关键字:

共享内存使用经验