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

2018-08-11  本文已影响0人  leoyang90

前言

我们知道,由于 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;

共享内存的申请与创建

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);

    }

}


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;

    }

}

共享内存的 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);

}

上一篇 下一篇

猜你喜欢

热点阅读