在服务端的开发中,经常会有这种需求:不重启进程的状态下,更新数据。
一般常见的解决思路是:
(1) 全量加载其初始版本v1到内存中
(2) 设置更新接口update,当更新到来时,通过这个接口在内存中更新,并定时刷新到本地磁盘中(由于磁盘io较慢,一般不可能每次更新都及时回写磁盘)。
这种做法的好处是实现思路比较简单直观,但也存在一些缺点:
(1) update时存在资源的竞争,例如需要mutex lock,若更新频繁的话,对性能存在较大影响。
(2) 若进程crash,则更新无法保证一致性。即更新完毕还没刷回磁盘之前,进程挂了,那么这个更新就丢失了。
其实,还有一种解决方案是使用mmap+文件的方式。
(1) 主进程直接打开一个文件并mmap到一块内存区域,按照一定数据结构直接读写它。
(2) 更新进程直接修改这个文件,然后会通过mmap自动的在主进程中生效。
下面通过一段char*的共享,简单的演示了这个原理:
主程序:
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main(int argc, char **argv) { int fd; char *mapped_mem, * p; int flength = 1024; void * start_addr = 0; // open a file fd = open("share.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // mmap file to memory mapped_mem = (char*) mmap(start_addr, flength, PROT_READ, MAP_SHARED, fd, 0); // output memory <---> file for(int i=0; i<100; i++) { printf("%s\n", mapped_mem); sleep(2); } close(fd); munmap(mapped_mem, flength); return 0; }
更新程序:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <cstring> #include <unistd.h> int main() { // write to file int fd = open("share.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); char buf[1024]; // change file content for(int i=0; i<10; i++) { sprintf(buf, "share %d", i); lseek(fd, 0, SEEK_SET); write(fd, buf, strlen(buf)); write(fd, "#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <cstring> #include <unistd.h> int main() { // write to file int fd = open("share.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); char buf[1024]; // change file content for(int i=0; i<10; i++) { sprintf(buf, "share %d", i); lseek(fd, 0, SEEK_SET); write(fd, buf, strlen(buf)); write(fd, "\0", 1); sleep(5); } return 0; }", 1); sleep(5); } return 0; }