面试题: C++怎么实现热更新内存数据?

2022-03-31 20:40:38 +08:00
 java253738191

c++服务端初始化阶段会将某二进制文件load 到内存中,每次请求都会访问该内存数据,c++服务端有持续请求,现在该二进制文件更新了,如何将该二进制文件热更新到内存中且不影响线上请求?内存空间足够

3797 次点击
所在节点    程序员
31 条回复
ysc3839
2022-03-31 20:44:23 +08:00
加载新的到另外一块内存里,加载完了 swap 指针不就好了吗?
Protocol
2022-03-31 20:53:06 +08:00
bitmap
JeromeCui
2022-03-31 20:56:59 +08:00
换个端口,启动后更新 nginx 上游,然后关掉老服务
Kasumi20
2022-03-31 20:59:40 +08:00
发一个通告, 停服 2 小时进行更新
fishCatcher
2022-03-31 21:09:59 +08:00
共享内存实现 jit 吧
OSDI
2022-03-31 21:48:32 +08:00
为内存中二进制文件维护一个 epoch 和 counter ,更新版本 epoch 加 1 ,请求使用该内存,对应 epoch 的 counter 加 1 ,用完 counter 减 1 。二进制文件组成单链表,head 指向最新的二级制文件,用 cas 的方式更新表头,请求使用当前 head 。gc 线程检查链表中除 head 外 epoch 的 counter 值,如果为 0 ,垃圾回收。
nonwill
2022-03-31 21:54:32 +08:00
楼上说的很具体了,再具体到 C++,智能指针保护二进制文件数据,每个请求处理暂存指针副本即可
ipwx
2022-03-31 22:17:44 +08:00
// 全局或者某个单例
std::shared_ptr<...> yourData;


// 载入数据的函数
void loadData() {
std::shared_ptr<...> newData;
// 载入
yourData = newData;
}

// 使用数据的函数
void useData() {
std::shared_ptr<...> theData = yourData;
// 使用 yourData
}
xuanbg
2022-03-31 22:18:26 +08:00
一开始还以为是 C ,刚想说把指针指向新内存地址就完了。。。仔细一看艹!更简单,把新对象赋值给变量就好了嘛。
ysc3839
2022-03-31 22:38:43 +08:00
@ysc3839 补充一下,因为楼主没说具体情况,一些很复杂的情况下这种做法可能也会出问题。
假设一个完整的“请求”是每隔一分钟读取这段数据中的某个字节发送给客户端,要发送一百次才算完成请求。此时如果代码里访问数据是直接用全局的指针,比如 send(globalPointer[x]) 这样,就会出现问题。
需要更完整的细节才能确定正确做法。
3dwelcome
2022-03-31 23:19:37 +08:00
@JeromeCui "换个端口,启动后更新 nginx 上游,然后关掉老服务"

其实不用,很多人并不知道,在最新版本的 linux 内核里,已经支持多个程序绑定同一个 TCP 端口了。
feather12315
2022-04-01 00:10:05 +08:00
我想到一个方法(未尝试):
1. 使用 mmap 建立文件映射
2. 文件更新后使用 madvice ( MADV_DONTNEED )释放映射

理论基础:
文件映射会将文件映射入内存,只有访问了内存才会触发缺页中断载入数据,madvice dontneed 释放了页表,访问相同的内存会再次触发缺页中断载入数据。
feather12315
2022-04-01 00:11:34 +08:00
疑惑:
mmap 载入的数据会不会随文件的更新而自动更新?毕竟 vma 的 backend 是 file ,能够确认的是 msync 这个系统调用可以将内存中的数据 flush 到文件。
Inn0Vat10n
2022-04-01 00:25:40 +08:00
直接分批停机 rolling
exch4nge
2022-04-01 00:26:37 +08:00
@ipwx 多线程情况下修改 shared_ptr 应该是不安全的,建议用 std::atomic_load 与 std::atomic_save 替代修改与读的操作。
icylogic
2022-04-01 02:49:13 +08:00
读到另一块内存里然后 std::atomic<Content*> 一改不就完了……如果要自动释放原来的内存,套一层 shared_ptr ,或者 https://github.com/facebook/folly/blob/main/folly/concurrency/AtomicSharedPtr.h

除非你有其他没说出来的需求,比如同一时刻所有 reader 访问的内容必须一致什么的 ……
documentzhangx66
2022-04-01 04:17:39 +08:00
@ysc3839 大佬说的对。

前面的楼层说直接进行新旧内存区域的替换,我觉得业务上可能会有问题。比如直接替换后,新旧内存区域中的数据结构与偏移量都不一样了,如果替换后,还以以前的方式继续访问,很有可能会出问题。

我觉得,应该是以事务的方式,进行新旧内存区域的替换,并且替换后还要重置数据结构与偏移量会更稳,但这就需要把很多业务,改成支持事务的接口调用,方便抽象,有一定的工作量。具体设计方法,可以参考数据库原理与设计相关书籍。
LifStge
2022-04-01 08:31:58 +08:00
面试题啊 这就..... 想考察啥呢 结果肯定是没有的 不都是要分各自情况吗 能保证直接替换没问题的 加载了 直接替换就是了 ... 关键问题还是要保证对前面调用是否出问题...
说个方法 业务上对于这块数据的获取肯定有指定的几个接口吧 直接热更加载补丁代码 加载完数据 暂停相关线程 把获取数据相关的接口 直接 hook 到补丁代码上 直接返回新数据地址就好了 然后根据自己的业务逻辑 在可以判断前面的数据没有相关引用的情况下 释放掉就好了 再或者强迫症下 就把原本接口的相关数据的地址替换成热更的 再把 hook 的接口恢复过来....
iceheart
2022-04-01 08:44:21 +08:00
这不像面试题,是来要方案了
sfqtsh
2022-04-01 09:13:11 +08:00
还以为说的二进制[可执行]文件呢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/844178

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX