精华内容
下载资源
问答
  • 共享内存读写冲突
    千次阅读
    2018-04-11 22:08:26

    共享内存的读写冲突问题:
    在设计上尽量就是一个写,一个或多个读。那么要解决的问题有两个:
    1. 读写同步问题,例如如果写一段数据中就有人来读了,那就是读到了一半旧的一半新的数据,播放帧就是花屏,又例如写结构体,先写帧地址,在写帧长度,那么user就可能读到新的地址和旧的长度导致越界(实际和读写锁一样:写比读优先,写时不允许读写,多个可以同时读)。
    2. 写了之后通知读者有更新。和信号量的一对一不同,我们需要多个读者读同一个资源,因此采用信号或futex通知。

    int futex(int *uaddr, int op, int val, const struct timespec *timeout,
                     int *uaddr2, int val3);

    例如:
    等待参数1地址的值改变(自己知道的现值是参数3),参数2为op。timeout为最多等待的时间,为NULL则不更新就一直阻塞。后两个参数忽略。
    futex(&ptr_bq->latest_seq, FUTEX_WAIT, ptr_bq->latest_seq, timeout, NULL, 0);
    通知参数1地址的值已改变,新的值就是这个地址里的值,参数2为op。它最多环境参数3个数的等待者。后三个参数忽略。
    futex(&ptr_bq->latest_seq, FUTEX_WAKE, CUST_NUM_MAX, NULL, NULL, 0);
    注意,第一个参数也是共享内存里的,当然,对于多进程,由于页表不同,第一个参数的虚拟地址(即进程中看到的地址)不同,但物理地址相同的,所以没关系。

    –共享内存同步要注意什么–
    使用共享内存的时候,除了设计上减少同步,还有别的要注意么?
    1,创建任何东西先带着CREAT | EXCL去创建,失败了则直接打开,这是原子性必备的。
    2,共享内存初始化之前如何同步?即如何保证创建chm的进程对共享内存初始化好了,其他进程才能够shmget? 设置mode的x位(用shmctl设置IPC_STAT,因为x位未使用)后开始初始化共享内存,结束后取消x位,任何进程打开共享内存后stat轮询检查x位(用shmctl获取IPC_STAT)是否复位,复位后才可以开始操作。
    3,进程共享的mutex, cond,你应该都会用,不会用看书或者man pthread.h找接口。
    4,共享内存可以做成chunk list内存块链表,一般用于在共享内存中建立树型数据结构,或者建死的多级hash表,或者循环队列,都是可以做的。

    进程间共享的mutex和cond,可实现共享内存的读写互斥:

    int   pthread_condattr_setpshared(pthread_condattr_t *, int);
    int   pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);

    例如(随便找的例子):
    void my_lock_init(char *pathname)
    {
    int fd;
    pthread_mutexattr_t mattr;
    fd=open(“/dev/zero”, O_RDWR, 0);
    mptr=mmap(0, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mptr, &mattr);
    }

    一个进程里的多线程就好办多了,互斥直接用pthread_mutex_lock,对于整数变量的访问可以通过__sync_add_and_fetch来无锁编程。

    更多相关内容
  • Qt 读写共享内存

    千次阅读 2020-10-01 14:05:43
    Qt 读写共享内存 一、简述 记--使用Qt进行简单的读写共享内存。 二、工程结构 三、测试代码 #include <QDebug> #include <QSharedMemory> //测试使用的数据结构 typedef struct{ int age; ...

                                                               Qt 读写共享内存

    一、简述

            记--使用Qt进行简单的读写共享内存。

    二、工程结构

    三、测试代码

    #include <QDebug>
    #include <QSharedMemory>
    
    //测试使用的数据结构
    typedef struct{
        int age;
        char name[12];
    }PeopleInfo_t;//注意考虑内存对齐,尽量凑为8的倍数,不够的添加保留位
    
    //定义共享内存的大小
    #define SHM_SIZE    (sizeof(PeopleInfo_t))
    
    int main(int argc, char *argv[])
    {
        Q_UNUSED(argc);
        Q_UNUSED(argv);
    
        bool isOK;
    
        /************************************ [1] 将数据写入共享内存 ****************************************/
        //1 创建共享内存操作对象
        QSharedMemory *shm_write = new QSharedMemory("QtShmTest");
    
        //2 创建共享内存
        isOK = shm_write->create(SHM_SIZE, QSharedMemory::ReadWrite);
        if (!isOK) {
            qDebug()<<"create shm failed.";
            return -1;
        }
    
        //准备数据
        PeopleInfo_t people = {71, "zhangsan"};
    
        //3 写入数据
        shm_write->lock();//上锁
        char *shm_addr = (char *)shm_write->data();//获取共享内存映射首地址
        memcpy(shm_addr, &people, qMin(SHM_SIZE, sizeof(people)));//写入数据, 这里注意写入的数据长度不能超过共享内存的大小
        shm_write->unlock();//解锁
    
        //4 释放共享内存
        //shm_write->detach(); //因为后面要测试读取,这里放在最后释放
    
        /************************************ [2] 从共享内存读物数据 ****************************************/
        //1 创建共享内存操作对象
        QSharedMemory *shm_read = new QSharedMemory("QtShmTest");
    
        //2 关联到指定的共享内存
        shm_read->attach(QSharedMemory::ReadOnly);//只读
    
        //3 读取数据
        PeopleInfo_t peopleInfo = {0, ""};
        shm_read->lock();//加锁
        memcpy(&peopleInfo, shm_read->constData(), qMin(SHM_SIZE, sizeof(PeopleInfo_t)));
        shm_read->unlock();//解锁
    
        //4 释放资源
        shm_read->detach();
    
        qDebug()<<"age:"<<peopleInfo.age<<" name:"<<peopleInfo.name;
    
        qDebug()<<"QtShm Test Finish. 2020.10.01 ";
    
        shm_write->detach();
        return 0;
    }
    

    四、效果

    五、总结

     5.1 注意事项

    读写共享内存注意不要超过共享内存大小,注意加锁和解锁防止出现死锁情况,共享内存数据结构需要注意内存对齐,不然数据会发生偏移。

    加锁解锁操作会降低操作共享内存的效率,但不加锁就容易导致几个程序同时修改共享内存从而发生数据丢失(相互覆盖)

    所以对共享内存写入数据有必要加锁,对于读取共享内存数据,看具体需求(实时性,数据容错性,数据完整性),一般都会加锁。

     

    5.2 重要步骤

    1. 创建共享内存操作对象
    
    QSharedMemory(const QString &key, QObject *parent = Q_NULLPTR);
    
    2. 创建共享内存
    
    bool create(int size, AccessMode mode = ReadWrite);
    
    3. 共享内存关联--非创建者就需要关联,创建共享内存的不用该步骤
    
    bool isAttached() const;//判断是否已经关联
    
    bool attach(AccessMode mode = ReadWrite);//关联并指定读写方式
       
    
    4. 共享内存数据操作,共享内存首地址
    
    void *data();
    
    const void* constData() const;
    
    const void *data() const;
    
    5 释放--取消关联
    
    bool detach();
    
    如果没有程序关联该共享内存,该共享内存才会真正被释放、被系统回收。
    
    

     

    展开全文
  • 多核DSP之双核多镜像之共享内存

    千次阅读 2018-01-09 20:48:07
    最近在做DSP双核通信,很让人头疼,看了很多IPC示例,发现是阻塞通信,而OpenMp...采用的方法就是利用共享内存,采用的系统是SYS/BIOS系统。 首先在cmd文件中定义共享内存的大小: /* 内存段描述 */ MEMORY { I

    最近在做DSP双核通信,很让人头疼,看了很多IPC示例,发现是阻塞通信,而OpenMp虽然可以做并行系统,但是题目要求是两个核做互不想干的内容、偶尔进行一下通信传递一下数据就可以了。所以在经过大量调研以后,决定采用多镜像IPC通信。

    采用的方法就是利用共享内存,采用的系统是SYS/BIOS系统。
    首先在cmd文件中定义共享内存的大小:

    /* 内存段描述 */
    MEMORY
    {
        IPC         o = 0x80000000  l = 0x10000000 /* 256MB 多核通信专用内存 */
        DDR3_CORE0  o = 0x90000000  l = 0x08000000 /* 128MB 核心 0 专用 DDR3 内存 */
        DDR3_CORE1  o = 0x98000000  l = 0x08000000 /* 128MB 核心 1 专用 DDR3 内存 */
    }

    DDR3的大小是512M,这里分成三块,256M的共享内存,以及各128M的单核使用的内存。内存的大小可根据实际情况自定义。

    1.简单的int类型的整数
    对于简单的整数,只需要在.c文件中进行定义:

    // 共享变量定义
    extern volatile unsigned int far Flag;

    然后在cmd文件中,为该变量进行内存分配:

        .IPC            >  0x80000004              START(Flag)

    在Tsk中,可以直接调用Flag,并为其赋值:

        Flag=1;

    当然,为了把数值写入内存而不是放在cache缓存中,需要把缓存写入内存中:

    Cache_wb((void *)&Flag, 4, Cache_Type_ALLD, TRUE);

    2.对于char型数组
    char型数组实际上与int型整数类似,只不过分配内存的时候不能用IPC定义。
    在.c文件的定义:

     unsigned char confirm_img[100000];

    在cmd文件中的定义:

        .IPC      >       0x86000004   START(confirm_img)

    写入数据的时候,我们可

    memcpy(detect_img, image,  sizeof(image)* sizeof(int));

    写入内存。

    3.结构体
    结构体比较麻烦,之前写了好几次没有成功,后来发现是缓存没有写入内存。
    结构体在.c文件中的定义:

     result_point_info Result;

    在cmd文件中:

        .IPC      >       0x82000004   START(Result)

    每次赋值,写入后,要用

    Result.x=1;
    Cache_wb((void *)&Result.x, 4, Cache_Type_ALLD, TRUE);

    方式进行写入内存。
    并非采用什么通信机制,就是把需要共享的数据放到共享内存中,另一个核需要的时候,再去指定的位置读取数据就可以了~~

    展开全文
  • LINUX共享内存使用常见陷阱与分析

    万次阅读 2014-11-26 15:07:46
    LINUX共享内存使用常见陷阱与分析 October 25, 20112 Comments 所谓共享内存就是使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,...

    LINUX共享内存使用常见陷阱与分析

    所谓共享内存就是使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。其他进程能把同一段共享内存段“连接到”他们自己的地址空间里去。所有进程都能访问共享内存中的地址。如果一个进程向这段共享内存写了数据,所做的改动会即时被有访问同一段共享内存的其他进程看到。共享内存的使用大大降低了在大规模数据处理过程中内存的消耗,但是共享内存的使用中有很多的陷阱,一不注意就很容易导致程序崩溃。


    超过共享内存的大小限制?

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

    # cat /proc/sys/kernel/shmmax
    

    如果机器上创建的共享内存的总共大小超出了这个限制,在程序中使用标准错误perror可能会出现以下的信息:

    unable to attach to shared memory
    

    解决方法:

    1、设置 SHMMAX

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

    通过直接更改 /proc 文件系统,你不需重新启动机器就可以改变 SHMMAX 的默认设置。我使用的方法是将以下命令放入 />etc/rc.local 启动文件中:

    # echo "2147483648" &gt; /proc/sys/kernel/shmmax
    

    您还可以使用 sysctl 命令来更改 SHMMAX 的值:

    # sysctl -w kernel.shmmax=2147483648
    

    最后,通过将该内核参数插入到 /etc/sysctl.conf 启动文件中,您可以使这种更改永久有效:

    # echo "kernel.shmmax=2147483648" &gt;&gt; /etc/sysctl.conf
    

    2、设置 SHMMNI

    我们现在来看 SHMMNI 参数。这个内核参数用于设置系统范围内共享内存段的最大数量。该参数的默认值是 4096 。这一数值已经足够,通常不需要更改。

    您可以通过执行以下命令来确定 SHMMNI 的值:

    # cat /proc/sys/kernel/shmmni
    4096
    

    3、设置 SHMALL

    最后,我们来看 SHMALL 共享内存内核参数。该参数控制着系统一次可以使用的共享内存总量(以页为单位)。简言之,该参数的值始终应该至少为:

    ceil(SHMMAX/PAGE_SIZE)
    

    SHMALL 的默认大小为 2097152 ,可以使用以下命令进行查询:

    # cat /proc/sys/kernel/shmall
    
    2097152
    

    SHMALL 的默认设置对于我们来说应该足够使用。

    注意: 在 i386 平台上 Red Hat Linux 的 页面大小 为 4096 字节。但是,您可以使用 bigpages ,它支持配置更大的内存页面尺寸。

    多次进行shmat会出现什么问题?

    当首次创建共享内存段时,它并不能被任何进程所访问。为了使共享内存区可以被访问,则必须通过 shmat 函数将其附加( attach )到自己的进程空间中,这样进程就与共享内存建立了连接。该函数声明在 linux/shm.h中:

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

    参数 shmid 是 shmget() 的返回值,是个标识符;
    参数 shmflg 是存取权限标志;如果为 0 ,则不设置任何限制权限。在 中定义了几个权限:

    #define SHM_RDONLY 010000 /* attach read-only else read-write */
    #define SHM_RND 020000 /* round attach address to SHMLBA */
    #define SHM_REMAP 040000 /* take-over region on attach */
    

    如果指定 SHM_RDONLY ,那么共享内存区只有读取权限。

    参数 shmaddr 是共享内存的附加点,不同的取值有不同的含义:

    Ø如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给 shmflg 参数指定 SHM_RND 值,如果没有指定,则共享内存区附加到由 shmaddr 指定的地址;否则附加地址为 shmaddr 向下舍入一个共享内存低端边界地址后的地址 (SHMLBA ,一个常址)。

    Ø通常将参数 shmaddr 设置为 NULL 。

    shmat() 调用成功后返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败则返回 -1。

    其映射关系如下图所示:


    图1.1 共享内存映射图

    其中,shmaddr表示的是物理内存空间映射到进程的虚拟内存空间时候,虚拟内存空间中该块内存的起始地址,在使用中,因为我们一般不清楚进程中哪些地址没有被占用,所以不好指定物理空间的内存要映射到本进程的虚拟内存地址,一般会让内核自己指定:

    void ptr = shmat(shmid, NULL,0);
    

    这样挂载一个共享内存如果是一次调用是没有问题的,但是一个进程是可以对同一个共享内存多次 shmat进行挂载的,物理内存是指向同一块,如果shmaddr为NULL,则每次返回的线性地址空间都不同。而且指向这块共享内存的引用计数会增加。也就是进程多块线性空间会指向同一块物理地址。这样,如果之前挂载过这块共享内存的进程的线性地址没有被shmdt掉,即申请的线性地址都没有释放,就会一直消耗进程的虚拟内存空间,很有可能会最后导致进程线性空间被使用完而导致下次shmat或者其他操作失败。

    解决方法:

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

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

    附:

    函数shmat将标识号为shmid共享内存映射到调用进程的地址空间中,映射的地址由参数shmaddr和shmflg共同确定,其准则为:
    (1) 如果参数shmaddr取值为NULL,系统将自动确定共享内存链接到进程空间的首地址。
    (2) 如果参数shmaddr取值不为NULL且参数shmflg没有指定SHM_RND标志,系统将运用地址shmaddr链接共享内存。
    (3) 如果参数shmaddr取值不为NULL且参数shmflg指定了SHM_RND标志位,系统将地址shmaddr对齐后链接共享内存。其中选项SHM_RND的意思是取整对齐,常数SHMLBA代表了低边界地址的倍数,公式“shmaddr – (shmaddr % SHMLBA)”的意思是将地址shmaddr移动到低边界地址的整数倍上。

    Shmget创建共享内存,当key相同时,什么情况下会出错?

    shmget() 用来创建一个共享内存区,或者访问一个已存在的共享内存区。该函数定义在头文件 linux/shm.h中,原型如下:

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

    参数 key是由 ftok() 得到的键值;

    参数 size 是以字节为单位指定内存的大小;

    参数 shmflg 是操作标志位,它的一些宏定义如下:

    IPC_CREATE : 调用 shmget 时,系统将此值与其他共享内存区的 key 进行比较,如果存在相同的 key ,说明共享内存区已存在,此时返回该共享内存区的标识符,否则新建一个共享内存区并返回其标识符。

    IPC_EXCL : 该宏必须和 IPC_CREATE 一起使用,否则没意义。当 shmflg 取 IPC_CREATE | IPC_EXCL 时,表示如果发现内存区已经存在则返回 -1,错误代码为 EEXIST 。

    注意,当创建一个新的共享内存区时,size 的值必须大于 0 ;如果是访问一个已经存在的内存共享区,则置 size 为 0 。

    一般我们创建共享内存的时候会在一个进程中使用shmget来创建共享内存,

    Int shmid = shmget(key, size, IPC_CREATE|0666);
    

    而在另外的进程中,使用shmget和同样的key来获取到这个已经创建了的共享内存,

    Int shmid = shmget(key, size, IPC_CREATE|0666);
    

    如果创建进程和挂接进程key相同,而对应的size大小不同,是否会shmget失败?

    Ø 已经创建的共享内存的大小是可以调整的,但是已经创建的共享内存的大小只能调小,不能调大

    shm_id = shmget(key,4194304,IPC_CREAT);
    

    创建了一个4M大小的共享内存,如果这个共享内存没有删掉,我们再使用

    shm_id = shmget(key,10485760,IPC_CREAT);
    

    来创建一个10M大小的共享内存的时候,使用标准错误输出会有如下错误信息:

    shmget error: Invalid argument
    

    但是,如果我们使用

    shm_id = shmget(key,3145728,IPC_CREAT);
    

    来创建一个3M大小的共享内存的时候,并不会输出错误信息,只是共享内存大小会被修改为3145728,这也说明,使用共享内存的时候,是用key来作为共享内存的唯一标识的,共享内存的大小不能区分共享内存。

    这样会导致什么问题?

    当多个进程都能创建共享内存的时候,如果key出现相同的情况,并且一个进程需要创建的共享内存的大小要比另外一个进程要创建的共享内存小,共享内存大的进程先创建共享内存,共享内存小的进程后创建共享内存,小共享内存的进程就会获取到大的共享内存进程的共享内存,并修改其共享内存的大小和内容(留意下面的评论补充),从而可能导致大的共享内存进程崩溃。

    解决方法:

    方法一:

    在所有的共享内存创建的时候,使用排他性创建,即使用IPC_EXCL标记:

    Shmget(key, size,IPC_CREATE|IPC_EXCL);
    

    在共享内存挂接的时候,先使用排他性创建判断共享内存是否已经创建,如果还没创建则进行出错处理,若已经创建,则挂接

    Shmid = Shmget(key, size,IPC_CREATE|IPC_EXCL);
    
    If (-1 != shmid)
    
    {
    
    Printf("error");
    }
    
    Shmid = Shmget(key, size,IPC_CREATE);
    

    方法二:

    虽然都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现,即这种方法不使用key来创建共享内存,由操作系统来保证唯一性。

    ftok是否一定会产生唯一的key值?

    系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

    ftok原型如下:

    key_t ftok( char * pathname, int proj_id)
    

    pathname就时你指定的文件名,proj_id是子序号。

    在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为0×010002,而你指定的proj_id值为38,换算成16进制为0×26,则最后的key_t返回值为0×26010002。

    查询文件索引节点号的方法是: ls -i

    但当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

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

    所以如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值

    如果存在生成key_t值的文件被删除过,则很有可能自己现在使用的共享内存key_t值会和另外一个进程的key_t值冲突,如下面这种情况:

    进程1使用文件1来ftok生成了key10000,进程2使用文件2来ftok生成了key 11111,此时如果进程1和进程2都需要下载文件,并将文件的内容更新到共享内存,此时进程1和2都需要先下文件,再删掉之前的共享内存,再使用ftok生成新的key,再用这个key去申请新的共享内存来装载新的问题,但是可能文件2比较大,下载慢,而文件1比较小,下载比较慢,由于文件1和文件2都被修改,此时文件1所占用的文件节点号可能是文件2之前所占用的,此时如果下载的文件1的ftok生成的key为11111的话,就会和此时还没有是否11111这个key的进程2的共享内存冲突,导致出现问题。

    解决方法:

    方法一:

    在有下载文件操作的程序中,对下载的文件使用ftok获取key的时候,需要进行冲突避免的措施,如使用独占的方式获取共享内存,如果不成功,则对key进行加一操作,再进行获取共享内存,一直到不会产生冲突为止。

    方法二:

    下载文件之前,将之前的文件进行mv一下,先“占”着这个文件节点号,防止其他共享内存申请key的时候获取到。

    另外:

    创建进程在通知其他进程挂接的时候,建议不使用ftok方式来获取Key,而使用文件或者进程间通信的方式告知。

    共享内存删除的陷阱?

    当进程结束使用共享内存区时,要通过函数 shmdt 断开与共享内存区的连接。该函数声明在 sys/shm.h 中,其原型如下:

    #include
    #include
    int shmdt(const void *shmaddr);
    

    参数 shmaddr 是 shmat 函数的返回值。
    进程脱离共享内存区后,数据结构 shmid_ds 中的 shm_nattch 就会减 1 。但是共享段内存依然存在,只有 shm_attch 为 0 后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除。一般来说,当一个进程终止时,它所附加的共享内存区都会自动脱离。

    我们通过

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

    来删除已经存在的共享内存。

    第一个参数,shmid,是由shmget所返回的标记符。

    第二个参数,cmd,是要执行的动作。他可以有三个值:

    命令 描述
    IPC_STAT 设置shmid_ds结构中的数据反射与共享内存相关联的值。
    IPC_SET 如果进程有相应的权限,将与共享内存相关联的值设置为shmid_ds数据结构中所提供的值。
    IPC_RMID 删除共享内存段。

    第三个参数,buf,是一个指向包含共享内存模式与权限的结构的指针,删除的时候可以默认为0。

    如果共享内存已经与所有访问它的进程断开了连接,则调用IPC_RMID子命令后,系统将立即删除共享内存的标识符,并删除该共享内存区,以及所有相关的数据结构;
    如果仍有别的进程与该共享内存保持连接,则调用IPC_RMID子命令后,该共享内存并不会被立即从系统中删除,而是被设置为IPC_PRIVATE状态,并被标记为”已被删除”(使用ipcs命令可以看到dest字段);直到已有连接全部断开,该共享内存才会最终从系统中消失。

    需要说明的是:一旦通过shmctl对共享内存进行了删除操作,则该共享内存将不能再接受任何新的连接,即使它依然存在于系统中!所以,可以确知,在对共享内存删除之后不可能再有新的连接,则执行删除操作是安全的;否则,在删除操作之后如仍有新的连接发生,则这些连接都将可能失败!

    Shmdt和shmctl的区别:

    Shmdt 是将共享内存从进程空间detach出来,使进程中的shmid无效化,不可以使用。但是保留空间。
    而shmctl(sid,IPC_RMID,0)则是删除共享内存,彻底不可用,释放空间。

    2 thoughts on LINUX共享内存使用常见陷阱与分析

    1. dcshi
      October 25, 2011 at 5:33 am

      ========问题:
      文中提到:
      当多个进程都能创建共享内存的时候,如果key出现相同的情况,并且一个进程需要创建的共享内存的大小要比另外一个进程要创建的共享内存小,共享内存大的进程先创建共享内存,共享内存小的进程后创建共享内存,小共享内存的进程就会获取到大的共享内存进程的共享内存,并“修改其共享内存的大小”和内容,从而可能导致大的共享内存进程崩溃。

      ========根据以上描述:
      1) 首先,进程A: shmid = shmget(key, 32*1024, IPC_CREAT|(SHM_R|SHM_W));
      shmptr = shmat(shmid, 0, 0)
      2) 然后,进程B:shmid = shmget(key, 16*1024, IPC_CREAT|(SHM_R|SHM_W));
      因为key一样,所以进程A 创建的共享内存大小也就改成了 16*1024
      3) 那么,进程A:memset(shmptr, 0, 32*1024) 将是非法操作

      =======实际测试和结果:
      测试环境: 基于Linux内核 2.6.16.46-0.12-xenpae-1024-18
      测试结论: 进程A创建的共享内存大小并未改变,memset操作也没有问题。
      原因: 进程中的共享内存是改进程虚拟内存空间到物理内存的映射,进程间互相独立。

      相关数据:
      parent: stat.size = 32768
      parent: stat.size = 32768
      child: x

      [2]+ Done ./shm.exe

      ======相关代码:

      80 int main(int argc, void **argv)
      81 {
      82 int shmid;
      83 char *shmptr;
      84 key_t key = ftok("/usr/bin", 0x12345678);
      85 int pid;
      86 struct shmid_ds stat;
      87 pid = fork();
      88 if(0 == pid)//child
      89 {
      90 sleep(1); //等parent创建shm
      91 shmid = shmget(key, 16*1024, IPC_CREAT|SHM_R);
      92 shmptr = shmat(shmid, 0, 0);
      93 sleep(5);
      94 printf("child: %cn", *(char *)shmptr);
      95 memset(shmptr, 'y', 32*1024); //这样memset 32k 也没有问题
      96
      97 }
      98 else if(0 < pid) //parent
      99 {
      100 shmid = shmget(key, 32*1024, IPC_CREAT|(SHM_R|SHM_W));
      101 shmptr = shmat(shmid, 0, 0);
      102 shmctl(shmid, IPC_STAT, &stat);
      103 printf("parent: stat.size = %dn", stat.shm_segsz);
      104 sleep(3); //等child 修改size
      105 shmctl(shmid, IPC_STAT, &stat);
      106 printf("parent: stat.size = %dn", stat.shm_segsz);
      107 memset(shmptr, 'x', 32*1024);
      108 }
      109 else
      110 {
      111 }
      112
      113 return 0;
      114 }
      
      • dcshi
        October 25, 2011 at 5:40 am

        === 关于shmdt和shmctl(shmid, IPC_RMID ===

        shmdt — 将进程内存空间的虚拟内存 和 内核空间的共享内存 脱离关系
        每调用1次,attachCnt减1,当 attachCnt 为0时,内核空间的共享内存“才可以”被删除
        [!] 进程退出时,会将之前 attach 的shm自动 detach。

        所以,即使进程退出时进行了dt,但共享内存在内核中占用的空间并未释放。

        RMID — 删除ID,并尝试释放内核的相关空间(如果attachCnt为0才释放)
        这是让id失效,进程不能再对该id进行attach,并尝试释放内核相应的空间。

        探讨: 如果进程创建了共享内存,虽然可能显式或隐式地进行了dt,但如果没有调用 shmctl(id, IPC_RMID…)
        该共享内存占用的内核空间应该是没有释放的。
        换言之,不断地创建,而没有RMID,应该会造成相关内存泄漏,至少是ID泄漏。

        佐证:
        1) 《UNIX网络编程》卷2,第一章说明:
        SysV的共享内存的持续性(persistency)是 kernel-persistency。
        即:如果不显式地删除(RMID),那么只有等内核重启才能释放。
        2) 之前邮件的程序也没有RMID,运行后,用ipcs 很容易看到ID 还存在!
        3) APUE关于shm相关章节的程序里没有dt(因为进程退出时会自动dt),但进行了RMID

        使用ipcs命令可以一直看到挂接数为0的共享内存,你这个时候用相同的ID去挂接还是能挂接上的,说明这个共享内存一直是存在的,这样说明不删除共享内存肯定会导致相关的内存泄漏。
        另外,使用共享内存时候,最好不要使用进程退出来dt,应该自己程序来保证,还有一种情况,进程异常退出了,系统是不会去dt的,这时候就要依赖自己来保证了,去屏蔽系统异常信号,自己去dt并删除共享内存。


    展开全文
  • 往小了说,golang建议使用channel来共享信息而不是使用共享内存,这是一种优雅的方式,避免了数据同步带来的繁琐和低效。 往大了说,本质上还是让资源去调度请求,而不是让请求去调度资源。 资源就那么多,所有请求...
  • linux不同进程使用共享内存及互斥锁

    千次阅读 2022-03-09 09:42:40
    linux不同进程使用共享内存及互斥锁 1 共享内存 头文件: #include <sys/shm.h> #include <sys/ipc.h> 1.1 key值 指定一个文件地址,让不同进程可以通过ftok产生同一个key值: std::string path = ("/...
  • GPU 共享内存bank冲突(shared memory bank conflicts) 时间 2016-11-05 21:47:58 FindSpace 原文 http://www.findspace.name/easycoding/1784 主题 共享内存 Introduction本文总结了GPU上共享内存的bank ...
  • 进程同步(读写共享内存

    千次阅读 2014-09-14 09:23:30
     进程同步(读写共享内存
  • 共享内存(shared memory共享内存 共享内存 共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过mmap()映射普通文件 (特殊情况下还可以采用...
  • 共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache)。任何一个缓存的数据被更新后,由于其他...
  • CUDA编程第五章: 共享内存&常量内存

    千次阅读 2021-02-15 23:18:35
    了解数据在共享内存中是如何被安排的 掌握从二维共享内存到线性全局内存的索引转换 解决不同访问模式中存储体中的冲突共享内存中缓存数据以减少对全局内存的访问 使用共享内存避免非合并全局内存的访问 ...
  •   前面简单得学习了共享内存和信号量,其中信号量的一个重要作用就是对共享资源的保护,保证共享资源在一个时刻只有一个进程独享。那么现在,就来测试一下。 一.测试代码 #include <stdio.h> #include <...
  • Peterson算法是一种用于互斥的并发编程算法,该算法允许两个或多个进程共享一次使用的资源而不会发生冲突,仅使用共享内存进行通信。 算法实现: 该算法使用两个变量flag和turn。一个flag[n]的值true表示进程n想...
  • 共享内存的创建,以及亲缘进程和非亲缘进程之间的如何使用共享内存
  • 实验内容:进程间通信—共享内存。 (1)、验证:编译运行课件 Lecture 08 例程代码: Linux 系统调用示例 reader-writer 问题:Algorithms 8-1 ~ 8-3. POSIX API 应用示例 producer-consumer 问题:Algorithms 8-4 ~...
  • 操作系统(二):父子进程和共享内存 一、杂七杂八的资料 1. fprintf: 在此之前先区分一下:printf,sprintf,fprintf。 printf就是标准输出,在屏幕上打印出一段字符串来。 sprintf就是把格式化的数据写入到...
  • 这一段因为持续使用共享内存和内存映射,最早的例子也是从网上找的,然后改了一下满足我们的需求,但底层的区别和联系没有细想。今天因为要测试给认知框架开发的数据共享库涉及到了共享内存,同事问这些事情,而前面...
  • 指针篇之十 传递指针共享内存

    千次阅读 2013-12-09 15:20:31
    而指针作为内存间接访问的索引,可实现内存共享机制,代替内存间的大数据搬运和拷贝。打个粗俗的比方,小孩在家里随地大小便,大人每次都要在后面处理,这时真希望他会蹲马桶啊!第一步一定是告诉并让他记住马桶在...
  • 共享内存加锁互斥访问

    万次阅读 2017-03-07 00:22:28
    在做项目时遇到需要用到多个进程对共享内存读写,考虑到数据冲突问题,特加上互斥作为访问约束条件,具体代码如下: HANDLE CreateOpenFileMapping(LPCTSTR lpShareMemName) { //打开共享的文件对象。  HANDLE ...
  • 共享内存共享内存是进程间通信中最简单的方式之一。共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个...
  • mmap共享内存

    千次阅读 2018-12-14 19:56:19
    二 、mmap内存映射原理 三、mmap和常规文件操作的区别 四、 mmap优点总结 五、mmap相关函数 六、程序举例 七、mmap使用细节 一、mmap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到...
  • 在“c++共享内存(一)”中,我写了一个demo,实现了 程序A接受到用户输入的字符串后存放到共享内存中,程序B收到用户输入任意字符的操作之后会从共享内存中读取该字符串并显示在控制台中。 但这有个问题,如果程序A...
  • Linux 进程间通信(IPC)—共享内存
  • 共享内存因为需要分配给不同的线程所以被分成了不同个Bank,一个Warp中有32个线程,在比较老的GPU中,16个Bank可以同时互相访问,即一条指令就可以让半个Warp同时访问16个Bank,这种并行访问的效率可以极大的提高...
  • 共享内存是一种IPC形式,与其它IPC机制如管道、消息队列等相比,数据不必在进程与内核间多次交换,进程间通信的速度更快。当共享内存区映射到共享它的进程的地址空间时,再加以一些同步控制,这些进程就可以进行数据...
  • 搬:Linux C++内存映射方式读写文件

    千次阅读 2019-05-25 15:13:30
    Linux C++内存映射方式读写文件 Linux C++内存映射方式读写文件 584851044 0人评论 5181人阅读 2014-03-01 15:12:08 内存映射就是将磁盘上的文件映射到系统内存中,对内存的修改可以同步到对磁盘文件的修改。...
  • 简单看下Java 的内存及其共享

    千次阅读 2017-08-06 10:56:22
    简单看下Java 的内存及其共享本文简单分析下Java的内存, 结合CPU的的架构, 看看内存共享, 以及为什么会遇到的伪共享的问题. 第一: CPU的相关知识 目前的程序大部分跑在多核多线程处理器上, 下面是i3 双核4线程的...
  • 共享内存无锁队列的实现

    千次阅读 2017-11-09 14:52:24
    共享内存无锁队列的实现 躲在树上的数据库 2017-11-06 211标签: 消息队列 , 无锁队列 作者:范健 导语: 共享内存无锁队列是老调重弹了,相关的实现网上都能找到很多。但看了公司内外的很多...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,783
精华内容 22,313
关键字:

共享内存读写冲突