kvm随笔系列一: kvm中的同步控制

Linux 内核提供的管理同步的措施有:
原子操作(automic_t)
内存屏障
自旋锁
信号量
顺序锁
completion
RCU
下面分别看看每种的基本原理以及在KVM中是如何应用的:
(1)  automic_t使用特殊cpu指令(x86采用lock)保证指令不会被其它cpu或环境切换打断。
例子(kvm\i8254.c): pit_timer_fn ==> 
if (ps->reinject || !atomic_read(&ps->pending)) {
atomic_inc(&ps->pending);
queue_kthread_work(&pt->worker, &pt->expired);
}
kvm_pit_ack_irq ==> value = atomic_dec_return(&ps->pending);

(2) 内存屏障
在的CPU一般采用流水线来执行指令。一个指令的执行被分成:取指、译码、访存、执行、写回、等若干个阶段。然后,多条指令可以同时存在于流水线中,同时被执行。
指令流水线并行的,多个指令可以同时处于同一个阶段,只要CPU内部相应的处理部件未被占满即可。比如说CPU有一个加法器和一个除法器,那么一条加法指令和一条除法指令就可能同时处于“执行”阶段, 而两条加法指令在“执行”阶段就只能串行工作。
相比于串行+阻塞的方式,流水线像这样并行的工作,效率是非常高的。然而,这样一来,乱序可能就产生了。
  在多处理器下,除了每个处理器要独自面对上面讨论的问题之外,当处理器之间存在交互的时候,同样要面对乱序的问题。
一 个处理器(记为a)对内存的写操作并不是直接就在内存上生效的,而是要先经过自身的cache。另一个处理器(记为b)如果要读取相应内存上的新值,先得 等a的cache同步到内存,然后b的cache再从内存同步这个新值。而如果需要同步的值不止一个的话,就会存在顺序问题
 __loaded_vmcs_clear ==>
crash_disable_local_vmclear(cpu);
list_del(&loaded_vmcs->loaded_vmcss_on_cpu_link);

/*
* we should ensure updating loaded_vmcs->loaded_vmcss_on_cpu_link
* is before setting loaded_vmcs->vcpu to -1 which is done in
* loaded_vmcs_init. Otherwise, other cpu can see vcpu = -1 fist
* then adds the vmcs into percpu list before it is deleted.
*/
smp_wmb();


loaded_vmcs_init(loaded_vmcs);
crash_enable_local_vmclear(cpu);


vmx_vcpu_load==>
local_irq_disable();
crash_disable_local_vmclear(cpu);


/*
* Read loaded_vmcs->cpu should be before fetching
* loaded_vmcs->loaded_vmcss_on_cpu_link.
* See the comments in __loaded_vmcs_clear().
*/
smp_rmb();


list_add(&vmx->loaded_vmcs->loaded_vmcss_on_cpu_link,
&per_cpu(loaded_vmcss_on_cpu, cpu));
crash_enable_local_vmclear(cpu);
local_irq_enable();

(3)自旋锁
自旋锁使用原则:
任何拥有自旋锁的代码,都不能休眠,不能因为任何原因放弃CPU。除了服务中断!
自旋锁会禁止当前CPU上的抢占,即使在单处理器架构上。
拥有锁的时间越短越好!
在中断中使用自旋锁,那么这个自旋锁必须是通过禁止中断形式上锁的。
在软件中断中使用自旋锁,那么这个自旋锁必须是通过关闭软件中断形式上锁的
static void allocate_vpid(struct vcpu_vmx *vmx)
{
int vpid;


vmx->vpid = 0;
if (!enable_vpid)
return;
spin_lock(&vmx_vpid_lock);
vpid = find_first_zero_bit(vmx_vpid_bitmap, VMX_NR_VPIDS);
if (vpid < VMX_NR_VPIDS) {
vmx->vpid = vpid;
__set_bit(vpid, vmx_vpid_bitmap);
}
spin_unlock(&vmx_vpid_lock);
}

(4) 信号量
内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。只有在资源被释放时,进程才再次变为可运行。只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量
hva_to_pfn ==>
down_read(&current->mm->mmap_sem);
exit:
up_read(&current->mm->mmap_sem);

(5) 顺序锁
适用情况:
1. 当要保护的资源很小,很简单
2. 会被频繁读取访问并且很少发生写入访问
3. 访问会很快
这种情况下就可以使用 seqlock

使用限制:通常,seqlock不能保护包含指针的数据结构。
        kvm中未使用该锁。
使用示例: unsigned int seq;
do {
seq = read_seqbegin(&the_lock);
} while read_seqretry(&the_lock, seq);

(6) Completion
虽然信号量可以用于实现同步,但往往可能会出现一些不好的结果。例如:当进程A分配了一个临时信号量变量,把它初始化为关闭的MUTEX,并把其地址传递给进程B,然后在A之上调用down(),进程A打算一旦被唤醒就撤销给信号量。随后,运行在不同CPU上的进程B在同一个信号量上调用up()。然而,up()down()的目前实现还允许这两个函数在同一个信号量上并发。因此,进程A可以被唤醒并撤销临时信号量,而进程B还在运行up()函数。结果up()可能试图访问一个不存在的数据结构。这样就会出现错误。为了防止发生这种错误就专门设计了completion机制专门用于同步。
 kvm中未使用该机制。
