共享内存和Cgroup

821 阅读2分钟

背景

memory cgroup中没有任务,但memory.stat中的cache统计项显示有值,经排查是共享内存没有释放,故进行问题复现和原理追究。

问题复现

问题复现的总体思路也很简单,就是创建两个进程,这个两个进程使用共享内存进行通信,如何将一个进程加入到一个memoy cgroup中,查看内存统计值。

先查看当前系统的共享内存使用情况。如下图所示,当前系统shared的值为25M,当前系统没有利用共享内存进行通信的进程。

image.png

生成大小为20M的共享内存,代码如下。

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>

int main(int argc, const char * argv[]) {
    
    //生成一个key
    key_t key = ftok("./", 88);
    
    //创建共享内存,返回一个id
    //数字 4 、2 和 1表示读、写、执行权限
    //用户、所属组、其他组都有读写权限
    int shmid = shmget(key, 20*1024*1024, IPC_CREAT|0666); // IPC_CREAT: Create entry if key does not exist
    printf("共享内存的shmid is %d \n", shmid);
    
    if (shmid == -1) {
        perror("shmget failed");
        //exit(0) 表示程序正常退出,exit⑴/exit(-1)表示程序异常退出。
        exit(1);
    }
    
    //映射共享内存,得到虚拟地址
    //shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
    //shmflg: SHM_RDONLY:为只读模式,其他为读写模式
    void *p = shmat(shmid, NULL, 0);
    if (p == (void *)-1) {
        perror("shmat failed");
        exit(2);
    }
    
    //写共享内存
    int *pp = p;
    int i;
    for(i=0;i<20*1024*1024/4;i++){
        *(pp + i) = i;
    }
    printf("写入共享内存成功,点击回车解除内存映射\n");
    getchar();
    
    //解除映射
    if (shmdt(p) == -1) {
        printf("shmdt failed");
        exit(3);
    }
    
    printf("解除映射成功,点击回车销毁共享内存\n");
    getchar();
    
    //IPC_RMID:删除这片共享内存
    if (shmctl(shmid, IPC_RMID, NULL)) {
        perror("shmctl failed");
        exit(4);
    }
    
    return 0;
}

image.png

image.png

读取共享内存中的值,代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>

int main() {
    
    //生成key
    key_t key = ftok("./", 88);
    
    //获取共享内存,返回id
    //0:只获取共享内存时指定为0
    //shmflg0:取共享内存标识符,若不存在则函数会报错
    int shmid = shmget(key, 0, 0);
    if (shmid == -1) {
        perror("shmget failed");
        exit(1);
    }
    
    //映射共享内存,得到虚拟地址
    void *p = shmat(shmid, 0, 0);
    if (p == (void *)-1) {
        perror("shmat failed");
        exit(2);
    }
    
    //读取共享内存
    int data1 = *(int *)p;
    int data2 = *((int *)p + 1);
    printf("共享内存第1000个的数为 %d", *((int *)p + 999));
    
    //printf("从共享内存中读取了,%x 和 %x\n", data1, data2);

    printf("点击回车解除共享内存映射\n");
    getchar();
    
    //解除映射
    if(shmdt(p) == -1) {
        perror("shmdt failed");
        exit(3);
    }
    
    
    return 0;
}

image.png

image.png

从上图可以看到,有两个进程正在使用共享内存进行通信,下面将读进程利用memory cgroup进行内存限制。

image.png

查看内存统计信息,发现内存使用为0,复现失败!! image.png

发现没有按预期出现问题,顿时不知所措,查阅相关资料,了解了共享内存有多种实现方式,故试一试其它共享内存的方式。

参考资料

宋宝华:世上最好的共享内存(Linux共享内存最透彻的一篇)