精华内容
下载资源
问答
  • 共享内存

    2017-11-28 00:56:25
    一、共享内存的定义共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将...

    一、共享内存的定义

    共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

     

    二、共享内存的函数

    与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单,它们声明在头文件sys/shm.h中。

     

    1、shmget函数(创建共享内存)

    原型:

    int shmget(key_t key, size_t size, int shmflg);  

     

    (1)key(非 0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

    (2)size以字节为单位指定需要共享的内存容量。

    (3)shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

     

    2、shmat函数(把共享内存映射到一个地址)

    第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:

    void *shmat(int shm_id, const void *shm_addr, int shmflg);  

    (1)shm_id是由shmget函数返回的共享内存标识。

    (2)shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。

    (3)shm_flg是一组标志位,通常为0。

    调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

     

    3、shmdt函数将共享内存从当前进程中分离)

    注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。

    原型:

    int shmdt(const void *shmaddr); 

    参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

     

    4、shmctl函数用来控制共享内存)

    原型:

    int shmctl(int shm_id, int command, struct shmid_ds *buf);  

     

    (1)shm_id是shmget函数返回的共享内存标识符。

    (2)command是要采取的操作,它可以取下面的三个值 :

       IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

       IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

       IPC_RMID:删除共享内存段

    (3)buf是一个结构指针,它指向共享内存模式和访问权限的结构。

    shmid_ds结构至少包括以下成员:

    struct shmid_ds  

    {  

        uid_t shm_perm.uid;  

        uid_t shm_perm.gid;  

        mode_t shm_perm.mode;  

    };  


    eg:

    sem.h

    1.	#include <stdio.h>  
    2.	#include <stdlib.h>  
    3.	#include <unistd.h>  
    4.	#include <sys/sem.h>  
    5.	  
    6.	union semun  
    7.	{  
    8.	    int val;  
    9.	};  
    10.	  
    11.	void sem_init(int,int);//因为要用到两个信号量,所以初始化时直接初始两个  
    12.	void sem_p(int);  
    13.	void sem_v(int);  
    14.	void sem_destroy();  

    sem.c

    1.	#include "sem.h"  
    2.	  
    3.	static int semid = 0;  
    4.	  
    5.	void init(int in1,int in2)  
    6.	{  
    7.	    if((semid  =  semget((key_t)1234,2,IPC_CREAT|IPC_EXCL|0600)) == -1)  
    8.	    {  
    9.	        if((semid = semget((key_t)1234,2,IPC_EXCL)) == -1)  
    10.	        {  
    11.	            perror("semget error;\n");  
    12.	        }  
    13.	    }  
    14.	    else  
    15.	    {  
    16.	        union semun buf;  
    17.	        buf.val = in1;  
    18.	        if(semctl(semid,0,SETVAL,buf) == -1)  
    19.	        {  
    20.	            perror("semctl error");  
    21.	        }  
    22.	  
    23.	        union semun buf1;  
    24.	        buf1.val = in2;  
    25.	        if(semctl(semid,1,SETVAL,buf1) == -1)  
    26.	        {  
    27.	            perror("semctl error");  
    28.	        }  
    29.	    }  
    30.	}  
    31.	void sem_p(int num)  
    32.	{  
    33.	    struct sembuf buff;  
    34.	    buff.sem_num = num;  
    35.	    buff.sem_op = -1;  
    36.	    buff.sem_flg = SEM_UNDO;  
    37.	    if(semop(semid,&buff,1) == -1)  
    38.	    {  
    39.	        perror("semop error");  
    40.	    }  
    41.	}  
    42.	void sem_v(int num)  
    43.	{  
    44.	  
    45.	    struct sembuf buff;  
    46.	    buff.sem_num = num;  
    47.	    buff.sem_op = 1;  
    48.	    buff.sem_flg = SEM_UNDO;  
    49.	    if(semop(semid,&buff,1) == -1)  
    50.	    {  
    51.	        perror("semop error");  
    52.	    }  
    53.	}  
    54.	  
    55.	void destroy()  
    56.	{  
    57.	    if(semctl(semid,0,IPC_RMID) == -1)  
    58.	    {  
    59.	        perror("destory error");  
    60.	    }  
    61.	  
    62.	}  

    write.c

    1.	#include <stdio.h>  
    2.	#include <stdlib.h>  
    3.	#include <string.h>  
    4.	#include <unistd.h>  
    5.	#include <assert.h>  
    6.	#include <fcntl.h>  
    7.	#include <sys/sem.h>  
    8.	#include <sys/shm.h>  
    9.	#include "sem.h"  
    10.	  
    11.	int main()  
    12.	{  
    13.	    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);  
    14.	    if(shmid == -1)  
    15.	    {  
    16.	        perror("shmget error:");  
    17.	    }  
    18.	    void * id = (void *)0;  
    19.	    id = shmat(shmid,NULL,0);  
    20.	    if(id  == (void  *)-1)  
    21.	    {  
    22.	        perror("shmat error:");  
    23.	    }  
    24.	  
    25.	    char * s = (char * )id;  
    26.	  
    27.	    init(1,0);  
    28.	    int i = 0;  
    29.	    while(1)  
    30.	    {  
    31.	        sem_p(0);  
    32.	        fgets(s,128,stdin);  
    33.	  
    34.	        sem_v(1);  
    35.	        if(strncmp(s,"end",3) == 0)  
    36.	        {  
    37.	            break;  
    38.	        }  
    39.	    }  
    40.	  
    41.	    if(shmdt(s) == -1)  
    42.	    {  
    43.	        perror("shmdt error:");  
    44.	    }  
    45.	    sleep(10);  
    46.	    shmctl(shmid,IPC_RMID,0);  
    47.	    destroy();  
    48.	}  

    read.c

    1.	#include <stdio.h>  
    2.	#include <stdlib.h>  
    3.	#include <string.h>  
    4.	#include <unistd.h>  
    5.	#include <assert.h>  
    6.	#include <sys/sem.h>  
    7.	#include <sys/shm.h>  
    8.	#include "sem.h"  
    9.	  
    10.	int main()  
    11.	{  
    12.	    int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);  
    13.	    if(shmid == -1)  
    14.	    {  
    15.	        perror("shmget error:");  
    16.	    }  
    17.	    char *s = (char *)shmat(shmid,NULL,0);  
    18.	    if(s == (char *)-1)  
    19.	    {  
    20.	        perror("shmat error:");  
    21.	    }  
    22.	    init(1,0);  
    23.	       //init(0);  
    24.	        int i = 0;  
    25.	    while(1)  
    26.	    {  
    27.	        sem_p(1);  
    28.	        if(strncmp(s,"end",3) == 0)  
    29.	        {  
    30.	            break;  
    31.	        }  
    32.	        printf("%s",s);  
    33.	          
    34.	        sem_v(0);  
    35.	    }  
    36.	    if(shmdt(s) == -1)  
    37.	    {  
    38.	        perror("shmdt error:");  
    39.	    }  
    40.	}  

    展开全文
  • 今天我们来谈一谈Linux进程间通信的方式之一共享内存我们先来看看共享内存的定义:共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效...

    今天我们来谈一谈Linux进程间通信的方式之一共享内存

    我们先来看看共享内存的定义:

    共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

    怎么使用共享内存,有以下几点步骤:

    1.创建共享内存 shmget

    2.映射到物理内存 shmat

    3.数据读写

    4.释放共享内存shmdt

    5.销毁共享内存shmctl

    介绍一下api:

    1.shmget

    原型:

    int shmget(key_t key, size_t size, int shmflg);

    示例:

    shmget(key,1024*4,IPC_CREAT|0666);

    2.shmat

    原型:

    void *shmat(int shmid, const void *shmaddr, int shmflg);

    示例:

    shmat(shmid,0,0);

    最简单的方法——让系统自动安排共享内存

    3.shmdt

    原型

    int shmdt(const void *shmaddr)

    示例:

    shmdt(shmaddr);

    其中shmaddr=shmat(shmid,0,0);连接的共享内存的起始地址

    4.shmctl

    原型:

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);

    示例:

    shmctl(shmid,IPC_RMID,0);

    接下来我们看看实际应用场景:

    输入端开辟共享内存并且映射,进行写入,读取端映射相同id号的共享内存,直接读取(共享内存就像两个人可以在同一张纸上写字一样,其中一个人写了,你直接可以读到)

    编程示例:

    输入端:

    #include

    #include

    #include

    #include

    #include

    int main()

    {

    int shmid;

    char *shmaddr;

    key_t key;

    key=ftok(".",1);

    shmid= shmget(key,1024*4,IPC_CREAT|0666);

    if(shmid ==-1){

    printf("shmeget error!\n");

    exit(-1);

    }

    shmaddr =shmat(shmid,0,0);

    printf("shmat complete\n");

    strcpy(shmaddr,"made by chenkai");

    sleep(5);

    shmdt(shmaddr);

    shmctl(shmid,IPC_RMID,0);

    printf("quit!\n");

    return 0;

    读取端:

    #include

    #include

    #include

    #include

    #include

    int main()

    {

    int shmid;

    char *shmaddr;

    key_t key;

    key=ftok(".",1);

    shmid= shmget(key,1024*4,0);

    if(shmid ==-1){

    printf("shmeget error!\n");

    exit(-1);

    }

    shmaddr =shmat(shmid,0,0);

    printf("shmat complete\n");

    printf("data:%s\n",shmaddr);

    shmdt(shmaddr);

    printf("quit!\n");

    return 0;

    }

    输入端写入完毕,5秒后销毁共享内存,如果读取端未在5秒内读取,则读不到:

    cdd66f912e21e6091b9a2b63f24801b2.png

    5秒内执行shmr可以读到:

    368922c0be4ca756b9596df4d36956ae.png以上就是关于Linux进程间通信–共享内存的介绍,尚有不足之处,请各位大神指正。

    salute CLC

    展开全文
  • 一、共享内存的定义和原理 1、共享内存的定义   顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的...

    一、共享内存的定义和原理

    1、共享内存的定义

      顾名思义,共享内存就是允许多个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程

      特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量
    在这里插入图片描述

    2、共享内存的原理

      在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存
    在这里插入图片描述
      当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存这块内存可以被两个进程同时看到。这样当一个进程修改共享内存的数据时,另一个进程访问共享内存时就会得到新的数据

    3、面试题—>超过共享内存的大小限制怎么办?

      在一个linux服务器上,共享内存的总体大小是有限制的,这个大小通过SHMMAX参数来定义(以字节为单位),您可以通过执行以下命令来确定 SHMMAX 的值:

    # cat /proc/sys/kernel/shmmax 
    

      SHMMAX 的默认值是 32MB 。一般使用下列方法之一种将 SHMMAX 参数设为 2GB :

    sysctl -w kernel.shmmax=2147483648 
    

    4、面试题—>同一个进程多次进行shmat会出现什么问题?

      当首次创建共享内存段时,它并不能被任何进程所访问。为了使共享内存区可以被访问,则必须通过 shmat 函数将其附加( attach )到自己的进程空间中,这样进程就与共享内存建立了连接。
    在这里插入图片描述
      这样挂载一个共享内存如果是一次调用是没有问题的,但是一个进程是可以对同一个共享内存多次 shmat进行挂载的,物理内存是指向同一块,如果shmaddr为NULL,则每次返回的线性地址空间都不同。而且指向这块共享内存的引用计数会增加,也就是进程多块线性空间会指向同一块物理地址。这样,如果之前挂载过这块共享内存的进程的线性地址没有被shmdt掉,即申请的线性地址都没有释放,就会一直消耗进程的虚拟内存空间,很有可能会最后导致进程线性空间被使用完而导致下次shmat或者其他操作失败

      可以通过判断需要申请的共享内存指针是否为空来标识是否是第一次挂载共享内存,若是则使用进行挂载,若不是则退出

    void* ptr = NULL; 
    ... 
    if (NULL != ptr) 
    return; 
    ptr = shmat(shmid,ptr,0666); 
    

    二、共享内存的使用

    1. 调用函数shmget()创建一个新共享内存段或者取得一个既有的共享内存段的标识符

    2. 调用函数shmat()将共享内存附加到进程的虚拟地址空间中

    3. 为了引用共享内存,程序需要使用由shmat()函数返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段起点的指针;

    4. 调用函数shmdt()分离共享内存段,调用之后,进程无法再引用这段共享内存。

    5. 调用函数shmctl()删除共享内存段。只有一个进程需要执行这一步。

    1、shmget函数

    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int shmget(key_t key, size_t size, int shmflg);
    
    • 功能创建共享内存或者取得一个既有的共享内存段的标识符
    • 参数
      • key:程序需要提供一个参数key(非0整数),它有效地为共享内存段命名;
      • size:size以字节为单位指定需要共享的内存容量,如果正在创建一个新段,则必须指定size。如果正在引用一个现存的段,则size指定为0.当创建一个新段,段内的内容初始化为0;
      • shmflg:权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
    • 返回值:成功:返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。失败:返回-1。

    2、shmat函数

    #include <sys/types.h>
    #include <sys/shm.h>
    
    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    
    • 功能启动对该共享内存的访问,并把共享内存附加到当前进程的地址空间
    • 参数
      • shm_id:由shmget函数返回的共享内存标识;
      • shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址;
      • shm_flg:一组标志位,通常为0。
    • 返回值:成功:返回一个指向共享内存第一个字节的指针。失败:返回-1。

    3、shmdt函数

    #include <sys/types.h>
    #include <sys/shm.h>
    
    int shmdt(const void *shmaddr);
    
    • 功能将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用
    • 参数
      • shmaddr:shmat函数返回的地址指针。
    • 返回值:成功:返回0。失败:返回-1。

    4、shmctl函数

    #include <sys/shm.h>
    
    int shmctl(int shm_id, int command, struct shmid_ds *buf);
    
    • 功能用来控制共享内存
    • 参数
      • shm_id:shmget函数返回的共享内存标识符;
      • command:要采取的操作,它可以取下面的三个值 :
        • IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值;
        • IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值;
        • IPC_RMID:删除共享内存段。
      • buf:一个结构指针,它指向共享内存模式和访问权限的结构。
    struct shmid_ds
    {
        uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
    };
    
    • 返回值:成功:返回0。失败:返回-1。

    三、共享内存的demo

    // ShmWrite.cpp
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    
    #define BUFFER 4096
    #define PERM S_IRUSR|S_IWUSR
    
    int main(int argc, char *argv[])
    {
        // 创建一个新的共享内存段或者取得一个已有共享内存段的标识符
        int shmid = shmget((key_t)123456, BUFFER, 
        					PERM | IPC_CREAT | IPC_EXCL);
        if (shmid == -1)
        {
            perror("shmget");
            exit(1);
        }
        // 将共享内存段加载到调用进程的虚拟地址空间中
        char* shmaddr = (char *)shmat(shmid, NULL, 0);
        if (shmaddr == (void*)-1)
        {
            perror ("shmat");
            exit(1);
        }
        // 从标准输入读数据
        fgets(shmaddr, 100, stdin);
        // 分离共享内存段
        if (shmdt(shmaddr) == -1)
        {
            perror("shmdt");
            exit(1);
        }
        sleep(10); // 睡眠十秒后共享内存失效
        // 删除这块共享内存
        if (shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("shmctl");
            exit(1);
        }
        return 0;
    }
    
    // ShmRead.cpp
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/stat.h>
    #include <sys/shm.h>
    
    #define BUFFER 4096
    #define PERM S_IRUSR|S_IWUSR
    
    int main(int argc,char **argv)
    {
        int shmid = shmget((key_t)123456, BUFFER, PERM);
        if (shmid == -1)
        {
            perror("shmget");
            exit(1);
        }
        char* shmaddr = (char *)shmat(shmid, NULL, 0);
        if (shmaddr == (void*)-1)
        {
            perror("shmat");
            exit(1);
        }
        // 输出从另一个进程传过来的数据
        printf("%s\n", shmaddr);
        if (shmdt(shmaddr) == -1)
        {
            perror("shmdt");
            exit(1);
        }
        return 0;
    }
    

    四、共享内存总结

      共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC(进程间通信)方式,它是针对其它进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步与通信。

    【优点】:

    • 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

    • 不像匿名管道那样要求通信的进程有一定的父子关系,可用于任意两个进程之间通信。

    【缺点】:

    • 共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

    • 利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信。

    参考:https://www.cnblogs.com/wuchanming/p/4381910.html
    https://blog.csdn.net/echo_ana/article/details/53456543

    展开全文
  • 1、共享内存的定义和原理 2、特点 3、共享内存的使用 4、应用 5、进程间通信的几种方式总结

    1、共享内存的定义和原理

    1.1共享内存的定义
    顾名思义,共享内存就是允许多个不相关的进程访问同一个逻辑内存。我们可以通过下面这张图示来很好的说明。
    在这里插入图片描述

    1. 它是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式
    2. 不同进程之间共享的内存通常安排为同一段物理内存
    3. 进程可以将同一段共享内存连接到他们自己的地址空间中,所有进程都可以访问共享内存中的地址
    4. 如果某个进程向共享内存写入数据,所做的改动立即影响到可以访问统一段共享内存的任何其他进程
      注意!!! 共享内存没有提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动可以阻止第二个进程开始对它进行读取。所以要使用信号量来进行同步控制

    1.1共享内存的原理
    我们都知道,在Linux中,每一个进程都有属于自己的进程控制块和地址空间,并且都有与之对应的页表,负责将进程的虚拟地址空间和物理地址进行映射。如下图所示。
    在这里插入图片描述
    从图中我们可以看出,两个进程的虚拟地址空间通过页表映射到物理空间的同一区域,他们所指的这块区域就是共享内存。
    注意!!
    这块内存可以被两个进程同时看到。这样当一个进程修改共享内存的数据时,另一个进程访问共享内存时就会得到新的数据。
    概括来说:
    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但是多个进程都可以访问

    2、特点

    1、最快的IPC,效率高
    为了充分的说明这一点,我们引入之前进程间通讯的另一种方式——管道和消息队列。因为管道和消息队列进行共享的空间都是由内核对象提供管理,所执行的操作也都是系统调用,而这些数据最终还是要在内存中存储。概括来说,AB两个进程通过管道的方式来进行进程间通信如下:
    在这里插入图片描述
    所以需要四次数据拷贝

    • 从内存空间缓冲区将数据拷贝到内核空间缓冲区
    • 从内核缓冲区将数据拷贝到内存
    • 从内存将数据拷贝内核空间缓冲区
    • 从内核空间缓冲区将数据拷贝到用户空间缓冲区

    而对于共享内存来说,A、B两个进程都有一个各自的指针指向共享内存,可以通过指针访问里面的数据,少了两次拷贝,没有传输的概念。相应的,效率也就变快了。
    在这里插入图片描述
    2、共享内存属于临界资源
    就是由于这个原因,所以我们需要用信号量来进行同步控制。
    3、可以实现任意两个进程之间的通讯
    4、父进程fork子进程或者exec执行一个新的程序,在子进程和新进程里面不会继承父进程之前使用的共享内存。

    3、共享内存的使用

    1. 调用函数shmget创建一个新共享内存段或者取得一个既有共享内存段的标识符
    2. 调用==shmat()==将共享内存附加到进程的虚拟地址空间中
    3. 需要使用shmat()函数返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存起点的指针
    4. 调用==shmdt()==分离共享内存段,调用之后,进程无法再引用这段共享内存
    5. 调用==函数shmctl()==删除共享内存段。

    内核为每个共享内存维护着一个结构,该结构包含以下成员

    struct shmid_ds {
        struct ipc_perm shm_perm;   //用户权限
        size_t          shm_segsz;  //共享内存大小
        time_t          shm_atime;  //最后一次连接时间
        time_t          shm_dtime;  //最后断开连接时间
        time_t          shm_ctime;  //最后改变事件
        pid_t           shm_cpid;   //PID
        pid_t           shm_lpid;   //最后一个PID
        shmatt_t        shm_nattch; //多少个进程正在使用这个共享存储
    };
    
    

    3.1shmget函数

    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmget(key_t key, size_t size, int shmflg);
    

    创建共享内存或者取得一个既有共享内存段的标识符。

    1. key:非0值,他有效的为共享内存段命名
    2. size:指定需要共享的内存容量
    3. shmflg:权限标志。和open函数的mode参数一样,创建它的话,可以与IPC_CREAT做或操作。
    4. 成功返回一个与key相关的共享内存标识符,失败返回-1.

    3.2shmat函数

    #include <sys/types.h>
    #include <sys/shm.h>
    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    

    主要是启动对该共享内存的访问,并把共享内存附加到当前进程的地址空间。

    1. shm_id:由shmget函数返回的共享内存标识
    2. shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址
    3. shm_flg一组标志位,通常为0
    4. 成功返回指向共享内存第一个字节的指针,失败返回-1.

    3.3shmdt函数

    #include <sys/types.h>
    #include <sys/shm.h>
    
    int shmdt(const void *shmaddr);
    

    将共享内存从当前进程中分离。注意!!这种分离并不是删除他,只是使该共享内存对当前进程不再可用

    3.4shmctl函数

    #include <sys/shm.h>
    int shmctl(int shm_id, int command, struct shmid_ds *buf);
    

    用于控制共享内存

    1. shm_id:shmget函数返回的共享内存标识符

    2. command:要采取的操作,可以取下面的三个值
      2.1IPC_STAT:用共享内存的当前关联值覆盖shmid_ds的值;
      2.2IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
      2.3IPC_RMID:删除共享内存段。

    3. buf:一个结构指针,它指向共享内存模式和访问全线结构

    struct shmid_ds
    {
        uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
    };
    
    

    4、应用

    4.1使用共享内存来实现A进程获取用户数据,B进程打印用户数据
    分析:
    仔细分析,我们可以发现其实有两个过程存在

    1. B进程必须在A进程获取数据之后才能打印
    2. A进程必须在B进程将数据处理后才能再次获取

    所以,我们需要使用两个信号来实现同步控制。sem1A控制B,sem2表示B控制A。运行程序,A进程先写入数据,B进程处于阻塞状态,故sem1=0,sem2 = 0.整个同步控制如下图所示。
    在这里插入图片描述
    整个代码的流程如下:

    1. 先进行shmget函数对内存空间的创建和初始化,再连接共享内存,用ptr保存shmat连接到的内存地址

    2. 信号量的初始化和创建

    3. 进程循环读取数据,对sem2信号量P操作,进行数据的写入,写完之后对sem1信号量V操作。B进程与之相反

    4. shmdt断开共享内存连接

    5. shcmtl删除共享内存

    shmA.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<assert.h>
    #include<unistd.h>
    
    #include<sys/shm.h>
    #include"sem.h"
    
    int main()
    {
        int shmid = shmget((key_t)1234,128,IPC_CREAT|0664);
        assert(shmid != -1);
    
        char* ptr = (char*)shmat(shmid,NULL,0);
        assert(ptr != (char*) -1);
    
        int init_val[2] = {0,1};
        int semid = CreateSem(1234,inint_val,2);
        assert(semid != -1);
    
        while(1)
        {
            semP(semid,1);
            printf("input:");
            fgets(ptr,127,stdin);
            semV(semid,0);
            if(strncmp(ptr,"end",3) == 0)
            {
                break;
            }
        }
        shmdt(ptr);
        shmctl(shmid,IPC_RMID,NULL);
    }
    

    shmB.c

    int main()
    {
        srand((unisgned int)(time(NULL)*time(NULL)));
        
        int shmid = shmget((key_t)1234,128,IPC_CREAT|0664);
        assert(shmid != -1);
    
        char* ptr = (char*)shmat(shmid,NULL,0);
        assert(ptr != (char*) -1);
    
        int init_val[2] = {0,1};
        int semid = CreateSem(1234,inint_val,2);
        assert(semid != -1);
    
        while(1)
        {
            SemP(semid,0);
            if(strncmp(ptr,"end",3) == 0)
            {
                break;
            }
            printf("B process: %s",ptr);
            int n = rand()%3+1;
            sleep(n);
            printf("B Deal Over\n");
            memset(ptr,0,128);
            SemV(semid,1);
        }
        shmdt(ptr);
        shmctl(shmid,IPC_RMID,NULL);
    }
    

    测试:
    在这里插入图片描述

    4.2超过共享内存的大小限制怎么办?
    在linux服务器上,共享内存的大小可以通过SHMAX参数来定义(以字节为单位),可以通过执行下面的命令来确定SHMMAX的值。

    # cat /proc/sys/kernel/shmmax
    

    在这里插入图片描述
    可以使用下列的方法之一将SHMMAX参数设为2GB

    sysctl -w kernel.shmmax=2147483648
    

    4.同一个进程多次进行shmat会出现什么问题?
    简单来说shmat的作用就是使进程与共享内存建立连接的。如果同一个进程多次进行shmat则进程多块线性空间会指向同一块物理地址
    这样,如果之前挂载过这块共享内存的进程的线性地址没有被shmdt掉,就会一直消耗进程的虚拟内存空间,很有可能会最后导致进程线性空间被使用完导致下次shmat或者操作失败。

    解决方法:
    就像我们上面代码所操作的一样,通过判断需要申请的共享内存指针是否为空来表示是否是第一次挂载共享内存,若是则使用进行挂载,若不是则退出。

     char* ptr = (char*)shmat(shmid,NULL,0);
     assert(ptr != (char*) -1);
    

    5、进程间通信的几种方式总结

    说到这里,我们就把进程间通信的几种方式给详细说明完了,那么接下来,我们对其各自的特点和使用场景来做一个详细的总结如下。

    通信方式 特点 使用场景
    有名管道 1、半双工通信方式 2、在磁盘上会存在一个管道文件标识,但管道文件并不占用磁盘block空间 应用于同一台主机上的所有权限访问的任意几个进程间通讯
    无名管道 1、半双工通信方式 2、借助父子进程之间共享fork之前打开的文件描述符3、字节流服务 只能用于父子进程之间
    消息队列 1、可以实现信息的随机查询,可以按消息的类型读取2、独立与发送和接收进程,进程终止时,消息队列及其内容并不会删除 3、消息具有特定的格式以及特定的优先级 发送带有类型的数据,可以真正实现多进程间通讯
    信号量 1、主要用于进程间同步 2、基于操作系统的PV操作 负责数据操作的互斥、同步等功能,通过控制其他通信资源来实现进程间通信
    共享内存 1、最快的IPC 2、需要进行同步 3、一般和信号量结合一起使用 使得多个进程访问同一块内存空间 ,通信机制运行效率高
    展开全文
  • 共享内存IPC原理共享内存进程间通信机制主要用于实现进程间大量数据传输,下图所示为进程间使用共享内存实现大量数据传输示意图:共享内存是在内存中单独开辟一段内存空间,这段内存空间有自己特有数据结构...
  • 为了在两个CPU之间交互(AMP:Asymmetric Multi Processing),设计0XFFFF0000...注意设置该地址Memory禁用缓存cache,这样CPU能够及时读到该地址内存中变化数据,Xil_SetTlbAttributes(0xFFFF0000,0x14de2);然后通...
  • 1.共享内存的定义 共享内存就是允许多个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将...
  • 一、共享内存的定义 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。...
  • IPC主题三之共享内存

    2016-09-02 20:06:52
    一、共享内存的定义共享内存就是几个进程能访问到物理地址上的同一块空间,从而实现进程间通信。共享内存是不具备同步和互斥机制的。二、函数解析(1)int shmget( key_t key, size_t size, int flag ); 作用:创建...
  • 共享内存的处理

    2015-11-15 21:56:00
    对于临界区,它是解决线程间的共享内存的,不能垮进程 对于互斥量,可以是线程间的,也可以是进程间的,进程间是利用系统内核对象的,该内核对象是可以有名字,利用该名字可以达到安全的共享内存 在线程之间,临界...
  • 我们先来看看共享内存的定义: 共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理...
  • 怎么定义共享内存 大小 linux

    千次阅读 2014-11-26 15:11:06
    以 root 用户身份登录。编辑文件 /etc/sysctl.conf。...按如下所示,设置 kernel.shmax 和 kernel.shmall 值: echo MemSize > /proc/sys/shmmax echo MemSize > /proc/sys/shmall 其中,MemSize
  • 上一篇博文提到系统调用mmap通过映射一个普通文件实现共享内存。那么本文中介绍System V 共享内存则是通过映射特殊文件系统shm中文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统...
  • 摘要: 总结了共享内存的定义,给出了使用共享内存的使用步骤,分析了使用共享内存需要使用的shmget,shmat,shmdt,shmctl函数,最后给出一个实例加深理解。 一、什么是共享内存  这个字面意思其实就已经很好...
  • Linux为共享内存提供了四种操作。1. 共享内存对象创建或获得。与其它两种IPC机制一样,进程在使用共享内存区域以前,必须通过系统调用sys_ipc...对共享内存对象创建或获得由函数sys_shmget完成,其定义如下:int...
  • 共享内存定义在kernel函数里面还是外面? 我下面有一个类似图像直方图统计功能内核函数,每个线程对应一个像素点。 如果不使用共享内存是这样: ``` atomicAdd(&gpu_EO_0_stats_pix_count[catagory_row_id*...
  • 进程间通信(IPC):共享内存

    千次阅读 2013-01-24 10:32:45
    共享内存的定义如下: #include void *shmat(shm_id,const void *shm_addr, int shmflg);//连接到一个进程的地址空间中 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);//共享内存的控制函数 int shmdt...
  • OpenResty之共享内存的使用

    千次阅读 2018-01-22 00:01:54
    1、定义共享内存100MB,nginx.conf配置文件http域增加如下指令代码: http { lua_shared_dict share_mem_cache 100m; ... } 2、使用共享内存: location /set { content_by_lua_block { local share_mem...
  • ShareMemLib将共享内存代码封装成lib,定义了发送者和监听者 两个进程在实例化ShareMemory时指定相同map,并指定当前为发送者或监听者 发送者负责发送消息,监听者负责接受消息(监听者务必实现接受消息处理方法) ...
  • 共享内存,消息队列

    2019-07-23 16:31:53
    共享内存是最高效的IPC机制,因为它不涉及进程之间的任何...Linux共享内存的API都定义在sys/shm.h头文件中 shmget系统调用创建一段新的共享内存,或者获取一段已经存在的共享内存。定义如下: #include<sys/sh...
  • Linux 编程之共享内存

    2020-08-02 15:08:03
    1、共享内存定义 共享内存就是允许两个或多个... 共享内存的生命周期随内核。即所有访问共享内存区域对象的进程都已经正常结束,共享内存区域对象仍然在内核中存在(除非显式删除共享内存区域对象),在内核重新引导之...
  • linux 共享内存

    2018-11-21 16:15:14
    利用共享内存读取系统log 首先定义一个存储内容结构体 static struct shbuf_ds { int size; // size of data written int head; // start of message list ...
  • linux共享内存

    2017-08-07 14:50:14
    1.定义key 生成一个任意不重复key作为...2.开辟共享内存:shmget(key,size,flag); key:标识 size: 共享内存大小,单位byte flag:权限位,一般创建时|IPC_CREAT,如果|ICP_EXCL则只能新建,已有就报错 返回值:shmi

空空如也

空空如也

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

共享内存的定义