Swoole 源码分析——内存模块之共享内存

前言

我们知道,由于 PHP 没有多线程模型,所以 swoole 更多的使用多进程模型,因此代码相对来说更加简洁,减少了各种线程锁的阻塞与同步,但是也带来了新的问题:数据同步。相比多线程之前可以直接共享进程的内存,进程之间数据的相互同步依赖于共享内存。本文将会讲解 swoole 中共享内存的源码。

前置知识:

共享内存数据结构


typedef struct _swShareMemory_mmap

{

    size_t size;

    char mapfile[SW_SHM_MMAP_FILE_LEN];

    int tmpfd;

    int key;

    int shmid;

    void *mem;

} swShareMemory;

  • 注意 mem 是一个 void 类型的指针,用于存放共享内存的首地址。这个成员变量相当于面向对象中的 this 指针,通过它就可以访问到 swShareMemory 的各个成员。

  • size 代表共享内存的大小(不包括 swShareMemory 结构体大小), mapfile[] 代表共享内存使用的内存映射文件的文件名, tmpfd 为内存映射文件的描述符。key 代表使用 System Vshm 系列函数创建的共享内存的 key 值, shmidshm 系列函数创建的共享内存的 id(类似于fd),这两个由于不是 POSIX 标准定义的 api,用途有限。

共享内存的申请与创建

swoole 在申请共享内存时常常调用的函数是 sw_shm_malloc,这个函数可以为进程匿名申请一大块连续的共享内存:


void* sw_shm_malloc(size_t size)

{

    swShareMemory object;

    void *mem;

    size += sizeof(swShareMemory);

    mem = swShareMemory_mmap_create(&object, size, NULL);

    if (mem == NULL)

    {

        return NULL;

    }

    else

    {

        memcpy(mem, &object, sizeof(swShareMemory));

        return mem + sizeof(swShareMemory);

    }

}

  • sw_shm_malloc 函数可以看出,虽然我们申请的是 size,但是实际申请的内存是要略大的,因为还要加上 swShareMemory 这个结构体。当函数返回时,也不会直接返回申请的内存首地址,而是复制了 object 各个成员变量的值后,在申请的首地址上加上 swShareMemory 的大小。

void *swShareMemory_mmap_create(swShareMemory *object, size_t size, char *mapfile)

{

    void *mem;

    int tmpfd = -1;

    int flag = MAP_SHARED;

    bzero(object, sizeof(swShareMemory));

#ifdef MAP_ANONYMOUS

    flag |= MAP_ANONYMOUS;

#else

    if (mapfile == NULL)

    {

        mapfile = "/dev/zero";

    }

    if ((tmpfd = open(mapfile, O_RDWR)) < 0)

    {

        return NULL;

    }

    strncpy(object->mapfile, mapfile, SW_SHM_MMAP_FILE_LEN);

    object->tmpfd = tmpfd;

#endif

#if defined(SW_USE_HUGEPAGE) && defined(MAP_HUGETLB)

    if (size > 2 * 1024 * 1024)

    {

        flag |= MAP_HUGETLB;

    }

#endif

    mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flag, tmpfd, 0);

#ifdef MAP_FAILED

    if (mem == MAP_FAILED)

#else

    if (!mem)

#endif

    {

        swWarn("mmap(%ld) failed. Error: %s[%d]", size, strerror(errno), errno);

        return NULL;

    }

    else

    {

        object->size = size;

        object->mem = mem;

        return mem;

    }

}

  • 由于 swoole 的各个进程都是由 master 进程所建立,也就是各个进程之间存在亲戚关系, 因此swShareMemory_mmap_create 函数直接以 匿名映射 、(/dev/zero 设备) 的方式利用 mmap 建立共享内存,并没有 open 具体的共享内存文件,或者调用 shm_open 打开 POSIX IPC 名字。

  • 值得注意的是 MAP_HUGETLB,这个是 linux 内核 2.6.32 引入的一个 flags,用于使用大页面分配共享内存。大页是相对传统 4K 小页而言的,一般来说常见的体系架构都会提供2种大页大小,比如常见的 2M 大页和 1G 大页。使用大页可以减少页表项数量,从而减少 TLB Miss 的概率,提升系统访存性能。当然有利必有弊,使用大页降低了内存管理的粒度和灵活性,如果程序并不是对内存的使用量特别大,使用大页还可能造成内存的浪费。

共享内存的 calloc

callocmalloc 大同小异,无非多了一个 num 参数


void* sw_shm_calloc(size_t num, size_t _size)

{

    swShareMemory object;

    void *mem;

    void *ret_mem;

    int size = sizeof(swShareMemory) + (num * _size);

    mem = swShareMemory_mmap_create(&object, size, NULL);

    if (mem == NULL)

    {

        return NULL;

    }

    else

    {

        memcpy(mem, &object, sizeof(swShareMemory));

        ret_mem = mem + sizeof(swShareMemory);

        bzero(ret_mem, size - sizeof(swShareMemory));

        return ret_mem;

    }

}

共享内存的 realloc

realloc 函数用于修改已申请的内存大小,逻辑非常简单,先申请新的内存,进行复制后,再释放旧的内存:


void* sw_shm_realloc(void *ptr, size_t new_size)

{

    swShareMemory *object = ptr - sizeof(swShareMemory);

    void *new_ptr;

    new_ptr = sw_shm_malloc(new_size);

    if (new_ptr == NULL)

    {

        return NULL;

    }

    else

    {

        memcpy(new_ptr, ptr, object->size);

        sw_shm_free(ptr);

        return new_ptr;

    }

}

修改共享内存的权限

在内存映射完成后,由标记读、写、执行权限的 PROT_READPROT_WRITEPROT_EXEC 等权限仍可以被 mprotect 系统调用所修改。


int sw_shm_protect(void *addr, int flags)

{

    swShareMemory *object = (swShareMemory *) (addr - sizeof(swShareMemory));

    return mprotect(object, object->size, flags);

}

共享内存的释放


void sw_shm_free(void *ptr)

{

    swShareMemory *object = ptr - sizeof(swShareMemory);

    swShareMemory_mmap_free(object);

}

int swShareMemory_mmap_free(swShareMemory *object)

{

    return munmap(object->mem, object->size);

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • Linux进程通信实现机制有很多,也有各自优缺点和适用场景,关于她们之间的对比,等各种通信机制一一介绍后,再来一个...
    batbattle阅读 4,070评论 3 13
  • 消息队列 消息队列是在内核中实现的,并且是具有一定的优先级的一种进程间通信模型 POSIX PIC消息队列 在un...
    秋风弄影阅读 1,159评论 0 0
  • 总是感觉工作和生活不如意,一有烦心事我就会像朋友诉苦,有时候孤独无助的时候有个人愿意听你诉说,在倾诉的时候...
    evergreenbec阅读 438评论 0 0
  • 王小波说:“生活是一个缓慢受锤的过程。” 在我21岁的某天凌晨,我正在铁路一线值班,消灭事故,跟随着线路师傅跨过...
    好猫1阅读 102评论 0 0
  • 六个小时前,我拔了两颗牙。 是两颗智齿,我也不知道为什么,从一回到家就闹腾着去拍X光,拍完的瞬间,愣了三十秒不到就...
    一朵喵叽阅读 155评论 0 0