linux共享内存

linux同一机器上数据同步方法,把一块物理内存映射到两个不同的进程中,在进程中看到的内存地址是不一样的,因为进程中只能看到虚拟内存。并且共享内存相当于申请了一块内存,只不过两个进程访问的是同一个地方,并没有提供锁的机制,也就是会出现同时修改数据的问题,需要自己用信号量等同步机制确保不会出现脏读等竞争资源的问题。

shmget

创建一个共享内存或者获取一个共享内存对应的id

 #include <sys/shm.h>

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

key-共享内存唯一id,是数字,自己确定,一个进程创建,另一个进程获取的时候也是根据这个key
size-创建共享内存的大小,字节的个数
shmflg-创建共享内存的类型,一般是IPC_CREAT | 0666IPC_CREAT是创建的意思,0666与linux的权限管理一样,表示可以读写。

shmat

获取一个共享内存id对应的地址,也就是把共享内存附加到当前进程,也就是可以直接使用这块内存了。

#include <sys/shm.h>

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

shmid-上面创建或者获取返回的id
shmaddr-一般为NULL,表示指定要把共享内存附加到哪个地址,NULL表示让系统自己决定
shmflg-一般为0,可以控制附加内存的权限。为0表示可读写。

shmdt

把共享内存从当前进程剥离。

#include <sys/shm.h>

int shmdt(const void *shmaddr);

shmaddr-上面函数返回值,这里是共享内存在当前进程的地址,不是共享内存id

shmctl

操作设置共享内存,大部分用来删除共享内存。

#include <sys/shm.h>

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

shmid-创建的函数返回的共享内存的id
cmd-IPC_RMID,表示销毁共享内存
buf-NULL,控制共享内存的结构体,这里设置为空
所有操作共享内存的进程销毁后,共享内存才会销毁。

示例

下面有两个程序,test.c和test1.c。使用gcc把test.c编译成a,把test1.c编译成b。

gcc test.c -o a 
gcc test1.c -o b

先运行a,在运行b如下

./a
shmid[491568]
p[85fe3000]

p[xbcdefghij]
./b
shmid:491568
p[d86f9000]
abcdefghij

a先运行,创建一个key是1234的共享内存,然后赋值abcdefghij,b再运行,获取一个key为1234的共享内存,然后读取数据,这时可以看到b进程读取到了a的数据。
在b的终端敲一下回车,b会把第一字符赋值为x,再敲一下回车,b退出,在a的终端敲一下回车,a会读取数据,为xbcdefghij,说明b的修改,a中也跟着修改了,并且虽然b退出了,销毁了共享内存,但是由于a还在使用,实际上并没有销毁。
也可以看出在a和b中同一个物理内存的的映射地址是不一样的a中地址是85fe3000,b中地址是d86f9000。所以同一个共享内存的地址在不同进程中的地址是不一致的,因为进程中是虚拟地址。

test.c

#include<stdio.h>
#include<sys/shm.h>

int main()
{
    int shmid = shmget(1234, 1024, IPC_CREAT|0664);
    printf("shmid[%d]\n",shmid);
    void* p = shmat(shmid,NULL,0);
    printf("p[%x]\n", p);
    char * cp = (char*)p;
    for(int i = 0; i < 10; i++)
    {
        *(cp+i) = 'a' + i;
    }
    getchar();
    printf("p[%s]\n", p);
    shmdt(p);
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

test1.c

#include<stdio.h>
#include<sys/shm.h>

int main()
{
    int shmid = shmget(1234, 0, IPC_CREAT|0666);
    printf("shmid:%d\n",shmid);
    void* p = shmat(shmid,NULL,0);
    printf("p[%x]\n", p);
    char * cp = (char*)p;
    printf("%s\n",cp);
    getchar();
    *cp = 'x';
    getchar();
    shmdt(p);
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

https://man7.org/linux/man-pages/man2/shmget.2.html
https://man7.org/linux/man-pages/man3/shmat.3p.html
https://man7.org/linux/man-pages/man3/shmdt.3p.html
https://man7.org/linux/man-pages/man2/shmctl.2.html

posted @ 2023-03-18 15:44  秋来叶黄  阅读(167)  评论(0编辑  收藏  举报