RCU (Read-Copy-Update) for synchronization
qemu进程启动后,有一个rcu的线程,用来提供读写锁的
gdb) bt //rcu线程,用来提供读写锁的
#0 0x00007f40799131c9 in syscall () at /lib64/libc.so.6
#1 0x000055f58a9e23dd in qemu_futex_wait (f=0x55f58b3cb174 <rcu_call_ready_event>, val=4294967295) at /home/work/qemu/include/qemu/futex.h:29
#2 0x000055f58a9e25a6 in qemu_event_wait (ev=0x55f58b3cb174 <rcu_call_ready_event>) at util/qemu-thread-posix.c:460
#3 0x000055f58a9ed023 in call_rcu_thread (opaque=0x0) at util/rcu.c:258
#4 0x000055f58a9e2753 in qemu_thread_start (args=0x55f58bb04b30) at util/qemu-thread-posix.c:521
#5 0x00007f4079befdd5 in start_thread () at /lib64/libpthread.so.0
#6 0x00007f4079918ead in clone () at /lib64/libc.so.6
RCU线程函数
static void *call_rcu_thread(void *opaque)
{
struct rcu_head *node;
rcu_register_thread();
for (;;) {
int tries = 0;
int n = atomic_read(&rcu_call_count);
/* Heuristically wait for a decent number of callbacks to pile up.
* Fetch rcu_call_count now, we only must process elements that were
* added before synchronize_rcu() starts.
*/
while (n == 0 || (n < RCU_CALL_MIN_SIZE && ++tries <= 5)) {
g_usleep(10000);
if (n == 0) {
qemu_event_reset(&rcu_call_ready_event);
n = atomic_read(&rcu_call_count);
if (n == 0) {
#if defined(CONFIG_MALLOC_TRIM)
malloc_trim(4 * 1024 * 1024);
#endif
qemu_event_wait(&rcu_call_ready_event);
}
}
n = atomic_read(&rcu_call_count);
}
atomic_sub(&rcu_call_count, n);
synchronize_rcu();
qemu_mutex_lock_iothread();
while (n > 0) {
node = try_dequeue();
while (!node) {
qemu_mutex_unlock_iothread();
qemu_event_reset(&rcu_call_ready_event);
node = try_dequeue();
if (!node) {
qemu_event_wait(&rcu_call_ready_event);
node = try_dequeue();
}
qemu_mutex_lock_iothread();
}
n--;
node->func(node);
}
qemu_mutex_unlock_iothread();
}
abort();
}
-------------------------------------------------------------------------------------
应用场景:
例如有链表list1,里面有三个节点 1 2 3,现在有三个用户A B C 都在使用这个链表list1,A现在要删除节点2,按照常规做法是把list1锁住,阻塞用户B C的使用或者阻塞用户A删除节点2,删除节点2,然后用户B C去重新获取链表,这样的缺点很明显达不到高并发的需求,BC被阻塞了,引入了RCU后,先用rcu修饰链表list1,分为新旧链表,用户删除节点2后生成的链表为新链表list11,此时用户BC不受影响,只是将它们使用的链表list1标识为旧链表list01,等到BC不在操作节点2的时候在去释放节点2
这样的好处:用户A对链表的操作没有阻塞用户B C,而只是标识它们使用的是旧链表,等到它们自身不再使用节点2的时候再去释放,达到了高并发的效果