页表共享:一种动态内存扩缩设计
背景
PolarDB:PolarDB数据库共享内存三种形式 & 内存扩缩
当前数据库有三种共享内存形式,是同时存在的:
- mmap方式申请的anonymous shared memory;地址通过fork传递;
- sysv方式申请的buffer pool;这部分分为两个功能,一个叫做persisted buffer pool,这部分是为了我们能在异常重启后继续使用内存,地址通过fork传递;一个叫做dsga,就是我们的共享内存的扩缩,地址通过attach detach内存段实现地址共享(维护了一个1TB的虚拟内存地址,通过fork传递);
- 并行查询的动态共享内存,根据参数可以使用mmap/sysv/posix三种方式,地址通过attach detach内存段实现地址共享。每个进程有自己的内存段地址;
站在OS的角度,这里有两类shared memory需要使用页表共享。
在线扩缩共享内存:
现有的扩缩方式,需要在父进程调整后,attach和detach子进程,在子进程数量大的时候这种通信开销较大。如果使用页表共享,仅仅需要父进程调用madvise(MADV_DONTNEED)即可。如此即可将使用的内存释放出来。扩缩的需求是能够对最小到2M粒度的内存(边界都是2M对齐)进行madvise(MADV_DONTNEED)操作。
MAP_DENYWRITE:被Linux内核屏蔽的flag
一 背景
谈到MAP_DENYWRITE,可能有些陌生。这个flag很少被用户态开发者关注,其中没有被关注的理由主要是“this flag is ignored by os”,简而言之,操作系统(Linux内核)将会忽略掉用户传入的MAP_DENYWRITE标志。回到MAP_DENYWRITE是什么?与MAP_ANONYMOUS、MAP_SHARED、MAP_PRIVATE等一样,是系统调用mmap()函数为映射目标设置映射方式的一种flag。顾名思义,MAP_DENYWRITE表示这段映射的虚拟地址区间不允许写操作,例如,我们通过open()以可写的方式获取文件句柄,将会返回“ETXTBSY: text file is busy”错误。
本文主要介绍MAP_DENYWRITE相关的内容,例如使用了MAP_DENYWRITE的可执行二进制文件和忽略MAP_DENYWRITE的动态共享库。为了描述简单,后面直接使用EXEC和DSO(Dynamic Shared Object)分别表示可执行二进制文件和动态共享库。为了更加清晰的展示MAP_DENYWRITE作用,设计了实验1和实验2来显示用户态可见的差异。同时,借助实验1和实验2也为引出两个疑问:(a)为什么i_writecount会导致“ETXTBSY: text file is busy”;(b)为什么vim“写-存”DSO触发程序“Segment fault”;其答案分别可以在第三节和第四节获取。最后,第五节设计了最后一个实验对实验3进行补充。
二 MAP_DENYWRITE是什么
...