本文原创为freas_1990,转载请表明出处:http://www.jb51.cc/article/p-cpagvcnt-yu.html
如果接触过Oracle数据库,或者多进程框架的server软件,对共享内存的概念自然不会陌生。
共享内存到底是什么呢?共享内存与常见的malloc()获取到的内存有什么区别呢?
在单进程模型里,直接调用malloc即可获取到内存,然后对该内存进行读、写,如果有多线程存在,再通过mutex接口即可做到并发控制。
众所周知的是,进程与进程的内存区域是相互隔开的,如果一个server为了提高并发能力而使用了多进程,此时,多个进程必须要能访问同一块内存区域(才能实现同步),问题出来了。进程与进程的内存区域是隔开的,必须要采用“共享内存”机制,才能让多进程共同访问这一块内存区域。
然而,接下的问题是,posix的标准只给出了shmget()获取到一大块内存,这一大块内存如何才能有效使用?
从本质上来说,如果这一大块内存没用好,它就和“数组”一样呆板无用。
我们来看一下postgresql是如何对共享内存进行slice的呢?
/* * ShmemAlloc -- allocate word-aligned byte string from * shared memory * * Assumes ShmemLock and ShmemFreeStart are initialized. * Returns: real pointer to memory or NULL if we are out * of space. Has to return a real pointer in order * to be compatable with malloc(). */ long * ShmemAlloc(unsigned long size) { unsigned long tmpFree; long *newSpace; /* * ensure space is word aligned. * * Word-alignment is not good enough. We have to be more * conservative: doubles need 8-byte alignment. (We probably only need * this on RISC platforms but this is not a big waste of space.) * - ay 12/94 */ if (size % sizeof(double)) size += sizeof(double) - (size % sizeof(double)); Assert(*ShmemFreeStart); SpinAcquire(ShmemLock); tmpFree = *ShmemFreeStart + size; if (tmpFree <= ShmemSize) { newSpace = (long *)MAKE_PTR(*ShmemFreeStart); *ShmemFreeStart += size; } else { newSpace = NULL; } SpinRelease(ShmemLock); if (! newSpace) { elog(NOTICE,"ShmemAlloc: out of memory "); } return(newSpace); }
相比于已经设计好的malloc,共享内存需要自己设计管理这一大片内存的数据结构。而上面这个函数就类似于malloc里调用的get_one_page()。
本质上,它是在共享内存的起始地址与结束地址之间滑动(cursor的概念)。
从ShmemAlloc返回的内存block会被postgresql的hash数据结构“收容”,此后,这片共享内存才能被正常使用与维护。
原文链接:https://www.f2er.com/postgresql/196002.html