使用说明:
在A函数中,如果需要等待其他的处理,使用void wait_for_completion(struct completion * comp ); 则在这个位置上将处于非中断的sleep,进行等待,也就是相关的线程/进程,用户是无法kill的。 
•在B函数,如果已经处理完,可以交由A函数处理,有下面两种方式 
void complete(struct completion * comp ); 如果要执行A必须等待B先执行,B执行后,A可以继续执行。如果A需要再次执行,则需要确保下一次B执行完。如果连续执行两次B,则可以执行两次A,第三次A要等第三次B执行完。 
void complete_all(struct completion * comp ); 只要B执行完,A就可以执行,无论执行多少次。如果需要再等待B的直系个可以使用INIT_COMPLETION(struct completion * comp ) 。重新初始化completion即可。 
void complete_and_exit(struct completion * comp ,long retval ) ; 这个处理具有complete的功能外,还将调用它的线程/进程终止。可用于一些无限循环的场景,例如受到某个cleaned up的信息后,e通知用户程序终止,允许A函数执行。

(7) RCU
 RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用。RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数据的时候不对链表进行耗时的加锁操作。这样在同一时间可以有多个线程同时读取该链表,并且允许一个线程对链表进行修改(修改的时候,需要加锁)。RCU适用于需要频繁的读取数据,而相应修改数据并不多的情景。在kvm中大量使用了该方式。
例1:
kvm_irq_delivery_to_apic_fast ==>
rcu_read_lock();
map = rcu_dereference(kvm->arch.apic_map);
        .........//对map指针内容的读操作
       rcu_read_unlock();

recalculate_apic_map ==>
new = kzalloc(sizeof(struct kvm_apic_map), GFP_KERNEL);
.....对new指针内容复制
old = rcu_dereference_protected(kvm->arch.apic_map,
lockdep_is_held(&kvm->arch.apic_map_lock));
rcu_assign_pointer(kvm->arch.apic_map, new);

对apic map的读取较频繁,而更新较少。

例2:
读保护:
idx = srcu_read_lock(&kvm->srcu);
......
srcu_read_unlock(&kvm->srcu, idx);

写:
install_new_memslots==>
static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
struct kvm_memslots *slots, struct kvm_memory_slot *new)
{
struct kvm_memslots *old_memslots = kvm->memslots;

update_memslots(slots, new, kvm->memslots->generation);
rcu_assign_pointer(kvm->memslots, slots);
synchronize_srcu_expedited(&kvm->srcu);

kvm_arch_memslots_updated(kvm);

return old_memslots;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: KVM虚拟化技术是目前比较流行并开源的虚拟化技术之一。它不仅简单易用,而且与Linux内核紧密集成,性能也非常出色。 KVM的实战应用非常广泛,包括虚拟化服务器、云平台、桌面虚拟化等。通过KVM,我们可以在一台物理机上虚拟出多台虚拟机,并通过网络进行管理和访问。这样,我们就可以更好地利用物理资源,提高服务器的利用率,从而降低成本。 在KVM的原理解析,我们需要了解KVM是如何实现虚拟化的。首先,KVM利用了Linux内核对硬件的抽象和管理能力,通过向内核注册自己的虚拟设备,实现对硬件的虚拟化。其次,KVM使用了硬件辅助虚拟化技术,例如Intel的VT-x和AMD的AMD-V。这些技术可以使虚拟机在机器语言级别上直接访问物理硬件,并且在虚拟化过程几乎不会产生性能损失。 最后,KVM还支持各种虚拟化技术的组合,例如普通虚拟机、全虚拟化、半虚拟化、硬件辅助虚拟化、PCI设备直通等。这种灵活的虚拟化技术组合方式可以为不同的应用场景提供更好的支持,保证了虚拟机的性能、稳定性和安全性。 综上,KVM虚拟化技术是一种可靠、灵活、高效的虚拟化技术,可以帮助企业更好地利用物理资源,提高服务器的利用率,降低成本。同时,通过KVM的学习,我们能够更好地理解虚拟化技术的原理,为我们今后的学习和工作都将带来更好的支持。 ### 回答2: KVM虚拟化技术是一种基于Linux内核的全虚拟化技术,它可以将一台物理机虚拟出多台互相隔离的虚拟机,每个虚拟机可以独立运行不同的操作系统。KVM虚拟化技术的优点是稳定、可靠、高效,最为重要的是开源免费。 《KVM虚拟化技术:实战与原理解析》这本书为读者讲解了KVM的原理、安装、配置、使用、优化和常见问题解决等内容,既有理论知识又有实践经验。通过对本书的学习,读者可以深入了解KVM虚拟化技术的工作原理及其优势,准确掌握KVM虚拟机的安装和配置方法,提高虚拟机的利用效率,时刻做好系统的优化和维护工作。 该书有助于IT从业人员的技能提升,增强他们应对虚拟化技术挑战的能力。学习本书不仅可以有效提高工作效率,更能加深对虚拟化技术的理解,为今后从事相关工作奠定坚实基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值