欢迎各位兄弟 发布技术文章
这里的技术是共享的
Disk IO:
磁盘IO的调度器(IO Scheduler)常用的是哪4种
CFQ: Complete Fair Queue 完全公平队列
deadline: 最后期限调度
anticipatory: 期望调度器(它期望我们在读一个数据之后,还会读这个数据之后的其它数据,会等待几毫秒,所以它的性能表现在很多场景下都不太适用,因为这种等待很可能是白费的,尤其是在随机读写非常多的场景当中,,,它最适合的是在顺序读写比较多的场景当中??????)
NOOP: no opreation 没有操作方法调度器
想换一个Disk IO Scheduler,在 /sys/block/<device>/queue/scheduler 下面换?默认是CFQ
CFQ和deadline有一些补充性的参数,可以参考红帽官方的文档,了解一下可以调整的大小
Memory:MMU: 是Memory Management Unit的缩写,中文名是内存管理单元,是个硬件芯片
TLB: Translation Lookaside Buffer,它事实上是缓存,不是缓冲buffer,是缓存MMU转换的结果的,为了提高TLB的性能,有个手段叫使用大内存页(Hugetable page),也是一个在硬件中实现的缓存芯片
vm.swappiness,overcommit_memory,overcommit_ratio这三个在生产环境中经常拿来控制内存使用的
vm.swappiness: 调整一个进程到底是不是使用swap的,使用vm.swappiness来评定,vm.swappiness={0..100},,,,,,,,,,,,,,(使用交换内存可能会降低系统性能的,但假如物理内存不够用的话,可以使用交换内存),,,,,,,,,,,,,,,,vm.swappiness可以控制我们在多大程度上倾向于匿名页(anonymous page)交换到交换分区(交换设备)的,,,,,,,,,,指的是交换分区的倾向性,默认是60,越小,越不倾向使用,越大,越倾向使用,(页缓存page cache所占的百分比,加上vm.swappiness的百分比,,如果大于等于100%的话,就会开始使用交换分区了)
overcommit_memory: 过量使用内存,,,,在允许过量使用的情况下,才能使用swap,即 此时内存为( RAM(物理内存)+swap)
有三个值0,1,2,,,0表示启发式过量,1表示总是过量,2表示看overcommit_ratio是否超出指定百分比
overcommit_ratio 当overcommit_memory为2时,overcommit_ratio才有意义,
overcommit_ratio=50,意味着可使用的内存空间是多大?
计算公式: swap+RAM*ratio,,,,,
假设 swap: 4G,,RAM: 8G,,,可以使用的物理内存是 4G+8G*50%=8G (在50%的情况下,swap是RAM的一半就能保证RAM是全部使用的)
假设 swap: 4G,,RAM: 4G,,,可以使用的物理内存是 4G+4G*50%=6G
假设 swap: 2G,,RAM: 8G,,,可以使用的物理内存是 2G+8G*50%=6G (明明物理内存是8G,但是只能使用6G,不理想,,,,超过6G时,马哥说使用交换分区?又说可能产生OOM的,,他也是不太清晰,,当然这种道理知道了,也没什么作用)
要想充分100%的使用物理内存,设定方法:
第1种方法: swap是跟RAM一样大,vm.swappiness=0(尽量不使用交换分区),这样保证了RAM都能使用了
第2种方法: vm.swappiness=0(尽量不使用交换分区),overcommit_memory=2,overcommit_ratio=100,(此时所有可用内存就是 swap+RAM,,,,这样子的话有可能使用交换内存了,但vm.swappiness=0时,就尽量不使用交换内存了),,,这样既保证了RAM充分使用,又能使用swap了,,,,,,,,,,,,很多生产环境中是这样写的
[root@localhost ~]# cd /proc/sys/vm/
[root@localhost vm]# ls
admin_reserve_kbytes hugepages_treat_as_movable mmap_min_addr panic_on_oom
block_dump hugetlb_shm_group nr_hugepages percpu_pagelist_fraction
compact_memory laptop_mode nr_hugepages_mempolicy scan_unevictable_pages
dirty_background_bytes legacy_va_layout nr_overcommit_hugepages stat_interval
dirty_background_ratio lowmem_reserve_ratio nr_pdflush_threads swappiness
dirty_bytes max_map_count numa_zonelist_order unmap_area_factor
dirty_expire_centisecs meminfo_legacy_layout oom_dump_tasks vfs_cache_pressure
dirty_ratio memory_failure_early_kill oom_kill_allocating_task would_have_oomkilled
dirty_writeback_centisecs memory_failure_recovery overcommit_kbytes zone_reclaim_mode
drop_caches min_free_kbytes overcommit_memory
extfrag_threshold min_slab_ratio overcommit_ratio
extra_free_kbytes min_unmapped_ratio page-cluster
[root@localhost vm]#
[root@localhost ipv4]# cd /proc/sys/net/ipv4
[root@localhost ipv4]# ls
cipso_cache_bucket_size neigh tcp_min_tso_segs
cipso_cache_enable ping_group_range tcp_moderate_rcvbuf
cipso_rbm_optfmt route tcp_mtu_probing
cipso_rbm_strictvalid rt_cache_rebuild_count tcp_no_metrics_save
conf tcp_abc tcp_orphan_retries
icmp_echo_ignore_all tcp_abort_on_overflow tcp_reordering
icmp_echo_ignore_broadcasts tcp_adv_win_scale tcp_retrans_collapse
icmp_errors_use_inbound_ifaddr tcp_allowed_congestion_control tcp_retries1
icmp_ignore_bogus_error_responses tcp_app_win tcp_retries2
icmp_ratelimit tcp_available_congestion_control tcp_rfc1337
icmp_ratemask tcp_base_mss tcp_rmem
igmp_max_memberships tcp_challenge_ack_limit tcp_sack
igmp_max_msf tcp_congestion_control tcp_slow_start_after_idle
inet_peer_gc_maxtime tcp_dma_copybreak tcp_stdurg
inet_peer_gc_mintime tcp_dsack tcp_synack_retries
inet_peer_maxttl tcp_ecn tcp_syncookies
inet_peer_minttl tcp_fack tcp_syn_retries
inet_peer_threshold tcp_fin_timeout tcp_thin_dupack
ip_default_ttl tcp_frto tcp_thin_linear_timeouts
ip_dynaddr tcp_frto_response tcp_timestamps
ip_forward tcp_invalid_ratelimit tcp_tso_win_divisor
ip_forward_use_pmtu tcp_keepalive_intvl tcp_tw_recycle
ipfrag_high_thresh tcp_keepalive_probes tcp_tw_reuse
ipfrag_low_thresh tcp_keepalive_time tcp_window_scaling
ipfrag_max_dist tcp_limit_output_bytes tcp_wmem
ipfrag_secret_interval tcp_low_latency tcp_workaround_signed_windows
ipfrag_time tcp_max_orphans udp_mem
ip_local_port_range tcp_max_ssthresh udp_rmem_min
ip_local_reserved_ports tcp_max_syn_backlog udp_wmem_min
ip_nonlocal_bind tcp_max_tw_buckets xfrm4_gc_thresh
ip_no_pmtu_disc tcp_mem
[root@localhost ipv4]#
tcp_max_tw_buckets: tcp max time wait buckets,,,,,,tw buckets 是用来保存我们当前系统上所有处于time wait状态的会话的(连接的)个数,,,,,,,, 保持原样不动,或只能调大,不能调小(调小意味着容纳的个数变小了,如果当前服务器上有大量需要断开的连接的话,而个数不够使用的话,,,,想断开的连接就不能断开了,,,因为是从established转为time wait的,tw如果占满了,则established就不能断开了,,,,即会话必须要保持连接了,意味着tw这个缓存值可能会溢出的,,,,,,,,这个值如果调大的,意味着更多的处于established状态的又需要断开的连接就可以断开了,,,,,,,,,,,,,,,,,tw不是断开了吗?为什么不能把此前的tw从操作系统剔掉呢?这样就可以腾出来了,,,,,,tw下一步要转化为什么状态?tw端是主动断开端,有tw1,tw2,发起第一个FIN包,就转化成为tw1,等待对方发送FIN包之前就就成了tw2,,,,,所以这是主动断开的一方所能够产生状态?????处在tw状态,接下来就等待,要么是等待对方的FIN包,要么是等待对方的确认包,,,很显然,tw是不能从系统删除的,对方没确认,是不能自己把tw关掉的,是不能自己清出去的,,,,除非超时,FIN的超时时间是很长的,所在在超时时间到达之前,你的tw状态的会话是绝不可能被清出去的,得始终持有着,只要tw不能被正常的断开,或超时,意味着tw_buckets当中就不能腾出来,这样其它的需要断开的连接就无法进入tw状态,,,,所以tcp_max_tw_buckets的值只能增大,,,,若减小会使你的系统上能同时持有的tw状态的连接的个数会更少,,,,,,,),,,,,,,,,,,,,,,,,
我们需要经常调整的几个关于网络的内存参数 tcp_mem,tcp_rmem,tcp_wmem
tcp_mem: tcp memory,,, tcp的套接字缓冲区大小,包括接收缓冲和发送缓冲
tcp_rmem: tcp receive memory 接收缓冲
tcp_wmem: tcp memory 发送缓冲,,,wmem中的w就是write就是send的意思
tcp_fin_timeout: tcp 的 fin 状态的超时时间
还有两个经常调整的,跟核心相关的,核心缓冲,在 /proc/sys/net/core 下面
[root@localhost ipv4]# cd ..
[root@localhost net]# cd core/
[root@localhost core]#
[root@localhost core]# pwd
/proc/sys/net/core
[root@localhost core]#
[root@localhost core]# ls
busy_poll message_cost rmem_default wmem_default xfrm_larval_drop
busy_read netdev_budget rmem_max wmem_max
default_qdisc netdev_max_backlog rps_sock_flow_entries xfrm_acq_expires
dev_weight netdev_rss_key somaxconn xfrm_aevent_etime
message_burst optmem_max warnings xfrm_aevent_rseqth
[root@localhost core]#
wmem_default: 发送缓冲默认值,,,wmem中的w就是write就是send的意思
wmem_max: 发送缓冲最大值,,,wmem中的w就是write就是send的意思
rmem_default: 接收缓冲默认值,,,rmem中的r就是receive的意思
rmem_max: 接收缓冲最大值,,,rmem中的r就是receive的意思
在内存的可调参数里面,还经常调IPC相亲的机制
IPC: 进程间通信,,三种形式:消息(message),共享内存(shared memory),信号(semaphores)
经常调的就两种: 消息(message),共享内存(shared memory)
message的:
msgmni: message max number idenfitifiers 最多多少个消息队列
msgmax: shared memory max 进程间通信的时候所能够使用的消息的最大上限,单个消息大小的上限,单位是字节
msgmnb: message max number of bytes,单个消息队列的最大字节数
shm的
shmmni: ( Specifies maximum number of shared memory IDs ) 指定最大数目的共享内存段,,,在全系统范围内最大允许使用多少个共享内存段,,,,,,指定最大数目的共享内存段,,,,,,,,,,,,这是经常调的参数,尤其是在数据库服务器上,4096有时可能显得有点少,,,,,系统级别,所允许使用的共享内存段上限:
shmmax: shared memory max 单个共享内存段的最大的大小上限 单个共享内存段的最大的大小上限,是字节为单位,,,,,,
shmall: shared memory all pages number 最大页面数 最大页面数,,,,,,全局范围内一次性可以使用的最大共享内存页面数,,,,,,,,,系统级别,能够为共享内存分配使用的最大页面数
性能监控的命令有哪些(不管是cpu的还是内存的):
sar,dstat,vmstat,mpstat,iostat,top,free,iotop,uptime,cat /proc/meminfo,ss,netstat,lsof,time,perf,strace
马哥要求我们自己学习了
评估io工作情况的
blktrace: block device traffic trace 块设备流量跟踪???
blkparse: parse block device traffic trace 分析块设备流量跟踪????分析trace的结果
btt: analyse block traffic trace 分析块设备跟踪???也是分析blktrace的结果,但与blkparse分析方式不一样
time命令 可以评估一个命令的运行时长,
[root@localhost ~]# time /bin/ls
anaconda-ks.cfg install.log install.log.syslog
real 0m0.002s #在cpu上的总时长
user 0m0.000s #花在用户空间的时长
sys 0m0.002s #花在系统空间的时长
[root@localhost ~]#
perf: Performance analysis tools for Linux,评估一个命令的执行 perfstat命令???? perftop命令?????
对文件系统进行压力测试的工具命令
dd????,iozone????,io-stress?????,fio?????
fio工具非常强大,了解它,能够全面了解linux内核i/o栈到底是怎么工作的,,这个命令讲清楚,要好久,,,,fio本身只有命令行的使用方式,而且展开的结果也只是文本的,不直观,,,,,据说fio的作者,大牛就是大牛,他是linux内核协议站一部分代码的维护者,(他是一个内核开发人员),前不久,为他的fio工具添加了一个图形化的界面前端,,,也即fio测试结果出来以后,直接用个图形展示给你,io量是多大,你这个设备支持多大的iops(每秒钟的io量,io事务的次数)????用图形化的方式直观的展示给我们,,,这个人此前是做内核开发的,不太懂图形开发,人家用了一段时间专门学了图形开发,然后加了这么一个工具.
如下图
其实我们的操作系统本身就是一个虚拟机,操作系统将我们的硬件(cpu,内存,各种io设备),,某一个进程要想在主机上(硬件上)运行,意味着它自己要去管理cpu的使用,内存的使用,各种io设备的使用,,,,,从哪儿读数据,数据输出的时候写到哪里去都要自我管理的,,,,,,,这太麻烦了(任何一个人开发一个应用程序都得这么写),也不便于让多个程序多个进程同时运行起来(大家都打架了,你这个程序看到的内存是全部,我这个程序看到的内存也是全部,你认为cpu是你专用的,我也认为cpu是我专用的),,,,,,,,,,,,,,,,,,,
如下图
所以出现了一个资源管理软件层(它什么也不干,无非是将我们的整个硬件资源做了一次抽象)(把所有的硬件资源的使用都虚拟成了OS: system call)所以每一个进程(应用程序)在运行的时候,它需要用到的内存,只要但凡与需要硬件打交道,都需要经过OS: system call 来完成,,,,,,所以我想用cpu,于是向内核(应该是OS吧,好像又不是????)发起调用,,,,内核中有一个系统调用是向进程分配cpu的,内核中有一个系统调用是向进程分配内存的,等等,,,,,,,所以只要说我们这个进程需要用到硬件资源,它只要向我们的内核发起系统调用即可,,,,,,,,,,这就是为什么strace命令追踪的时候发现进程运行的过程中不停地需要执行很多很多的系统调用的,,,,,,,,,所以要访问存储在磁盘上的文件,也必须要通过系统调用才能完成,,,,,,,,,因为只要跟硬件打交道,都必须要经过内核(或者叫Kernel的系统调用接口来完成),
如下图
,,,,,,,,,,由此我们的OS又实现了在OS层次上将多个不同的系统调用按照某种机制能够给它分配到不同的进程上去,从而使得我们的OS还是一个特殊的组件(进程监控器的组件 Process monitor,),,,,它能够监控着每一个进程的运行,让进程运行在OS之上,进程所能够看到的仅仅是OS(或者说OS分配给它的cpu时间片,内存存储空间,或者是某一个IO设备的使用),进程获得某一个IO设备使用的时候,意味着进程要占据着这个IO的总线,(因为进程控制着IO设备跟cpu通信,进程能够从里面读取数据,,意味着IO要跟我们的CPU之间和内存之间使用总线的,一般来说是跟内存进行交互的)
所以,OS就是一个进程监控器(监视器),由此OS负责协调每一个运行在其上的进程对其资源的申请,,,,,所以使得第一个进程在使用cpu的时候,第二个进程只能等待(假设只有一颗cpu的情况下),,,,,,,,,,好在cpu速度非常快,进程本身所需要的cpu时间非常短,因此对于很多用户看来,我们的多个进程是并行运行的,,,,事实上,如果只有一颗cpu,它们是无法并行的,,,它们看来好像是同步的,事实上它们是异步在cpu上逐个运行的,,,,,,,,,,同时,每一个进程运行的时候,都需要一段内存空间,而且这段内存是连续的??????从低端内存到地址到高端地址,高端地址是栈,,低端地址有代码段,数据段,bss段,堆段,,,,所以每一个进程所看到的地址空间是连接平坦(平滑)的,所有的地址空间它都可以使用,,,但是物理内存可能也没有那么多,而且也不会为每一个进程都能让它申请使用那么大的空间,,,,,,,,但是每一个进程虽然假设自己有很大的内存可以用,但是真正在运行的时候,压根就需要不了那么多,,,,,,,,,,,,,,,,,
如下图,
由我们的OS结合cpu提供了一种管理机制,将每一个进程可以使用的线性地址空间转化为物理地址空间,这种映射是按照页面的方式进行的,我们进程其实所使用的地址分为rss(常驻内存集,是这个进程启动起来以后必须要位于物理内存当中的,是绝对不可以被交换出去的,也不可以被清出去的),page cache(就是打开的文件,进程在运行过程中需要打开一些文件,这些文件通常保存在page cache当中,page cache可以交换到交换分区里面,也可以被清出去的,因为它压根就是缓存,没事再打开一次就可以了,,,,第一个进程打开了一个文件,占据了很大内存,第二个进程在使用内存的时候,内存空间不够用了,那么把第一个进程打开的文件通通清出去,而后第二个进程的运行,就有了内存空间了,,,,而后切换到第一个进程上去了,第一个进程发现自己打开的文件没了,产生缺页中断,缺页异常,,而后再重新再调回内存,,,所以page cache是可以被清出去的,只要不是脏页,都可以清出去,,,就算是脏页,使用pdflush刷写到磁盘上就可以了,,,),anon page(匿名页,就是我们进程运行过程当中所产生的一些中间数据,比如堆中的数据,通常有一部分是匿名页)
如下图
要想完成从虚线性地址(虚拟地址)到物理地址的转换,要借助于MMU,但是MMU的每一次转换,意味着每一次转换都要实现一级页目录的查找,二级页目录的查找,然后再实现平移?????,再最后计算,才能得到内存,这是非常麻烦的,为了加速这个过程,所以有了tlb,,,,,,,,,IO设备是怎么能够实现跟cpu交互的,cpu是怎么知道它哪一次是跟哪个IO设备打交道的,当前主机上有很多IO设备,而总线只有一个,处理机制也只有一个,而我们怎么能够实现让cpu知道在哪一个时刻是跟哪一个IO设备打交道的????IO设备在系统启动的时候,必须要向整个系统注册它所使用的IO端口,IO端口是连续的(一个IO设备可以使用一片连续的IO端口),端口一般也是从0-65535,,,, 如果某一个IO端口被某一个设备注册使用了,其它设备只能使用剩余的其它端口了,,,,,,,但是一般来讲IO端口这么多,是用不完的,,同时为了保证cpu在知道在哪一个时刻哪个IO设备上产生了什么事件,还要每一个IO设备在启动的时候,向cpu注册使用中断,必须要注册使用中断号以实现当这个IO设备上来了一个信号的时候,我们的cpu知道哪一个IO设备产生了信号,而且要通过这个IO端口跟这个IO打交道,,,,,,,,,IO端口是让cpu跟IO设备实现数据交换的,而中断是用来让IO设备通知cpu,它有一个紧急事件需要处理的,cpu利用可编程中断控制器(应该是一个硬件吧),让每一个IO设备注册使用一个中断线上的中断设备号的,一旦某一个IO设备来了一个事件(比如网卡,有人ping过来一个报文,这个报文是个电信号,如果不处理,立马会消失的,,,我们要把这个信号放在哪儿?要读下来,放在内存网络缓冲区当中)(如果是磁盘IO,要把它读进来放在磁盘IO的缓冲区)(所以说每个设备都有缓冲区的),,,一般来讲,如果某个IO设备产生中断了,意味着这个事件比当前进程的运行更紧急,,,意味着中断处理必须是优先的,,,,,,,,我们的系统在什么时候产生中断呢?可以允许中断呢?既然它很优先,说明随时都可以中断,,在任何一个时钟中断信号到来之前都可以中断,,,,而且时钟中断信号到来的时候还可以实现抢占...所以任何时候,当一个IO设备有了信号来的时候,它必须要通过某一种机制向我们的中断控制器发起一个中断请求,中断控制器产生(接收)请求之后,我们的cpu马上就意识到这个(中断控制器有针脚),这个针脚一旦有了电压,cpu马上就能意识到了,,,,它是通过电压来传信号的,,,,,,,cpu一旦意识到某个中断设备有中断了,于是在内核的这一次处理之前(cpu一旦产生中断,cpu通知内核,告诉内核,有中断产生,内核必须要完成模式切换,此时可能正在运行另一个进程,这个进程被切换出去,内核上cpu上工作,,,然后内核指挥着cpu将信号读进内存缓冲区里面来)
如下图
因此中断产生的时候,我们的cpu第一时间能意识到,而且要通知内核的,,内核意识到cpu产生中断,内核必须要将进程切换出去,由内核来运行,而后指挥着cpu将中断那个设备上的数据取出来放到内存里,,,,但是如果这个设备是磁盘设备的话,如果我们需要从磁盘设备上读很多数据进来,,,,我们的cpu必须要一个一个的指挥着,,这就很慢了,我们的cpu会将大量的时间,时钟周期消耗在磁盘的数据交换上了,,,,,,,,,为了避免这种情况,cpu可以在内存中开辟一段空间来存储这段数据,而后cpu可以把这个数据的控制权(总线的使用权)交给这个设备的DMA,由DMA负责取数据,数据取完后放到内存里面了,,,,,,数据取完之后,放到内存里面了,然后DMA告诉cpu,数据取完了,cpu再回过头来处理数据,(DMA怎么告诉cpu,数据取完了? DMA又发送一个中断给cpu)(当事件结束了,一个硬件,外部的硬件,要跟cpu打交道,必须得通过中断来完成)
这背后的处理机制很复杂,包括IO设备也很复杂,cpu必须要知道每一个IO设备它的中断(中断号????)是什么,它的IO端口是什么,然后才能将这些IO设备跟当前整个系统关联起来,,并且在必要的时候,将这个IO设备附加在进程上的,为什么要附加到进程上?如果当前把焦点切换到文本编辑器, 敲的字到文本编辑器里了,,,把焦点切换到ppt编辑器,焦点就到ppt编辑器里面,,,,所以我们的内核必须要负责实时将我们的必要的IO设备关联到某一个正在运行的进程上,
如下图
我们可以这样理解,站在任何一个操作系统(内核)的角度上来讲,内核都认为自己是可以使用所有硬件的,因为它负责管理所有硬件,因此,是全量的cpu时间片,
第二,内核看到的内存,是连续的全部的内存空间,,这个内存空间(指的是物理内存),从0x0000,到最大空间结束,,, 它一定会认为自己是从0开始的那一段内存,是自己可以使用的,,,,,,只不过0x0000前面有一部分(1M)要给bios,有16M要给dma,,,(马哥这里指的是32位系统),,,,(如果是64位的系统,有1G要给dma),,,虽然是给dma,但并不是说内核不能使用
第三,内核看到的i/o,是全部可用的i/o,,,,,,,,,,,而且是可以直接管理使用io的,,,,而且每一个i/o设备还应该关联当前内核的所使用的硬件cpu上的某一个中断上,而且是某些个io端口上,
如下图
以物理内存而言,我们可以把物理内存地址空间划分为两段,真正的物理内存的地址叫PA(physical Address),而每个进程所看到的地址是线性地址(虚拟地址VA virtual address),,
那什么是虚拟化?
现在我们很多公司为了提供服务,它都得需要用到一台服务器,但是人们发觉到运行服务所需要用到的系统资源很少,但是他又期望这些服务对操作系统本身都是各自独立运行的,都运行在一台服务器上面,进程过多,管理起来不便,可能根据业务或者各种需求,我们需要运行多个操作系统,,,,,,,,如果买多台物理机,每个上面运行一个操作系统的话,首先,多个物理机要有地方存放,其次,多个物理机耗电量很大,第三,管理有很大不方便的,,,,,,,,,,,如果买一个性能稍好的主机,用电量少,只需要管理这一台物理机就可以了,,但是我们可以在一台物理机上运行多个各自独立运行的操作系统,,这个我们就称为虚拟化,将一个物理硬件虚拟成多个虚拟硬件平台,,,,(若装上os,只能运行一个操作系统),,,,我们这里的虚拟化,是真正意义上的虚拟物理硬件的技术,因此我们的操作系统变成这样子的了,
如下图
因此我们的操作系统变成这样子的了,在我们的OS之上????,OS不是直接运行在硬件上的了??????我们的vmware事实上还是一个OS??????vmware软件上运行了一堆OS,,,,,,,,,在OS之上,运行了很多进程,vmware只是其中的一个进程,在vmware之上,我们又运行了很多的虚拟机(OS),,,,,,,,,,,,,vmware是一个软件,这个软件是用来干什么的?模拟出来了一堆硬件设备,而且这一堆硬件设备,每一个都是一个独立的平台,所以我们可以在这些硬件上可以装额外的操作系统,,,,,这种技术会面临哪些问题?
如下图
每一个OS期望的是1)它使用的是全量的cpu时间片,我们的cpu时间片还得分给其它进程,我们的vmware只能获得一部分时间片,vmware之上的各个虚拟机(OS)获得的就更少了,,,所以每个虚拟机客观上获得的仅仅是很小一部片的时间片,,,,2)每一个操作系统运行起来之后,它的运行空间都分为两种模式,内核模式和用户模式,,,,意味着一运行虚拟机就有三个模式了,最底下OS的分为内核模式和用户模式,,,,,在用户模式运行的这个虚拟机里面,又有了内核模式和用户模式,在虚拟机里面所运行的进程,每一个进程所看到的地址空间又是连续的线性地址空间,但是虚拟机的os内核所看到的物理内存地址空间是什么空间?如果第一个虚拟机可以直接使用物理内存,第二个虚拟机也可以直接使用物理内存,,,因为第一个虚拟机意识不到第二个虚拟机的存在,第二个虚拟机意识不到第一个虚拟机的存在,所以两个虚拟机一交叉访问了物理内存,肯定是资源争用崩溃的,,,,(每个人只要发现自己没用的,都会认为是空的,它就用了这段内存,事实上别人在用了,所以会覆盖,所以导致结果肯定是崩溃的),,,因此是不能这样使用的,
如下图
事实上vmware程序本身所拿到的物理内存就已经是底下这个OS所虚拟出来的了,,,,底下的OS是怎么使用内存的?OS自己要留一部分给自己的内核使用,将另外一部分给进程,,,这就意味着我们的底下的OS将低地址空间从0开始的那一段,(在分了dma之后),留了一部分给自己内核使用了,这就意味着,底下的OS分配物理内存的时候分给每一个进程的时候,就已经是高地址空间了(非0的那一段地址空间),,,,,,,,,虚拟机的OS所看到的物理内存一定不是从0开始的,,,,很显然问题又出现了,每一个内核都必须使用从0开始的地址空间,因为这样好分配地址,,,,,,,,,这样子的话不是从0开始,内存又出现问题了,
如下图
每一个进程运行的时候,看下虚拟机进程运行,这个进程运行的时候,它使用的是虚拟地址空间,虚拟地址运行时候的转换成(通过MMU转换成)物理地址(转换成哪个物理地址?是虚拟机的物理地址,还是事实上的物理地址)(我们把虚拟机称为GuestOS)(把底下OS+vmware称为hypervisor,即资源监控器,,,hypervisor这里指的是vmware,是监控虚拟机运行的一个软件,只不过vmware结合我们vmware底层的内核结合在一起,称为hypervisor,这是一种特殊模型,等会儿马哥讲虚拟机不同的虚拟化模型,就更容易理解这个概念了,马哥先以我们熟悉的方式来解释),,,,,,,很显然GuestOS当中的某一个进程,它实现VA向PA转换的时候,转换的结果一定是这个整个的GuestOS内核所能够看到的物理地址,但最终数据是在真正的物理内存里在放着,而GuestOS的转换结果不能映射到物理内存上面去,还需要经过hypervisor进行转换(还需要我们的底层真正的OS来完成一次转换),,,,,,我们转换两次,性能会更差,(第一次转换cpu的MMU?????第二次转换cpu的MMU)
如下图
还有我们的IO设备只有一个(键盘,鼠标只有一个),我们现在有很多的虚拟机,(就不用提进程了,因为IO设备要关联到进程上去的??????),我们有那么多的GuestOS,我们这些GuestOS怎么知道自己使用的是哪一个IO设备,怎么能够给GuestOS提供IO设备,每个GuestOS起码拥有的IO设备有(键盘,鼠标可以没有,只要不交互,就没问题,,硬盘要有,访问网络网卡要有,,),,,,假设我们的真实的物理硬盘只有一块,怎么实现给不同的虚拟机分配不同的硬盘? cpu的虚拟化(通过分cpu的时间片来实现的),内存虚拟化(无非再虚拟一层,将离散的空间通过hypervisor给它整合成连续的分配给每一个GuestOS),,,,IO设备怎么虚拟化?真实的物理硬盘只有一块,怎么虚拟化?分区?分区本来就是已经被底层的OS使用过了,能把底层的分区直接映射成一个硬盘给GuestOS吗?(拿出一段分区,告诉GuestOS,这是一个硬盘,拿过去再去分区使用?好像是不可以的),,,,,还有网卡只有一个,怎么让网卡进行虚拟化呢?其实IO设备容易实现虚拟化,比内存更容易虚拟化,,,,,,,,,,,就以网卡为例,网卡本来就是向外发报文的,而且每一个ip报文本身就是独立的,我们无非就是把来自于每一个虚拟机的ip报文封装以来送给我们真正的OS通过网卡往外送,但是有个问题,物理网卡只有一个,当一个报文发过来之后,这两个虚拟机要不要接受?(在物理网卡上, 在以太网的物理网络里面,实现通信靠的是mac地址,所以当一个报文发过来之后,目标是GuestOS?还是真实物理机的的mac地址(应该是真实的物理机的mac地址吧)?????,报文是底层主机的某个进程接收呢?还是虚拟机的某个进程收呢?),,,,,,,,,,物理网卡只有一个,mac地址也只有一个,改虚拟机的mac地址?,,,,,,,我们事实上是改虚拟机的mac地址的,虚拟机的mac地址放在哪里的呢?mac地址是放在硬件上的,,虚拟机的mac地址放在虚拟机的网卡上??虚拟机的网卡与物理机的网卡有什么关系??(虚拟机也能看到cpu,它看到的cpu是各个cpu时间片拼凑起来的,,,,虚拟机也能看到物理内存,是各个不连续的物理内存拼凑起来的,,,,,,,,,,,,看到的网卡,是虚拟网卡,问题是怎么虚拟的)(cpu能划分,内存能划分,但是网卡是不能切几块放进去的,,怎么办?在桥接模型下,物理网卡真正面向外部的时候,它的mac地址只有一个,如果两个GuestOS,跟物理网卡使用的是不同的mac地址,当其它的主机发报文来的时候,如果它的目标mac不是这个物理的mac地址,是不会接收的,那么这两个虚拟机是拿不到信号的)(还有,我们的物理硬盘只有一个,每一个GuestOS期望自己看到的一定是某个硬盘设备,至少应该看到有一个硬盘,而且要对这个硬盘分区格式化,然后在里面装操作系统,,,,就算现在可以运行没有硬盘的操作系统,比如PE这样的操作系统,它也是把内存当作硬盘来用的????,它要有一个硬盘设备,而且我们要长长久久使用一个系统的话,必须得有一个硬盘(持久性存储))(硬盘怎么虚拟化?不管怎么讲,你的虚拟机存的数据最终是在硬盘上的,,而硬盘的IO只有一个,它能够关联的总线也只有一个,它的驱动,它的控制器也只有一个,你如果让每一个虚拟机都能够使用硬盘的控制器的话又要乱套了,,,,,,怎么办?怎么让硬盘虚拟给它? )
(其实大多部的IO设备,一般来讲,最简单的使用方式就是模拟,用软件的方式在vmware上模拟一个网卡(假网卡),模拟一个硬盘(假硬盘),假硬盘最终存储的数据是要存到硬盘上去,,怎么建立关联关系?在真硬盘上建一个本地回环镜像文件)(我们使用dd命令?????创建一个swap设备,还可以格式化,格式化以后还可以当swap分区来用,这就是本地回环设备?????,所以我们可以在物理硬盘上找某一个分区创建一个文件,让这个文件跟虚拟的硬盘建立关联关系,,,,,所以在Guest操作系统看来,这是一个硬盘,但这个硬盘是个假的,,它是我们真正物理硬盘上的一个文件而已,通过这种方式来模拟,,,,很显然在真正物理硬盘上无非建多个这样的文件,就是每一个GuestOS就可以有一块硬盘了,它往这块虚拟的硬盘上存数据的时候,这个数据最终要通过真正的操作系统的IO存到某个物理硬盘上的文件)
如下图
这个虚拟的硬盘,没有我们真正的硬盘性能好,因为中间已经切换过一次了,所以IO可能经过2次?????而多个GuestOS彼此之间还要共享同一块硬盘,,,,,,,,,,,,,如果我们想GuestOS的IO的性能要好一点,我们可以额外的找一块共享存储,把它做成iscii的设备,然后把各个GuestOS当作iscii的客户端,每一个GuestOS直接关联一个iscii,直接使用这个物理设备iscsii,,,,所以我们可以让虚拟机使用一个文件(真实物理硬盘上的文件,)装操作系统,(只装操作系统,不做任何的数据存储),真正存数据的时候,是将数据存在虚拟机所关联的iscii设备上来(这个iscsii本身是个物理设备,性能会好很多,只要网络带宽足够大,它就足够使用了),,,所以这就是我们虚拟化里面的我们可能经常提到的卷的概念,这里的iscii被称为卷的,
如下图
网卡如何虚拟,无非就是给每一个虚拟机用软件的方式模拟一块网卡,,当虚拟机真正向外发报文的时候,(如果两个GuestOS之间发报文,在我们的真实的主机内部通过进程间通信就可以搞定了)(如果GuestOS需要跟外部网络主机通信,)(如果GuestOS用不到跟外部网络通信,用不到跟物理机通信,就两个GuestOS之间通信,我们无非是模拟出来一块虚拟网卡给任何一台虚拟机,然后它们俩之间通过虚拟网卡通信,直接借助于我们的OS的进程间通信就能完成,此时无论使用什么mac地址都无所谓,,,,,,我们的vmware里面有虚拟通道,但是如果说它们需要跟外部网络通信的话,,,,,)(vmware提供的我们能够跟外部网络通信的方式有几种?桥接,NAT,但Host-only是不行的,Host-only意味着只能跟我们的物理机通信,跟物理机之外的其它主机是通不了信的,)(NAT的做法:把真正的物理机的操作系统当作网关,当作前端服务器,这个前端服务器通过地址转换将里面的每一个GuestOS的地址先转换成前端服务器的地址,然后用来通信,,,有点类似于把真正的物理机,两个GuestOS组成一个网络,)
如下图
有点类似于把真正的物理机,两个GuestOS组成一个网络,两个虚拟机需要跟外部网络通信的时候,把请求通过网关发给物理机,由物理机通过地址转换再送给外部网络,外部网络真正发现看到的源地址是物理机的地址,所以它回给物理机,由物理机自己内部的NAT将它再转换给某一个GuestOS,,,这就是NAT模型,在NAT模型下,外部的其它主机通过网络是看不到我们的虚拟机的,这就是源地址转换
如下图
桥接,意味着,把这两个虚拟网卡绑定在物理网卡设备上,并且让这个物理网卡运行在混杂模式(无论目标的物理mac地址是不是它,它都要接受的,)一旦接受下信号后,它发现内部所关联的虚拟网卡有可能是某个GuestOS的mac地址,所以报文就被通过物理网卡转给虚拟机了,,,,在二层?????就转给虚拟机了,而NAT是在三层?????转换虚拟机的,,,,,,桥接这个模型,它指的是2层就直接转给虚拟机,,,它实现的是类似于2层代理机制
如下图
物理网卡就是个前端网卡,它跟任何任何机器都没有关系(与物理机,两个虚拟机都没有关系),,,,,,,,,,也可以这样理解,物理机也有个虚拟网卡,这个虚拟网卡也有自己的mac地址,而我们真正的物理网卡是没有mac地址了,,,,,,,,,,所以一旦目标mac是物理机的,(是我们真正的物理网卡),就由物理机来收,,,,,,,,,如果目标mac是虚拟机的,就由虚拟机来收,,,,所以这个时候称为一个桥,这个物理网卡类似于交换机,一个入口进来了,三个出口,(一个到物理机上,还有两个到虚拟机上)(因此发进来的任何报文,到物理网卡这儿来,只要目标地址是当前主机上的这几个虚拟网卡,它都能够转发,,,,,,类似于桥接吧,,,)(桥接指的就是网桥,网桥就是交换机,所以物理网卡模拟的就是一个交换机,)
如下图
我们的x86系列的cpu,它的整个运行是分成特权级的,一共有4个级,环0(特权环 privileged ring, ring 0 ),环1( ring 1 ),环2( ring 2 ),环3( ring 3 ),,,,,,,,一般情况下,特权环是能够运行敏感指令的,什么叫system call(系统调用)?(比如一个进程运行的时候,当我们的内核调度一个进程到cpu上运行的时候,它要用到我们的cpu的某些运行能力,,,,,如果说这个进程获得了cpu的使用权,它可以让这个cpu去干坏事了,比如说指挥这个cpu让其它人的内存都去清一清,将整个机器重启一下,就会乱套了,,,,所以我们的cpu不能无所谓的什么指令都给你运行)(我们每一个厂商所生产的cpu,支持n多汇编集指令的,有些指令是敏感的指令,还有特权指令),,,,,,,,,,,,,,,我们的指令有普通指令和特权指令的概念,,,,,,,,,,,,,,我们的进程在运行的时候,只能让它运行普通指令,要想执行特权指令,(如指挥cpu去访问硬盘了,加载硬盘的数据到内存,指挥cpu去访问内存了,去读取内存中的数据?难道也要系统调用吗?????)就得系统调用,此时进程退出去,由内核来指挥着 ring 0 的运行,,,,,,,,,,,,,,,所以cpu上面的运行的命令,最终每个进程的运行,无非就是将进程中的那些代码转换为cpu可以运行的指令而已,而这些指令分为 普通指令和特权指令(有一部分是敏感指令),,,马哥又说敏感指令不是特权指令,但是有可能会触发特权指令的执行,或者有可能会影响到其它的进程运行的指令,,,,,,英特尔或AMD的cpu,或者X_86的cpu,在这方面有点模糊不清,,也就说某些指令是敏感指令,你说它是特权指令呢,它也不是特权指令,你说它是普通指令呢,它也不是普通指令,,,,,,所以我们的操作系统内核把特权指令和敏感指令都自己来管理了,而事实上有些敏感指令是直接做在ring 3当中了,,,,,,,,如果我们只有一个操作系统,当某个进程试图运行敏感指令的时候,它最终还是要转换成特权指令才能运行起来,,,,,,,也就意味着我们的内核最终是可以捕获这个敏感指令的,,,,,,但在虚拟化当中就不一定了
如下图
在虚拟机当中,GuestOS的内核是虚拟机的内核,虚拟机的内核捕获了这个敏感指令直接在虚拟机的cpu上运行了,这个敏感指令有可能会触发到其它的影响整个操作系统的工作,意味着物理主机,其它的GuestOS的安全性稳定性得不到保证了????所以X_86,它的某些敏感指令是无法实现虚拟的,,为什么呢?
如下图
所以X_86,它的某些敏感指令是无法实现虚拟的,,为什么呢?GuestOS上当某个进程需要执行敏感指令,是要提交给GuestOS内核,但是我们的GuestOS内核是不能运行敏感指令的,(当GuestOS的内核去运行敏感指令的时候,我们真正的物理机的内核必须要能够知道虚拟的内核在试图执行敏感指令(特权指令),,,我们不能让GuestOS的内核去运行敏感指令(特权指令))(如果 GuestOS 的内核能运行特权指令(敏感指令),就能指挥着我们的cpu去重启整个系统,整个物理主机及其它的虚拟主机的稳定性就不能保证了),,,,因此这几个环当中
如下图
因此这几个环当中,不能让虚拟机的内核直接运行在环0上,也就是我们的虚拟机的内核只能运行在环3上,(对于x_86 系统的OS来讲,环1,环2都没用,,,,,,,,,只指定了环0和环3),,,,,,,,,,,,,,,如果虚拟机的内核只能运行在环3上,显然是不合适的,因为我们的每一个内核都认为自己运行在环0上,但是你却非得让人家运行在环3上,,,怎么能够让它以为自己运行在环0上,又得模拟,告诉它你的确在环0上,但是事实上不在环0上,它还是在环3上,,,,,,因此,当虚拟机的内核试图运行敏感指令的时候,最终还得由我们的真正运行内核的那个物理机来负责实施,,,,,,,,,那就意味着虚拟机的一个指令,首先从虚拟机的进程通过系统调用到了虚拟机内核,虚拟机内核还得再转换一次到真正的宿主机(Host OS)内核,要转换两次,,,,,,这个指令要连续转换两次,而且还要让虚拟机内核认为它自己能够运行这个指令,,,怎么能够让GuestOS内核认为自己是运行在环0上的?,,,,,,,,,,,,,,,,,环0是个怎么环0?无非就是运行特权指令而已,就是一堆特权指令集而已,,,,,,,,所以我就告诉这个虚拟机内核,你有特权指令(一个,二个,三个都已经放给你了),你能够运行,所以GuestOS的内核就认为自己可以运行在环0上了,但是我们是不能让GuestOS的内核运行特权指令的,,,所以一旦我们的HostOS(底层的物理机)的内核发现虚拟机的内核试图运行这几个虚拟的特权指令的时候,HostOS立马自己站了出来,将虚拟机试图运行的给它转换到特权环上运行了,,,但是HostOS并非让虚拟机内核当中每一个特权指令都能够真正运行的,(比如虚拟机发起了一个重启系统的指令或关机的指令,是不能把整个cpu上电源切断的,因为真实的物理机HostOS和其它虚拟机还得运行,,,我们要关掉的仅仅是自己这个虚拟机本身而已,,,,所以特权指令并非是真正运行的,,,,这就是为什么要把特权指令虚拟出来的原因(我只是通知给你这样一个虚拟的主机,把仅仅这个虚拟机的电源切断就是了,事实上并没有切断整个机器的电源)),,,,,,,,所以说特权指令也需要虚拟,不然的话,就无法保证整个系统的其它虚拟机的安全性,,,,,,,,,,,,,所以必须要保证各虚拟机之间是隔离的,,,,,,,,,什么时候才能真正实现整个系统的关机(切断整个cpu电源)?只有在HostOS关机的情况下才能实现,,,,HostOS才是真正意义上的特权阶层
所以我们要想实现cpu的虚拟,面临的挑站,有如下几个
1.1 x86平台实现虚拟化技术的挑战
x86处理器有4个特权级别,Ring 0 ~ Ring 3,只有运行在Ring 0 ~ 2 级时,处理器才可以访问特权资源或执行特权指令;运行在 Ring 0级时,处理器可以运行所有的特权指令。x86平台上的操作系统一般只使用Ring 0和Ring 3这两个级别,其中,操作系统运行在Ring 0级,用户进程运行在 Ring 3 级。
1.1.1 特权级压缩(ring compression);;特权级要实行压缩
为了满足上面所述的需求,VMM ( virtual machine monitor 虚拟机监控器,就是 hypervisor ) (hypervisor+物理机操作系统内核)自身必须运行在Ring 0级,同时为了避免各GuestOS控制系统资源,各GuestOS不得不降低自身的运行级别而运行于Ring 3(Ring 1、2 不使用)。因为虚拟机面临着特权级不够使用的这样一种尴尬的局面
此外,VMM (就是 hypervisor,可以理解为HostOS)使用分页??????或段限制?????的方式保护物理内存的访问,但是64位模式下段限制不起作用,而分页又不区分Ring 0,1,2。为了统一和简化VMM的设计,GuestOS只能和用户进程一样运行在 Ring 3。VMM必须监视GuestOS对GDT、IDT (GDT、IDT指的是某些敏感的寄存器,比如指令寄存器等)等特权资源的设置,防止GuestOS运行在Ring 0级,同时又要保护降级后的GuestOS不受Guest进程的主动攻击或无意破坏?????。
1.1.2 特权级别名(Ring Alias)
设计上的原因,操作系统假设自己运行于ring 0,然而虚拟化环境中的GuestOS实际上运行于Ring 1或Ring 3 ( 不能运行在ring 0 上 ) (所以只好给它起个别名,给它弄一堆假的特权指令集,而且告诉它,这就是 ring 0 ),由此,VMM必须保证各GuestOS不能得知其正运行于虚拟机中这一事实,以免其打破前面的“等价执行”标准。例如,x86处理器的特权级别存放在CS代码段寄存器内,GuestOS却可以使用非特权PUSH指令将CS寄存器压栈,然后POP出来检查该值;又如,GuestOS在低特权级别时读取特权寄存器GDT、LDT、IDT和TR时并不发生异常。这些行为都不同于GuestOS的正常期望。
1.1.3 地址空间压缩(Address Space Compression)
地址空间压缩是指VMM必须在GuestOS的地址空间中保留一段供自己使用(VMM自己要用到内存,所以VMM,即hypervisor从0物理地址开始了,所以各虚拟机看到的物理地址一定不是从0开始的,),这是x86虚拟化技术面临的另一个挑战。VMM可以完全运行于自有的地址空间,也可以部分地运行于GuestOS的地址空间。前一种方式,需在VMM模式与GuestOS模式之间切换,这会带来较大的开销???????;此外,尽管运行于自己的地址空间,VMM仍需要在GuestOS的地址空间保留出一部分来保存控制结构,如IDT和GDT。无论是哪一种方式,VMM必须保证自己用到地址空间不会受到GuestOS的访问或修改。
1.1.4 非特权敏感指令;;;;;; 有些敏感指令并不属于特权指令,所以VMM无法捕获,
x86使用的敏感指令并不完全隶属于特权指令集,VMM将无法正确捕获此类指令并作出处理。例如,非特权指令SMSW在寄存器中存储的机器状态就能够被GuestOS所读取,这违反了经典虚拟化理论的要求。
1.1.5 静默特权失败(Silent Privilege Failures);;;;;;;;某些特权指令在失败时并不返回错误,这样一来,我们的虚拟机将无法从错误执行的结果中获取相关信息,并指挥虚拟机自己运行的
x86的某些特权指令在失败时并不返回错误,因此,其错误将无法被VMM捕获,这将导致其违反经典虚拟化信条中的“等价执行”法则???????。
1.1.6 中断虚拟化(Interrupt Virtualization);;;;;;;一旦产生中断,一旦有外部IO设备要触发cpu的使用了,中断是由谁来处理?(假设某一个网卡来了一个报文,现在需要中断,中断信息是要通知给cpu的,中断必须由HostOS来处理,而不能由某一个虚拟机来处理,,,,这样一来HostOS就非常繁忙了,,,而且GuestOS对特权资源的每次访问都会触发处理器异常,这必然会频繁屏蔽或启用中断 )
虚拟化环境中,屏蔽中断及非屏蔽中断的管理都应该由VMM进行;然而,GuestOS对特权资源的每次访问都会触发处理器异常,这必然会频繁屏蔽或启用中断,如果这些请求均由VMM处理,势必会极大影响整体系统性能。
1.2 X86平台虚拟化
完整意义上的计算机由硬件平台和软件平台共同组成。根据计算机体系结构理论,其硬件平台包括CPU、内存和各种I/O设备;而软件平台则包括BIOS、操作系统、运行时库及各种应用程序等。对于主机虚拟化技术来讲,其主要负责虚拟硬件平台及BIOS,而操作系统、运行时库及各种应用程序可以使用以往在物理平台上各种现有技术及产品。
如下图
1974年,Popek和Goldberg在一篇论文中定义了“经典虚拟化(Classical virtualization)”的基本需求,他们认为,一款真正意义上的VMM至少要符合三个方面的标准:
◇ 等价执行(Equivalient execution):除了资源的可用性及时间上的不同之外,程序在虚拟化环境中及真实环境中的执行是完全相同的;
◇ 性能(Performance):指令集中的大部分指令要能够直接运行于CPU上;#只有哪些特权指令不能直接运行
◇ 安全(Safety):VMM要能够完全控制系统资源;#不能因为某一个虚拟机的内核执行了一个指令,导致整个系统重启,(影响了其它虚拟机的运行),,,,,所以各虚拟机之间必须要完全实现隔离,并且任何一个虚拟机要想执行特权指令,我们的HostOS必须要能够提前捕获,并且能够对其做出处理,,,,,,,,,,,,,任何一个虚拟机都不能够越过HostOS对我们的整个物理硬件发出任何特权的控制指令,也就是说我们的HostOS能监控每一个虚拟机所执行的每一个特权指令,并且判定它到底能不能执行
任何一个进程,假设在虚拟机中运行提供web服务, 和在物理机中运行提供web服务,就服务本身来讲,是没有区别的(无论网卡怎么处理,中断怎么处理,跟这个web服务是没有关系的),,,,,,所以这叫等价执行
cpu虚拟化,是非常麻烦的,现在我们的cpu引入了所谓的硬件虚拟化技术,
英特尔的叫 VT-x,在硬件级别直接加了特权 ring -1 级别,
AMD的叫AMD-v( AMD virtual),也是在硬件级别直接加了特权 ring -1 级别吧
如下图
从而我们有了特权ring -1,这样一来, 我们就有了5个环,GuestOS运行在环ring 0上,而HostOS运行在环ring -1上,在ring -1环上,放的是所有的特权指令,而在环ring 0上,放的是我们的每一个GuestOS可以运行的一些指令,但是这些指令不至于影响到其它的虚拟机的,,,,所以任何时候,事实上环ring 0上是没有什么指令的,只不过是留了一个空闲的环放在这儿了,所以当任何时候,GuestOS试图运行环ring 0上的指令的时候,我们的环ring -1就会被触发,而后由我们的真正的HostOS来负责执行这个指令,(转换并翻译这个指令去运行),,,,,,,,这就叫硬件虚拟化(指令集的虚拟化,或者叫cpu的硬件虚拟化)
如下图
内存的虚拟化,GuestOS将地址从VA(虚拟地址)转化成了PA(物理地址),,,,,,,这个PA指的是GuestOS内核的PA,但是最终还要转化为HA(主机地址),,,所以要转换两次,这太麻烦了,,,,,那怎么办?
如下图
所以要转换两次,这太麻烦了,,,,,,那怎么办?内存虚拟化是怎么实现的呢???本来我们没有虚拟化的时候,地址通过MMU从VA到PA(此时PA是真正的物理地址,类似于后面有虚拟化时的HA),,,,,,,,,,,,,而有虚拟化的时候,OS首先要从VA转换到PA(虚拟机的物理地址),最终还要转化成HA(底层物理机的物理地址),假设HA中的两段不连接的地址合并成一段连接的地址给虚拟机使用了,虚拟机看到的这两段地址是连接的地址(它们被合并成连续的了,事实上在底层的物理地址上是不连续的)
如下图
内存地址,连续转换两次太麻烦了,因此我们的英特尔的cpu(或者AMD的cpu)又实现了MMU的虚拟化,(影子MMU)(shadow MMU)? 影子MMU是如何工作的?当某一个GuestOS试图将它的VA地址转换到PA上的时候(最终是要转换到HA上去的),,,所以它用了两个MMU,虚拟机的MMU转换(从VA转换为PA),真正的MMU转换(从VA到HA,,,我们的物理HostOS直接通过直正的MMU将VA转换成了HA即最底层的物理地址,,,,)(这个HA即最底层的物理地址与虚拟机的物理PA不是一码事,但是最终的HA即最底层的物理地址就是虚拟机的物理地址PA再次映射的地址,,,,,,HA是真正的地址,一步到位了,不需要二次转换了),,,,,,,,,,所以虚拟机自身转换的结果物理地址PA没有意义了,但是又必须转换,因为不转换的话,虚拟机操作系统就无法运行了,,,它必须要依赖于MMU转换的,因为我们不能让我们的虚拟机认为它自己是运行在虚拟机当中的,所以内存虚拟化是通过这种方式来完成的,,即影子MMU
如下图
Intel和AMD分别通过EPT(Extended Page Tables 扩展页表)和NPT(Nested Page Tables 嵌套页表)来提供MMU,
为虛拟化应用提升影子MMU的性能,并通过标记(tagged)TLB来避免虚拟机切换时频繁清写(flush)TLB以提高TLB缓存的命中率。
如下图
我们最终的地址转换结果是要保存在TLB当中的,要将MMU转换的结果保存在TLB当中的,TLB中保存的是MMU的转换结果(缓存的转换结果),如果是虚拟机1转换的时候,虚拟机1的MMU转换结果保存在TLB当中了,,,下一次,我们的内核切换到虚拟机2去执行了,虚拟机2也要用到MMU,虚拟机2也要用到TLB,,,,,MMU是cpu的芯片,TLB也是cpu的芯片,,,,,,很显然,对于虚拟机2来讲,这个TLB的缓存结果,不但没有意义,而且会带来混淆的,因为我们直接用这个TLB的缓存结果,导致它得到的地址一转换就成了虚拟机1的地址了,所以TLB不能用了,,,,,,那就意味着一旦我们当前cpu从运行虚拟机1转换到了运行虚拟机2,这个TLB要清空的(不清空的话,虚拟机2会误用虚拟机1的TLB的缓存结果的),,,清空的话,意味着我们要重写TLB,,,,过一会儿,它又切换到虚拟机1上面来了,又要清空TLB的缓存结果了(因为此时缓存的是虚拟机2的结果),又得为虚拟机1重新TLB的结果,,,,,来回的对TLB清来清去的,所以TLB就没有什么意义了,TLB本来的加速功能就不能起到效果了,,,,一个虚拟机一个TLB???不行,因为TLB是个硬件,MMU只是虚拟出来一个(大家共同使用的虚拟的,仅仅这一个),所以TLB不可能给每个虚拟机分一个的,况且也不知道我们的电脑上将有多少个虚拟机,也没法分,,,,,,,,,,,,,,,,那怎么办?我们以后在验证这个虚似中(这个TLB的缓存当中)所缓存的条目是不是供当前的虚拟机使用的时候,给它多加一个键,(虚拟机查找的时候????,根据键来查找值的,,,,,,, 以前的键是虚拟地址来查找物理地址的,,,,以后再多加一个键标识符,这个新键叫虚拟机编号)(所以每一个虚拟机再来查找TLB的时候,先查找自己的编号有没有,有的话再查找编号下的键,,,,,,,,,而这个编号我们称为Tagged(标记,标签)),,,,,每一个虚拟机都有自己的tagged,有了tagged这种技术以后,可以避免TLB在虚拟机切换时频繁的清写TLB了,从而能够提高TLB的命中率的,,,,这是新的英特尔和AMD才引进的技术,,,,以前没有这种技术,没有的话,只能频繁的清写
如下图
再讲讲,IO设备的虚拟化的情况,在实现虚拟化IO的时候,我们虚拟机1(GuestOS1),要想实现通过网卡向外发一个报文,这个报文中间要通过虚拟机监控器先发给Host的内核,通过Host内核的协议站再发给真正的物理网卡,,,,,,,,,,因为能够使用物理网卡的只有我们真正的HostOS,这样子的话性能会很差,,,我们要想提高它的IO性能的话,该怎么办?
如下图
我们为了提高虚拟化的能力,我们让底层的OS将网卡(硬件设备的驱动程序,驱动程序的能力)直接通过某个系统调用(本来驱动程序是没有必要让进程直接使用的,因为只有内核自己才有必要用驱动跟硬件打交道,,,,所以这个驱动程序没有必要让进程访问,,,所以一般我们不会把驱动程序做成系统调用的,,,,,,,,但是现在我们可以把驱动程序做成系统调用),,,,,,而后由虚拟机的内核直接跟这个系统调用打交道,这样一来,这个虚拟机的内核就可以直接将报文通过网卡发出去了,,,,,,这样就避免了我们的HostOS的其它模块再次处理了,,,,,,,,,正常情况下,如果不通过这种机制的话,就意味着虚拟机的这个设备网卡是模拟的,这个模拟设备就意味着它的封装内容必须要送给物理机上(既然是模拟设备,一定是在物理机上模拟出来的,而且这个模拟设备是个软件,用软件虚拟出来的,) (vmware里面的虚拟机一旦发一个报文,得首先发给自己这个软件的设备网卡,由这个软件设备再转发给vmware,由vmware交给HostOs内核,由HostOs内核处理下以后再还原回来,交给vmware,再交给虚拟机的内核,,这个软件设备网卡在用户空间)(vmware里面的虚拟机真正向外发报文的时候,首先是自己封装起来,发给模拟设备软件,,这个模拟设备软作是当硬件来用的,,,由于它是一般的软件,所以说是先发给HostOS的用户空间,,由HostOS的用户空间再发给HostOS的内核,,内核封装起来再往外发,,这样子的话,中间有n道步骤,,所以这个软件模拟的设备性能是不好的,),,,,,,,,,,,,,如果能让这个虚拟机的报文直接跟HostOS的内核打交道,性能会更好,把中间的这一环绕过去了,你这个模拟的文件该存在该存在,但是我直接绕过去,怎么绕过去?把我们的驱动程序做成系统调用,直接输出给虚拟机的内核???进程来使用,,,,,,,,,,,,,这样就违反了虚拟化的原则了,虚拟机怎么可能知道能够跟HostOS上的系统调用打交道?这样一来,虚拟机就知道自己在虚拟化里面了?????????刚才马哥一直在说,事实上这个虚拟机它并不知道自己是运行在虚拟机上面的了,虚拟机所看到的cpu, 内存,硬件设备都认为自己用的是真正意义上的cpu,内存,硬件设备,,,,,,,,,但是这里意味着必须要让虚拟机知道它自己是运行在虚拟机里面,而且虚拟机为了能够实现跟外部网络通信,必须要通过调用这个真正的OS系统调用才能跟某个硬件网卡打交道,,,,,,,,,,,,,,,所以这个时候,就意味着我们必须要让我们的虚拟机知道,而且要让虚拟机的内核清楚的明白自己是运行在虚拟化环境里面的,,,此时它有意识了,不再认为自己是运行在物理机上的了,明确知道自己是运行在虚拟化环境里面,而且要跟物理机上的OS的某一个系统调用打交道可以直接访问硬件,,,,,这种虚拟化技术我们称为半虚拟化.
Full-virtualization 全虚拟化 每一个虚拟机并不知道自己运行在虚拟化环境下,它自己在安装使用的时候跟在真正物理机上没有什么区别,
如果cpu不支持硬件虚拟化技术,只能模拟特权指令;;;;;;;简称为模拟
如果cpu支持硬件虚拟化技术,HostOS(VMM virutal machine montior)运行在ring -1,而GuestOS运行在ring 0;;;;;;;简称为HVM( Hardware-assistant VM cpu硬件辅助虚拟化 )
para-virtualization 半虚拟化 明确的知道自己运行在虚拟化环境下 这个性能好,跟硬件打交道时速度会快得多,,,,,,,,,简称为pv
我们的cpu怎么虚拟化?如果有硬件支撑???????,HostOS运行的特权将运行在ring -1上,GuestOS运行在ring 0上,,,,,,,,,,,,,如果没有硬件支撑,模拟,为GuestOS模拟特权指令,
如果cpu支持虚拟化技术了,至少意味着我们的虚拟化本身变得简单很多,
将pv和HVM整合起来,HVM(由cpu辅助,在cpu的虚拟化上容易实现)
如下图
半虚拟化技术,对于IO设备打交道的时候,它可以让我们的虚拟机内核直接跟HostOS(我们以后称为VMM,或者说是跟hypevisor)当中的这个硬件驱动打交道的,所以使得性能变好了,,,,,,既然我们的虚拟机内核知道自己运行在虚拟化环境下,而且又知道自己需要跟VMM打交道,,,,,我们把虚拟机内核改一改,让虚拟机内核知道自己不再运行特权指令了,,,,,,,,,,,(我们一直在说,为了让虚拟机能够运行在虚拟化环境下,我们要通过给它提供一个假的特权指令,给它提供一个虚拟的特权指令集,然后它也能运行,但最后还是要由VMM转换一下,)但既然这样麻烦,既然已经告诉它你已经运行在虚拟化环境里面了,干脆这样子,我也不给你提供虚拟化特权指令集了,,如果你需要运行特权指令,你就直接跟VMM打交道,通过VMM来调用,也就意味着,VMM(HostOS)把那些特权指令也干脆直接通过系统调用输出出来,,,,以前是仍然是虚拟的特权指令集,现在特权指令集也不虚拟了,你要用到特权指令,直接通过系统调用,跟我们的HostOS的硬件打交道,省得翻译一遍了,,,这样子,速度会很快,,,,,,,,,,,,,,,,只要是不允许虚拟机自己直接运行的(比如关整个物理机的电源),我们都通过系统调用提供给虚拟机内核,,,,,,,(我们如果没有虚拟机化,这些系统调用,不要提供)(如果操作系统不运行虚拟机,这些特权调用肯定没有必要提供,,,,它提供的目的主要是给那些能够知道自己运行在虚拟化环境中的虚拟机使用的),,,,,,,,,,,,,,,这些特权调用,我们通常称为Hypervisor call,(hypercall: Hypervisor call,明确说明只给虚拟机调用使用的,跟进程没关系)(而不叫system call),,,,,,,,,,,,,从此以后 ,每一个虚拟机知道自己运行在虚拟化环境下,,而且我们要改一改虚拟机的内核,(如果不改虚拟机的内核,是没办法调用这些hypercall(虚拟调用的)),,,,,,,,,,,,,,,,windows是不允许直接改内核的,所以windows是不能运行在半虚拟化模型下,,,,,,,,,所以每一个能够运行在半虚拟化模型下的这个虚拟机操作系统,我们必须要改它的内核的,把内核改一改之后,它知道自己运行在虚拟化环境下,而且知道自己在有些时候没有特权,它才调用hypercall跟硬件打交道的,,,,,,,,,,,,,,,,,,,,,此时整个问题透明了,那么中间的这些沟通成本就没了,所以这样一来半虚拟化的性能要好很多,,,,,,,,,,,,,,,
,此时整个问题透明了,那么中间的这些沟通成本就没了,所以这样一来半虚拟化的性能要好很多,,,,,,,,,,,,,,,但是有了硬件支持的虚拟化技术以后,我们连特权指令没必要模拟了,也没必要半虚拟化了??????有了HVM(有了Inter的VT-x或才AMD的AMD-v)以后,我们的特权指令压根儿就不需要模拟了,而且HostOS也没必要通过Hypercall将特权指令集向外输出了,,,,,,,,因为我们的cpu自己在硬件级别就能够管理虚拟化了,管理特权指令的虚拟化了,,,,,,,,,,,,,,,,,,,,所以,半虚拟化技术里面,我们本来cpu也实行了半虚拟化,io也实行了半虚拟化,memory也半虚拟化了(应该指的是影子MMU吧?)?????,,,,全部半虚拟化了,这样子性能就会很好了,,,,,但是有了硬件的辅助化技术(HVM)以后,cpu的半虚拟化就用不着了,io的半虚拟化一定是能用得上的,
如下图
我们的硬件无论再辅助,IO设备只有那一个(所以这个半虚拟化技术,驱动仍然是用得上,,,,,那因此就有了所谓的把pv中的cpu的半虚拟化不用了,(用HVM)),,,但是io的半虚拟化继续使用,,,,就叫 pv on HVM( 基于HVM的pv技术 ),,,,很显然,它既利用了cpu 的硬件辅助的虚拟化技术,降低了自己的负荷,又利用了IO的pv能力,,,,这样子的性能也会很好,
到这里是 2:00:00
虚拟化里面最难的就是上面说的这些东西,在vmware中虚拟机的创建是非常简单的,,,,多个虚拟机共享同一个io设备,性能无论再好,无论再虚拟化,资源依然是争用的,,,,,
如下图
如果本来某个设备就需要极高的io性能,在虚拟化环境下,它就不能提供这种能力了,,,,,,,,但是我们又期望它能提供这种能力,怎么办?IO透传技术,,,,比如网卡我们不需要底层的OS提供hypercall,底层的OS不用管理虚拟机的网卡了,,,,我们明确告诉底层OS,这个硬件设备网卡,底层OS不需要管理,这个硬件设备网卡,甚至都不提供给其它的虚拟机,这个硬件设备网卡不影响到其它的虚拟机,这个硬件设备网卡只为这一个虚拟机使用,(我们装了n个硬件网卡,每一个虚拟机有一个独立的硬件网卡),,,,,,,,所以硬件网卡就不虚拟了,由虚拟机的内核直接到硬件网卡,不通过底层的OS,直接使用的,,,,,这样子的话,性能会更好,这种技术叫IO透传技术,
如下面几张图
IO虚拟化是怎么实现的,有三种方式
第一种,模拟,Guest OS如果用到某个硬件网卡设备(模拟设备),然后先送给真正物理机HostOS的用户空间,最后送给HostOS的内核空间,最后才能通过真正的硬件网卡设备出去,
第二种,半虚拟化,通过系统调用,通过我们的虚拟化设备,直接交给物理网卡,,,,,,,,,虚拟化设备分为两段,前半段(是我们修改了GuestOS的内核以后,直接能够调用hypercall)在GuestOS内核,后半段(hypercall)在物理机的内核里面,,,,,这样子的话比模拟(全虚拟化)的方式要好很多,
第三种,GuestOS直接绕过去HostOS,直接使用硬件网卡设备,,,但是我们在HostOS上还可以直接管理这个硬件,HostOS上把这个硬件网卡设备禁用了,这个GuestOS就用不到了,,,,,,,所以HostOS还能对硬件网卡提供一定的管理功能,但大多数情况下可以不用这个管理功能,,,,,,,,,,,我们的HostOS为什么还需要管理这个硬件?因为我们HostOS得把这个硬件网卡分给虚拟机使用,,,然后这个虚拟机才能使用硬件网卡设备,,,你有很多虚拟机,很多硬件,把谁关联到谁,是需要HostOS进行分配的,,,所以需要HostOS有一定的管理接口,这就叫做透传技术,IO透传,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,但是各位要明白,如果我们没有使用透传,就意味着这个硬件(比如硬件网卡)只能归这一个虚拟机使用了,,,,,,所以如果有多个虚拟机,每一个虚拟机都要分一个硬件(比如硬件网卡),,,,,,,,,,,,所以要有足够的硬件才能使用透传,否则的话,只能使用共享,要么使用pv(半虚拟化),要么使用emulated(或者叫 emulation)(全虚拟化)(模拟)
我们常见的虚拟化技术的模型,
我们的vmware是怎么用的?
如下图
1)第一种模型:最下面是硬件,硬件上面是操作系统,操作系统上面装了vmware,vmware上面又运行了虚拟机,这种我们称为有宿主机的VMM(虚拟机监控器,即vmware),,,我们的VMM必须要借助于我们真正的底层的内核才能完成虚拟化的,,,,,这种称为Hosted VMM(借助于一个主机,借助于一个别的内核才能运行VMM),,,,,这个vmware可以利用真正主机上的各种管理工具,(比如底层主机可以有删除功能,我把vmware所创建的那些文件给它删了,就可以删除一个虚拟机了),,所以这个vwmare要借助于真正的Host功能才能工作起来的,,,,,,,,,,,,,,,,这个vmware是 vmware workstation,,,,,,还有一个叫vmware server,与 vmware workstation 是一个道理,,它们都需要借助于一个操作系统,它是个软件,有windows版本,也有linux版本,这个软件需要装在操作系统上才能运行,
2)第二种模型:最下面是硬件,在硬件之上直接运行VMM,(找一个物理机,物理机上面没装任何操作系统,直接装了一个VMM,,,,就是把vmware直接装上去了,然后在vmware之上直接运行各虚拟机,,,,这种在这个时候,我们通常把这个VMM称为hypervisor),,,,,,,,,,,VMM怎么运行?其实这个VMM自己自带了对cpu的管理机制,对内存的管理机制,(这个VMM必须要提供一个操作系统内核中的cpu的管理,进程管理,中断管理,内存管理的机制),这个VMM要提供驱动程序,否则硬件io驱动不了,所以这个VMM事实上是个操作系统,但是这个操作系统精简了只为提供虚拟机的,,,,,,,,,,,,,,,,,这个vmware是
vmware ESX,,,,,,,,,,,,,vmware ESX就是一种hypervisor,直接装在硬件上就能跑的,它是一个ISO镜像,(你主机上没有装任何操作系统,把ISO镜像导进去,然后就把自己的vmware装上去了,,然后在vmware上装任何的虚拟机),,,,,,,,,,,,,很显然,要想管理这些虚拟机,只能通过VMM了,,,,,,,,,,,,,,,vmware ESX 有免费版的,简装版的,叫vmware ESXi,,,,,,,,,,这种方式,直接跑在硬件上,自己直接来管理硬件了,这样一来,会有很多麻烦,(比如windows,要想更好的使用一个硬件,我虽然装了一个特殊的硬件,要想运行这个硬件,是需要装驱动的,,意味着这个驱动是为windows开发的,,,,,你要让这种VMM直接跑在这个硬件上,如果VMM驱动不了这个硬件的话,那就跑不了,那就意味着这个厂商再为这个专门的VMM开发硬件驱动,,或者VMM自己去开发硬件驱动,,)(VMware这个硬件厂商的能力还是很强大的,所以它应该有这种能力)(或者说要想命使用我们的vmware, hypervisor,你就只能跑在某些我要的那些硬件上,其它硬件不支持,,,,买的时候就买这种硬件)(将来在生产环境中,装任何一个业务软件,如果它需要直接跑在硬件上,你必须要看它支持的硬件列表,以后才能选购的,千万不能瞎买,买回来不能用,就麻烦了),,,,,,,,,,,,,这是第二种模型,vmware直接运行在硬件上的,
3)第二种模型:Xen,Xen虚拟化是一种非常独特的虚拟化技术,,,,,,,,,,,刚才说过,一般来讲,VMM要运行在硬件上,VMM自己要提供驱动的,而且要提供一个管理界面,不然的话怎么去创建虚拟机,至少要提供一下vmware的窗口来创建虚拟机,然后导出指定很多文件,然后添加一个虚拟化的硬件,,,这些过程要提供一下管理接口的,vmware ESX 自己有这个接口,,,,,,,,,,,而Xen这个VMM很独特,Xen除了提供对cpu的管理,对内存的管理,对中断的管理之外,驱动程序什么都没提供,也就意味着Xen自己驱动不了任何硬件设备,那它怎么办?? 你不是有硬件吗?在硬件上面装一个Xen,Xen是直接跑在硬件上面的,没错,,,,但是Xen不驱动任何额外的IO设备,你还需要在Xen上立即新建一个虚拟机,然后在这个虚拟机里面装个操作系统(一般是linux),这个linux提供驱动,提供管理界面,这个linux能够操纵各种硬件,,,,,我们说过一般虚拟机是不能直接管理硬件的,但是这个虚拟机(一般是linux)可以直接管理硬件,而后在这个虚拟机的辅助下可以创建其它的虚拟机,,,,,,,第一个虚拟机是特权虚拟机,它有个称呼,(在Xen当中,对于每一个虚拟机不是称为虚拟机的,而是称为Dom (Domain,域)),,,,而这个域有是编号的,Domain1,Domain2,Domain3,,,,,,,而这个特权domain 叫做 Dom0,,,其它的虚拟机简称为 Dom1(Domain1),Dom2(Domain2),Dom3(Domain3),,,,,,,,,以后想管理其它Dom,就必须要通过Dom0,,,,,,,,,,,,这样做,有什么好处,Xen取了个巧,Xen自己不提供驱动,你只要linux开发的有驱动,虚拟机都能用,其次,Dom0上的各种管理工具,Xen也能直接拿来用,,,,你的Copy,你的Move,你的各种管理工具,Xen都能拿来用,所以Xen是这么运行的:Xen需要结合Xen自身的hypervisor还要加上一个运行在dom0上的(linux的)操作系统,来得供一个完整意义上的Xen,,,,,,,,,,,,,,,,,,,所以这个域Dom0也被称为特权域,,,,,,,,,,,,,Dom0来提供驱动程序,而后驱动硬件设备,主要是IO硬件,,,,Dom0也无法管理cpu,Dom0要想使用cpu,也必须通过Xen,Dom0要想使用内存,也必须通过Xen,,,,,,所以Xen只对三个关键性的硬件(cpu ,interrupt,memory)提供虚拟,,,,其实interrupt还是cpu的,是cpu的一种机制而已,interrupt是对于io设备的一个管理接口,,,,,,,,,,,,,,,,,,,所以Xen是一个hypervisor,所以虚拟机(Domain1,Domain2)要想使用硬件,该怎么办???,,,,,,,,,,,,,,,,(假设在vmware workstation的场景下,我们要想创建一个虚拟机,这个虚拟机要模拟出来很多硬件的,网卡,硬盘都要模拟,,这种模拟很简单,只需要借助于VMM,在VMM上添加一个新的硬件,用vmware软件一创建就OK模拟了,,),,,,,,,,,,,,,,,在Xen场景下,因为Dom0是管理接口,所以只能在Dom0上创建虚拟的模拟设备,那就意味着这个模拟设备,那就意味着这个模拟设备要通过Xen关联到虚拟机Domain1上,Dom1要想使用网卡向外发信息,怎么发?
如下图,
Dom1要想使用网卡向外发信息,怎么发?Dom0使用软件模拟网卡的,软件在Dom0那边,最终看到的硬件设备在Dom1那边,,,,所以Dom1要想使用真实的物理网卡向外发信息,怎么发?首先,Dom1通过半虚拟化技术,经由Xen,把信号送给Dom0,由Dom0再联系硬件设备,,因为只有Dom0才能访问IO设备的,,,,Xen是不管IO设备的,由Dom0来管理,所以dom1先得跟dom0打交道,然后才能访问硬件设备的,,,,,,,,,,dom1想使用cpu,直接跟Xen打交道,,,,因为cpu是Xen直接管理的,,,,,,,,所以一部分要交给Dom0来管理(因为Xen把自己的一部分功能送给Dom0了),一部分要交给Xen来管理,,,,,,,,,,,,,,,,Xen是一种著名的半虚拟化解决方案,在半虚拟化领域,Xen是走在最前端的,,,,,,,,,,,,在Xen当中,它的虚拟化的驱动程序的前半段在DomU(Dom1,Dom2,Dom3,,,统称为DomU)的内核里面
如下图
在Xen当中,它的虚拟化的驱动程序的前半段在DomU(Dom1,Dom2,Dom3,,除了Dom0之外的,统称为DomU)的内核里面,它必须要通过hypercall能调用Dom0所提供的硬件的,,,,,,,,,,,,,,,,,,,,,,,所以Dom0将它所半虚拟化的这个硬件驱动程序通过Xen的hypercall(Dom0将这个硬件驱动程序送给Xen了),由Xen通过hypercall输出给DomU,,,,,,,,,,,,,,,,,,,,DomU要想用这个硬件,是通过hypercall来调用,而hypercall最终要送给Dom0,而Dom0再通过硬件设备传出去,,,,,,,,,,,,,
如下图
而hypercall最终要送给Dom0,而Dom0再通过硬件设备传出去,,,,,,,,,,,,,
Vmware workstation场景中,如果知道自己运行在虚拟化环境下,即半虚拟化的时候,虚拟机的驱动程序(硬件功能的前半段,即驱动的前半段,直接通过hypercall跟硬件打交道的)(这个hypercall是由底动的OS提供的),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
在Xen场景中,DomU真正对硬件的驱动在Dom0里面,所以DomU调用Xen的hypercall之后,还得送到Dom0里面,由Dom0绕过 Xen 通过硬件网卡向外提供网络信号,,,,,,,,,,,这就是Xen
如下图
所以我们安装Xen的同时,得安装Dom0,,,Xen主要实现的是半虚拟化技术,,,,就算你的底层cpu不支持硬件虚拟化,内存不支持半虚拟化(MMU不支持硬件虚拟化),不支持硬件虚拟化,IO不支持硬件虚拟化,我们的Xen照样是能获得高性能的,因为它直接使用半虚拟化技术,直接让它可以跟硬件打交道的,,,,,, Xen是半虚拟化里面的先锋人物,,,,,,,,,,,,,,,但是 Xen借助于硬件虚拟化,(比如cpu 支持硬件虚拟化,memory支持硬件虚拟化),意味着 Xen可以实现 pv on HVM,,,,,,,,,,,,,,,,如果我们的cpu支持硬件虚拟化了,Xen是可以支持全虚拟化的,
如下图
,,,,,,,,,,,,,,,,如果我们的cpu支持硬件虚拟化了,Xen是可以支持全虚拟化的,,,,大家知道,全虚拟化IO是模拟的,cpu特权指令集是模拟的,但是 Xen不支持特权指令集的模拟,它必须要半虚拟化,但是有了 HVM 以后,cpu也不用虚拟化了,这样一来, Xen 对于 cpu的使用,不用半虚拟化了,意味着虚拟机cpu的内核不用改了,但是对于硬件IO是需要改的,要使用半虚拟化技术,,,,,但是我们把硬件在 Dom0里同模拟给Domu,通过Xen输出给DomU了,你不用什么 hypercall调用了,直接认为是自己的,软件模拟的,这样子的话,内核也不用改了,,,,,,
如下图
这样子的话,内核也不用改了,,,,,,所以在完全虚拟化的情况下,就意味着(完全虚拟化与半虚拟化的最大区别就在于完全虚拟化的GuestOS操作系统的内核不用修改就能直接运行了),,,所以有了硬件虚拟化技术,Xen也能够支持全虚拟化的,,,,但是就算 Xen能够使用全虚拟化,由于软件,各种IO设备都是模拟的,所以性能会很差,,,,,,能够支持全虚拟化的主要目的在于虚拟机的操作系统内核不用再修改了,而这就意味着Xen之上的虚拟机里面也可以运行windows了,,,,,,,,,,,,,,,(刚才马哥一直在说,半虚拟化的情况下,内核必须要修改的话,windows是不行的)
全虚拟化可以运行windows,但是性能不好
如下图
如果说我们的cpu支持硬件虚拟化,而我们的GuestOS是linux,我们就可以使用 pv on HVM(也就是说cpu不虚拟化了,直接使用HVM),而其它的各种IO硬件(网卡,硬盘)还使用半虚拟化,只半虚拟化一部分,不包括cpu了,这就叫 pv on HVM,
如下图
问题是 Xen 自身,Dom0得使用一个硬件在全虚拟化(或半虚拟化)的方式当中,我们得能够创建虚拟化的模拟硬件,vmware 有个管理接口,直接点编辑什么的就能创建虚拟的模拟硬件
如下图
在linux 的命令行当中,怎么创建虚拟的模拟硬件,我们有个专门的工具,叫Qemu,这是法国的一个天才的程序员一个人开发的,而且 Qemu 完全就是一个虚拟软件,完全在命令行界面下,而且体积很小,只有1M,更重要的是它还能够跨平台进行虚拟,(一般情况下假如底层硬件是X86的cpu,上面运行的虚拟机的操作系统会认为自己的cpu也是X86的,(因为虚拟机只是虚拟了一部分特权指令,意味着虚拟机认为自己的cpu依然是X86),,所以是没办法跨平台的),,,,,,,,,,,,,,但是Qemu不是这样子的,它是一个模拟器,把cpu都给模拟出来,,,,,,,,,,,,,,,,,,,,,,,,,,一般情况下, cpu没有模拟,只是虚拟化了,将cpu时间片分出去了,,,,,,,,,,,,,,,,,而Qemu完全可以实现跨平台虚拟,假设最底层是X86的,你上面非要模拟出来苹果的arm,或者power pc,完全没有任何问题,,只不过你的 Qenu 负责将 power pc 翻译成 X86(因为你真正的底层cpu是 X86的,所以要翻译,所以性能不会好,但是至少可以这么弄,,,,有了这种功能的好处在于,它可以使程序员本来开发的一个程序只能在 power pc 上运行,现在没有power pc 硬件,Qemu可以帮你满足这个要求,它可以给你提供一个测试环境的),,,,,,,,,
如下图
Qemu 还有一个好处,Qemu可以模拟cpu,但是没有模拟,假设底层是X86的,上面虚拟机仍然打算用 X86,可以硬件加速了,可以优化一下 X86,这样就意味着,虚拟机(Domu)用接近于硬件的性能去跑,,,,只要上层虚拟机使用的硬件架构跟底层真正的硬件架构如果完全符合的话,Qemu 可以获得接近于硬件的性能的,但是这种接近也只是理论上的接近,真正与硬件性能还是有一定的差距的,85%-90%算不错的吧