c++服务端初始化阶段会将某二进制文件load 到内存中,每次请求都会访问该内存数据,c++服务端有持续请求,现在该二进制文件更新了,如何将该二进制文件热更新到内存中且不影响线上请求?内存空间足够
![]() |
1
ysc3839 131 天前 via Android ![]() 加载新的到另外一块内存里,加载完了 swap 指针不就好了吗?
|
![]() |
2
Protocol 131 天前
bitmap
|
3
JeromeCui 131 天前
换个端口,启动后更新 nginx 上游,然后关掉老服务
|
![]() |
4
Kasumi20 131 天前 ![]() 发一个通告, 停服 2 小时进行更新
|
5
fishCatcher 131 天前 via iPhone
共享内存实现 jit 吧
|
6
OSDI 131 天前 ![]() 为内存中二进制文件维护一个 epoch 和 counter ,更新版本 epoch 加 1 ,请求使用该内存,对应 epoch 的 counter 加 1 ,用完 counter 减 1 。二进制文件组成单链表,head 指向最新的二级制文件,用 cas 的方式更新表头,请求使用当前 head 。gc 线程检查链表中除 head 外 epoch 的 counter 值,如果为 0 ,垃圾回收。
|
![]() |
7
nonwill 131 天前
楼上说的很具体了,再具体到 C++,智能指针保护二进制文件数据,每个请求处理暂存指针副本即可
|
![]() |
8
ipwx 131 天前 ![]() // 全局或者某个单例
std::shared_ptr<...> yourData; // 载入数据的函数 void loadData() { std::shared_ptr<...> newData; // 载入 yourData = newData; } // 使用数据的函数 void useData() { std::shared_ptr<...> theData = yourData; // 使用 yourData } |
![]() |
9
xuanbg 131 天前
一开始还以为是 C ,刚想说把指针指向新内存地址就完了。。。仔细一看艹!更简单,把新对象赋值给变量就好了嘛。
|
![]() |
10
ysc3839 131 天前 via Android ![]() @ysc3839 补充一下,因为楼主没说具体情况,一些很复杂的情况下这种做法可能也会出问题。
假设一个完整的“请求”是每隔一分钟读取这段数据中的某个字节发送给客户端,要发送一百次才算完成请求。此时如果代码里访问数据是直接用全局的指针,比如 send(globalPointer[x]) 这样,就会出现问题。 需要更完整的细节才能确定正确做法。 |
![]() |
11
3dwelcome 131 天前 ![]() |
![]() |
12
feather12315 131 天前 via Android
我想到一个方法(未尝试):
1. 使用 mmap 建立文件映射 2. 文件更新后使用 madvice ( MADV_DONTNEED )释放映射 理论基础: 文件映射会将文件映射入内存,只有访问了内存才会触发缺页中断载入数据,madvice dontneed 释放了页表,访问相同的内存会再次触发缺页中断载入数据。 |
![]() |
13
feather12315 131 天前 via Android
疑惑:
mmap 载入的数据会不会随文件的更新而自动更新?毕竟 vma 的 backend 是 file ,能够确认的是 msync 这个系统调用可以将内存中的数据 flush 到文件。 |
14
Inn0Vat10n 131 天前
直接分批停机 rolling
|
![]() |
15
exch4nge 131 天前
@ipwx 多线程情况下修改 shared_ptr 应该是不安全的,建议用 std::atomic_load 与 std::atomic_save 替代修改与读的操作。
|
![]() |
16
icylogic 131 天前
读到另一块内存里然后 std::atomic<Content*> 一改不就完了……如果要自动释放原来的内存,套一层 shared_ptr ,或者 https://github.com/facebook/folly/blob/main/folly/concurrency/AtomicSharedPtr.h
除非你有其他没说出来的需求,比如同一时刻所有 reader 访问的内容必须一致什么的 …… |
![]() |
17
documentzhangx66 131 天前 ![]() @ysc3839 大佬说的对。
前面的楼层说直接进行新旧内存区域的替换,我觉得业务上可能会有问题。比如直接替换后,新旧内存区域中的数据结构与偏移量都不一样了,如果替换后,还以以前的方式继续访问,很有可能会出问题。 我觉得,应该是以事务的方式,进行新旧内存区域的替换,并且替换后还要重置数据结构与偏移量会更稳,但这就需要把很多业务,改成支持事务的接口调用,方便抽象,有一定的工作量。具体设计方法,可以参考数据库原理与设计相关书籍。 |
![]() |
18
LifStge 131 天前
面试题啊 这就..... 想考察啥呢 结果肯定是没有的 不都是要分各自情况吗 能保证直接替换没问题的 加载了 直接替换就是了 ... 关键问题还是要保证对前面调用是否出问题...
说个方法 业务上对于这块数据的获取肯定有指定的几个接口吧 直接热更加载补丁代码 加载完数据 暂停相关线程 把获取数据相关的接口 直接 hook 到补丁代码上 直接返回新数据地址就好了 然后根据自己的业务逻辑 在可以判断前面的数据没有相关引用的情况下 释放掉就好了 再或者强迫症下 就把原本接口的相关数据的地址替换成热更的 再把 hook 的接口恢复过来.... |
19
iceheart 131 天前 via Android
这不像面试题,是来要方案了
|
![]() |
20
sfqtsh 131 天前 via Android ![]() 还以为说的二进制[可执行]文件呢
|
21
encro 131 天前
其实最安全可靠的是提供接口,让别人调用,而不是拿文件做交互,
既然是文件交互,那么不可避免的存在实时性问题。 如果不关心实时性问题,那么就如一楼所说,是一个内存指针 swap 的问题了。 |
22
anonymousar 131 天前
2 个 shared ptr swap 。 其实就是 double buffer
|
23
zhangchongjie 131 天前
@iceheart 🤫别说出来
|
24
jones2000 131 天前
2 个内存块,奇偶切换
|
25
java253738191 OP @iceheart @zhangchongjie 你们这格局太小了,动不动说要方案,这种实现方法千千万又不是什么难题
|
26
boaofCHIAN 131 天前
内存够的话 double buffer 双内存块奇偶切换 切换的指针用 atmoi 保护起来
|
27
undef404 131 天前
是给服务端程序添加一段热更新的代码? 还是用另外的程序热更新服务端的内存数据?
|
28
java253738191 OP @boaofCHIAN 切换都简单,不考虑性能下 std::atomic<SharedPtr>或者 AtomicSharedPtr 都可以做,关键要考虑 old 的释放时机,old 内存可能请求正在调用
|
29
java253738191 OP @LifStge 面试题上面还有一题忘记说了,redis 的渐进式 rehash 如何实现的,其中有个 ht[2]如何切换的,衍生出该题
|
![]() |
30
ipwx 131 天前
@exch4nge atomic_load 要自己维护引用计数。
不过你说得对,多线程写同一个 shared_ptr 并不安全。但是多线程读是安全的,因此我们可以这样: std::shared_ptr<...> yourData; std::mutex yourDataLock; // 载入数据的函数 void loadData() { // 载入 std::shared_ptr<...> newData; // 更新 { std::unique_lock<...> lockGuard(yourDataLock); yourData = newData; } } // 使用数据的函数 void useData() { std::shared_ptr<...> theData = yourData; // 使用 yourData } |
![]() |
31
ipwx 131 天前
|