我们应该怎么分析QEMU代码中某段代码的性能呢?除了比较复杂的trace-event功能(我的博客中翻译过qemu tracing的文档),其实在QEMU自带有一个简单的profiler实现,它是一个简单的计时器封装。这篇博客主要介绍怎么在编译时开启、使用QEMU profiler,并说明怎么利用这个功能添加一个自己的计时器来分析QEMU中某段代码的性能。 1. 编译 我的代码是QEMU 2.12.0。要开启profiler功能,在编译前进行运行configure的时候,只要加入–enable-profiler选项就可以了,它会加入CONFIG_PROFILER这个宏定义。比如我用如下选项进行编译: cd QEMU_SRC_PATH ./configure –prefix=/PATH/TO/QEMU_BIN_DIR –target-list=x86_64-softmmu –enable-profiler make -j make install 将其编译到QEMU后,无须改动启动参数,它是默认启用的,但是我们一般需要用QMP shell来查看它帮我们计时的信息。 2. 使用 在启动QEMU虚拟机的命令中,我们需要加入一个QMP socket,用于一会接入我们的QMP shell: # -qmp后边接我们要创建的QMP socket文件的信息,这里我们将它创建在本目录(./qmp-sock文件) /PATH/TO/QEMU_BIN_DIR/bin/qemu-system-x86_64 … -qmp unix:./qmp-sock,server,nowait 在虚拟机启动后,用QEMU源码中自带的QMP shell脚本连接QMP socket: # 这个脚本就是QEMU源码中的scripts/qmp/qmp-shell文件 # 加-H是为了以HMP命令进行交互,否则就得使用json格式,不方便 /PATH/TO/QEMU_SRC/scripts/qmp/qmp-shell -H ./qmp-sock 之后出现以下欢迎界面: Welcome to the HMP shell! Connected to QEMU 2.12.0 (QEMU) 我们在(QEMU)后面输入info profile就可以看到QEMU自带的两个计时器的数值,每次查看完这个数值,数值会清零。 Welcome […]

日记式的个人胡扯,没有一句话保证正确,谢谢围观,欢迎指正。。 通用与专用,没有好坏 首先举两个极端的例子:深度学习应用十分重要,因此人们愿意专门为它花钱设计新型专用芯片和硬件架构,甚至可能为了它的性能,没准以后会重新写了一个叫”DnnOS”(我自己瞎编的名字)操作系统;而Jc写的helloworld.py程序无论在Linux, Windows 95还是Win 10上怎么折腾,都是打印一行hello, world,没有任何区别的。 哪个更好的问题没有绝对的答案,也许深度学习的新型硬件没有键盘鼠标和显示器,DnnOS也只要实现一套新型硬件的驱动,复杂程度远远小于Linux和Windows。但是Jc的helloworld.py无论运行在多么复杂的系统上,都无法到一个好的深度学习模型所带来的价值;反过来说,即使这套新型深度学习工具再牛逼,可能都无法运行Jc的helloworld.py程序。 所以,废话就是,当然是各有利弊。 什么应用需要专用系统, 首先当然是嵌入式设备,有些嵌入式设备并没有操作系统,有些有经过精简的操作系统。为实际应用量身定做的硬件和软件,对权衡性能和成本应该是最有利的! 另一种是,在我看来,是专用服务器,比如一台专门的数据库服务器,那么首先它的硬件选型是很重要的,比如存储设备要选高端的,显卡就不要了;它的操作系统的很多东西就用不上了,比如DBMS通常会关掉OS的文件缓存page cache,甚至不用OS的文件系统,来自己管理缓存和裸块设备,包括很多多余的硬件驱动都是不必要的。 什么应用需要通用系统 最易想到应该使用通用系统的例子,就是每个人的手机还有个人电脑了,由于每个人的爱好和工作习惯不同,它们的任务多种多样,资源利用的特点也各不相同。80年代的PC、00年代的手机,都是以计数器、画图、记事本功能齐全作为卖点,后来随着系统的发展,甚至会搞出应用商店这种来增加系统的功能,这时,工作负载就更不唯一。这样,在为每个用户量身定做系统不现实的情况下,只有系统更通用才会更满足消费者的多样需求(比如有人爱拍照,有人爱打王者荣耀)。 另一种,可能是云,也就是各种虚拟化和分布式技术。原因很简单,租用云服务的用户所需要的不只是一种服务(可能是云主机、云数据库或者云存储),更重要的是负载也是无法预测的。最典型的例子是云虚拟机,这时,一个“需要通用”的操作系统运行在云上,那么这个云更通用,比通用OS还要通用。比如,一个可以称为“云操作系统”的系统,要保证计算、存储、内存的可伸缩性,这时一般OS是不提供的,还要保证运行和数据可靠,这都是通用(通用虚拟机)之外的通用(伸缩性、可靠性)。 通用也是专用 讨论了什么时候应该通用和专用,及其原因之后,突然觉得:通用也是专用,因为不管是通用的还是专用的。因为专用的本来就是专用的;而通用的,开发它们多数都是 专用 来赚钱的,不管是手机还是云。

“写放大”(Write Amplification)在存储系统中是很常见的。但是,即使都是在存储系统中,“写放大”也有很多种,各种的写放大原理并不是很一样。下边根据自己的理解,进行了下总结,如有问题,恳请指正。 1. 读写单元较大导致的写放大 在文件系统中,读写单元固定,比如都是4K,这样,如果write函数写的数据小于4K,则要先把整块读入,再修改,再把新的4K整体写入(O_DIRECT情况除外)。这个过程可以称为 RMW (Read-Modify-Write),这就是File System的写放大问题。[1][2][5] (注意:Read-Modify-Write被更广泛地用在原子指令[3]和RAID[4]中。) 再如,在DBMS等应用层存储系统中,同样存在自己管理的读写单元,如MySQL的默认读写单元称为页,默认是16KB,所以一次读写只能以页的单位进行,这时,小于页的数据读写同样会带来整页的读写,进而造成了“写放大”,道理和文件系统是一样的。 2. RAID中的Read-Modify-Write造成的写放大 如前段所述,RAID中更新一个块,需要额外读原始块、校验块,额外写校验块,所以多了两个读,一个写,也称为Read-Modify-Write[4]。 这是由于校验块必须更新,且根据异或运算的可逆性,新校验块=新数据块^旧校验块^旧数据块。 3. SSD中闪存特性造成的写放大 在SSD中,一个block可以分为多个page,在读的时候,可以以page为单位,但是写的时候,只能以block为单位。因此写的单元比较大。在上层(比如文件系统)读写单元相同的情况下,同样是读写1个page的大小,读的话直接读就行,写的话却需要先把与要写page同一个block的数据全复制一遍,加上修改的page后,再一起写入block。写入的数据量远比实际的大,这就是SSD的写放大问题。 4. 存储系统一致性机制造成的同步写放大 在存储系统的很多层次中,都有保证系统crash consistency(一致性)的设计。因此,不管是应用层的存储系统(如DBMS、KV-store)、虚拟化层中的镜像管理、系统层的文件系统,甚至是硬件层的SSD FTL[7],都要通过强制同步各种元数据的写入顺序,或者利用redo log的思想,用journaling、log-structured或copy-on-write等策略保证元数据写入目的位置生效前先完整地生成日志,来保证系统崩溃或断电时,元数据之间是一致。但是,如果多层存储系统重叠,由于一致性机制导致同步次数增加就会层层放大。 比如,运行在x86虚拟机中的levelDB,其一次更新操作就会(1)最终导致levelDB写log文件和写数据两次同步写,这两次写就又会(2)导致2次的Guest文件系统log写和2次Guest文件系统数据写,一共4次同步写,这4次写又会导致(3)虚拟化镜像管理层的4 x N次写(N取决于镜像为保证元数据crash consistency的同步次数,若是qcow2格式,N可能有5次之多[6]),最后导致(4)Host文件系统的4 x N x 2 = 8 x N次同步写。当然这是一种比较极端的情况,但实际应用中也应该存在。 5. 基于LSM树的KV系统的Merge操作造成的写放大 levelDB等KV存储广泛采用了LSM树等结构进行存储组织,其特点就是靠上的level的数据会最终被merge sort到下层,由于多数level在磁盘文件中,这也就导致了同一KV数据的总写放大,放大的倍数就是大约是level的数目。和前边4中写放大不同的是,这种写放大并非写操作时马上就会发生写放大,而是写操作发生时会潜在的导致“未来会发生”写放大,所以这种写放大只会导致整体写代价提升,不会影响实时的延迟性能,只可能会影响磁盘带宽或者在SSD做存储设备时影响闪存耐久。FAST 16上有篇论文也专门分析了这种写放大。[8] [1] Why buffered writes are sometimes stalled, http://yoshinorimatsunobu.blogspot.com/2014/03/why-buffered-writes-are-sometimes.html [2] Block size and read-modify-write, https://www.spinics.net/lists/linux-xfs/msg14456.html [3] […]

出于某种目的,你可能不想把levelDB的所有的文件都存到一个目录下: 我们希望的: level 0 -+ level 1 +–> DIR1 (DISK1) level 2 -+ —————————— level 3 -+ level 4 | … +–> DIR2 (DISK2) level N -+ 但是 levelDB 不支持类似的选项,只能将文件存到一个目录;而且不幸的是,levelDB由于频繁的compaction操作,带来了频繁的文件创建和删除,且每个level包含多个文件,不易这样改。 我想到的方法有两个:(1) 用基于levelDB改进的RocksDB,它支持多路径。(2)修改levelDB源代码。为了更简单的实现我们想要的分level存到不同目录的功能,我们从levelDB存储的最底层“创建文件”步骤,利用软链接进行修改。 本文将首先介绍levelDB的目录结构,然后分析给出介绍这两种文件更改路径方法。 1. 目录结构 创建一个DB后,和这个DB相关的所有数据都会放在一个文件夹的多个文件中。这些文件包括xxx.ldb、xxx.log、LOG、MANIFEST-xxx、LOCK、CURRENT等。这些解释在[1]中说的都很清楚,我可以用中文再解释一遍。 日志文件 xxx.log xxx.log文件包括是最近存储的数据序列,所以是大小是乱序的,当xxx.log文件达到一定的大小(默认4MB),就会被转换成Sorted tables有序表文件。这个日志文件对应内存中当前的memtable,当这个memtable满了后,会被写到level-0,对应的xxx.log文件会被删除,新的xxx.log会被生成,对应于新的memtable。 有序表文件(SSTable) xxx.ldb SST是Sorted Strings Table的缩写,levelDB中,对应的文件格式是ldb。xxx.ldb文件中的KV记录是按key的大小排好序的。随着数据量增加,ldb文件有很多。但是,它们文件名前缀数字、文件大小都与所属的level无联系(文件名即文件大小都不包含level语义),所以我们无法从文件名和文件大小判断出某个文件中数据所处的level。不过,一个文件只可能属于一个level。 除level-0外,各个level的文件总大小是预先设定的,level-1 10MB,level-2 100MB, level-3 1000MB……;而level-0较特殊,其由文件个数限制,默认达到4个level-0 ldb文件就会merge到level-1中;而且,level-0中的数据可能有重叠存在。 清单文件 MANIFEST 我们从xxx.ldb文件名和大小无法判断其所属level,那么就要有一个额外的文件存储这些“元信息”。MANIFEST-xxx文件就负责存储哪个文件属于哪个level,每次打开这个DB,都会新建一个清单文件并标一个特殊的后缀作为标记,其中的内容也是以log-structured形式追加存储的。 当前文件 […]

《人有人的用途》(The Human Use of Human Beings — Cybernetics and Society)这本书是控制论鼻祖维纳(Norbert Wiener)的一本“科普书”,虽然只有100多页,我却是一本有深度的好书。第二部分笔记是书的第4~6章。 我曾经看过两本和经济学相关的畅销书,这种经济科普书中,常见类似“让你像经济学家一样思考”的宣传。书呢看了的确挺爽,思考世界的方式也很新鲜,但是看完之后并不能在头脑中留下系统的经济学知识体系,因此,这让我一直认为这类“经济科普”都是速食垃圾,读不得。 但这次再看完《人有人的用途》的几章,却动摇了我对那些科普书的看法。这是因为虽然我的专业与维纳相关,但我却从没有像他那样用信息、控制、反馈、计算、博弈的观点重新审视过这个世界。我虽不完全同意维纳所说,但就像经济学家可以用经济学的视角看世界,维纳也在用他的控制论视角看世界。如此看来,那些经济畅销书,还是有用的,不过我还是感觉先学经济知识,再看它们也许更好。 这三章中,维纳分别从控制论的角度描述了人类语言、有机生命和社会法律和人类的关系,下面是摘录和思考。。 第四章 语言的机制和历史 维纳的父亲是个语言学家,这章的篇幅也算是书中很长的。 (摘录)语言,从某种意义上讲来,就是通信自身的别称。 维纳认为人的语言、符号能力要远强于动物,这在于两方面:第一,信码是精巧和复杂的;第二,信码是有高度任意性的。 (摘录)人对语言的兴趣似乎是一种天生的对编码和译码的兴趣,它看来在人的任何兴趣中最近乎人所独有的。 虽然人类的语言的通信一般用于任何人的交流,但是维纳认为,用到人和机器、机器和人、机器和机器之间也是完全可能的。 (摘录)从线路到机器,中间还有一个转换过程,在这个阶段中,信息可以逸失而永不再得。……如前所述,这乃是热力学第二定律的控制论形式。 语言在进化的过程中也在和自然的通信干扰作博弈,比如一些人造语的词长分布和自然语言明显不同。 (摘录)语言的磨损可能是由几个原因引起的。语言也许只是力图反抗跟它捣乱的自然趋势,也许只是力图反抗人们有目的地搅乱其名义的企图。 把语言看成博弈,可以区分语言的两个变种:其一的主要目的是传送信息,另一的主要目的是把自己的观点强加到顽固不化的反对者头上。 维纳把人看做一种终端机器,那人和人之间就构成了通信网络,人和人的通信就分成了3个阶段: 语音阶段:人耳和脑中可能还存在类似滤波器的东西,高于或低于某个频率的声音即使有再多信息也无法接收。 语义阶段:如上所述,首先输入脑中的语音才能被解析出语义;且语义的翻译依赖长期的历史记忆;当一种语言和另一种语言不完全对等,就存在语义流动上的障碍。 (摘录:外国人讲的英语,是语义学上好的而语音学上差的英语;而普通人的酒后谈话,则是语音学上好的,语义学上不好的。) 行为阶段:指的是人由接收到语义可能导致的行为。 这三个阶段,信息量其实都是在减少的,这也符合热力学第二定律,如果信息在信道中被进行了任何干预,原有信息量必然是减小的,除非中途进行 馈进 。 (摘录)作为一门控制语言逸失的学科,控制论应用于语义学方面的愿望已经在若干问题上得到了实现……对行动有重要意义的,与其说是发出的信息量,不如说是进入通讯装置和存储装置的足以行动扳机的信息量。 第五章 作为消息的有机体 本章把有机体生命类比成消息。(有机体与混乱瓦解,正如消息与噪声间的关系) (摘录)在趋于毁灭的世界中,声明就是此时此地的一个孤岛,生命体抗拒毁灭和衰退这一总流的过程叫做稳态。 (摘录)……(我们会)生活到我们衰老速度大于更新速度为止 维纳认为人类等有机体可以看成一种 模式,这是因为我们的身体在不断代谢,组成人体的物质并非一成不变,只有“模式”是永存的。维纳的这种思想让我想到了当今的软件之于硬件,磨具之于产品,更直接的:3D打印。不过维纳更想说明的是,人类等有机体只是一种信息的形式,信息的传递比物质的传递更重要。(摘录:模式就是消息,它可以作为消息来传递。) 如果通信分为两种:物质运输和信息运输,那么两种之间也没有什么不可过渡的,只是当前的技术无法实现如此大量的基因数据传递(即使现在也不行),但是即使是人被交通运输向其他地方,维纳还是认为那本质上来说还是信息的运输。 以人体生殖细胞的结合与分裂为例,维纳认为“两部原先不相耦合的大型机器有可能耦合起来,从而从该阶段起像一部单一的机器那样工作。”,这我不禁想到当今的分布式系统、云计算的概念。 第六章 法律和通讯 正如第四章所说,语言是通信的形式之一,而法律则是语言的作用之一:用来“道德控制”。我觉得,媒体或者舆论应该在这方面也是类似的,都是用语言作为信息,让发信的一方的思想影响接收信息的思想,法律、新闻、舆论引起的社会后果便是信息传递后的反馈,这种反馈会再次作用到发信方,进行下一次“控制”。这种思想在维纳的这本书中也有体现。 维纳不仅强调了法律和语言通信的关系,还以契约法为例指出了法律必须有解释机构。“如果所用术语的意义未经确定或者它们的意义随法庭的不同而不同,则契约的双发就不能以任何正义来弄清订契约的意义了。”。我觉得,既然执行法律被类比为了通信,那么契约法律就类似我们现在常说的通信协议,收发双发事先的一致,才能有效的让有用的信息传递。 维纳还表达了他对当时刑法的不满,他说这是因为有人将刑法看做惩罚罪犯的手段,有人看做赎罪的仪式,有人看做隔离罪犯的手段,还有人看做道德改造的手段。因此,这导致了刑法制定的混乱。 (摘录)因为立法者或法官要对法律作出唯一正确的解释,因此,法律可以看做通信问题或者控制论问题,也就是说,法律的问题就是对若干危险情况进行只需和可重复的控制。 维纳还将当事人力图用法律条文赢得法官和陪审员的支持、律师有意图地引入混乱设法让对方的陈述无效类比为冯诺依曼博弈行为,或者一种互相的信息阻塞行为。 [1] 豆瓣读书–人有人的用处, https://book.douban.com/subject/1455960/

《人有人的用途》(The Human Use of Human Beings — Cybernetics and Society)这本书是控制论鼻祖维纳(Norbert Wiener)的一本“科普书”,虽然只有100多页,还是32开纸,但是我觉得却是一本有深度的好书。首先,这本书并没有任何公式和严谨的控制理论,还有很多恰当的小例子,因此特别易读;但是虽易读,并非无营养,相反,每一段、每个观点都可以细读,引发思考。 与“科普书”的定位相比,更应当注意的是这本书的副标题:“控制论和社会”,作者不仅让人信服的解释了熵的概念、(机器)学习的概念、语言信息传递的原理;也对法律、社会政策等方面的思考和讨论;最后时甚至对未来发展进行预测……行文间,作者的例子不仅涉及自己的控制论,还包括信息论、生物学、热力学、社会学等多方面的思考。更令人叹服的是,虽1954年成书时人工智能之一概念才刚刚起步,作者在这本当时的“科普”书中,就已经能够看出各种现在很火的统计机器学习、仿生神经网络等让机器从历史经验进行未来预测的思想! 我读这本书并不快,但第一次也只是匆匆略过,现在想抽时间再读一遍,准备记下好的观点和自己对应的思考。总之,这是一本连相关专业学生都无法当科普书来读的科普书。 第一章 历史上的控制论 其实控制论是属于信息论的。 在控制与通信中,我们要与组织性降低和含义受损的自然趋势( 熵增 理论)做斗争。 一个复杂的动作可以有如下定义:为了影响外部( 输出 )而引入了可以有大量组合的数据( 输入 ),这些数据可能是当下刚放入的数据,也可能来自过去 存储 的数据(记忆)。(我感觉这种观点其实类似计算机系统的输入输出I/O和存储区memory/storage) 机器对自己的动作的后果作为下一步动作的所需信息,即是 反馈 。 作者认为生命个体和机器都是利用反馈来控制熵的,控制中枢用 自己打算做的(目的信息) 和 外界运演过的(反馈信息) 进行动作。 第二章 进步和熵 这一章作者进一步通过热力学第二定律的角度解释 熵增 的概念。 机器不一定可以称作“生命”,但和生命类似的地方是都在 整体的熵增 中,实现了 局部的熵减 。 消息传递的产生,一定伴随着能量的损失。 第三章 定型和学习–通讯行为的两种模式 (关于反馈) (定型应该指的是人的构造决定了人的多样和创造力,学习比较类似反馈思想或者现在的机器学习思想,从发生过的事总结经验,或者发生过的对未发生的动作产生影响) (摘录)学习,和比较简单的反馈形式一样,也是一种从未来看过去和从过去看未来的有所不同的过程。 (摘录)能学习的生物是从已知的过去走向未知的未来,而未来是不能和过去互换的。 作者认为,双向通信在控制中很重要,(其实就是反馈)。作者还进一步讲了“ 社会反馈 ”的概念,比如阶层之间、君臣之间的通信有着特殊的限制,不同于一般人与人间的通信。 (人类社会与昆虫社会的区别源自机械定型) […]

大部分截图来自原书,贴出书的官方主页: 《Operating Systems: Three Easy Pieces》 (作者Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau)。感谢原作者这么好的书。 本篇笔记是书的第三部分(Persistence),讲述了操作系统和外存系统相关的内容。 第一节 I/O Device 1.1 IO 总线 一般情况下, IO设备的性能较差(慢),所以用Peripheral IO Bus,为什么不用像显卡一样用的PCI呢?因为1)越快的总线越短,这样空间不够插;2)越快的总线制作成本越高,如果存储设备照总线的性能差的远,没必要用高性能总线。 这张图为总线的层次结构,memory bus是最快的也是最近的,IO Bus比较远,也是最慢的,中间有用于显卡的PCI等总线。 1.2 典型设备的组成部分 一个典型的外围设备如图所示,包括两部分: 接口 和 内部结构 。 接口: 类似软件接口的功能,硬件接口是留给OS和设备交互的。 内部结构: 比如㓟CPU、MEM等基本组件,还有称为固件(firmware)的软件来实现内部功能。 1.3 两种IO模式(Polling和Interrupt) Polling的形式,而interrupt。 一种典型的协议是 Polling (轮询),称为programmed IO,步骤有4: 循环等待STATUS寄存器直到设备状态为不busy 写数据到DATA寄存器 写命令到COMMAND寄存器 循环等待STATUS直到设备为不busy Polling显著的缺点就是太浪费CPU时间,这是因为IO相对于CPU是很慢的,大量的CPU时间被用在了等待上。 Interrupt (中断)方法(也称为interrupt-handling IO)可以解决这个问题,用Interrupt方法进行IO时,当设备完成操作时,会raise一个硬件interrupt。但是这样的话,如果设备很快(比如现在的NVMe SSD设备),Interrupt由于需要进程上下文的切换、以及中断的控制等原因,会拖慢IO的速度。所以两种方法各有利弊: Polling […]

大部分截图来自原书,贴出书的官方主页: 《Operating Systems: Three Easy Pieces》 (作者Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau)。感谢原作者这么好的书。 本篇笔记是书的第一部分(Virtualization)的下半部分,讲述了操作系统是怎么通过地址空间的抽象,将内存资源进行虚拟化的。 第一节 地址空间 1.1 内存虚拟化 多进程OS的资源共享策略 本书上半部分讲了CPU的共享策略:通过进程(process)这个抽象,OS将时间片分给进程。 对于内存资源的共享:为了让昂贵的计算机能够支持多个程序同时运行,如果在切换某个进程时将内存数据从磁盘换入(进程共享磁盘,内存和寄存器都不共享),由于磁盘IO太慢,不现实。所以现在的系统,都是将相对较快的寄存器换入换出 ,所有进程数据共享内存资源( 寄存器不共享,内存共享 )。为了实现这种想法,并更好地管理内存, 地址空间(Address Space) 的抽象被引入(如下图),相对为每个程序固定分配一定大小的内存空间更灵活,用地址空间进行管理更加灵活。 地址空间在 结构 上主要分为Code、Heap和Stack,Code部分用来存程序运行的代码,Heap是用户程序动态分配内存(malloc/free)所使用空间,Stack是变量使用的空间。除非程序递归很多,一般Stack都是够用的;如果程序视图访问非法地址,可能出现Segmentation fault的错误。 每个进程都会有一个自己地址空间,且每个进程都认为自己的地址空间是从0开始的,并且地址空间的地址也不必和物理地址相等,甚至地址空间的总大小可以大于物理内存大小,这就体现了一种 内存虚拟化 的概念,OS的内存管理系统也可以称为 虚拟内存系统(virtual memory system, VM) 。因此,我们编写的程序中,所有我们可以得到的地址也都是虚拟地址,并非物理地址。 VM系统设计的三个目标:透明(transparency)、高效(efficency) 和 保护(protection) 。其中保护即隔离(isolation),进程间的地址空间需要隔离,进程和OS间也需要隔离,(甚至在有些微内核操作系统中,OS的一部分和OS的另一部分也进行了隔离),这样可以保证安全性。 1.2. memory API 对于用户程序,Stack中的变量是自动管理的,比如用int声明一个整数变量,而Heap中的内存是由程序(程序员)负责的,什么时候malloc/free都要程序员进行考虑,所以要格外小心一些常见的malloc错误,比如忘记分配内存、分配的不够导致buffer overflow、忘记初始化所分配内存内容、忘记free等。 purify 和 valgrind 这两个工具可以协助检测内存分配的问题。 1.3. malloc、free和mmap的关系** 要注意 […]

截图来自原书,贴出书的官方主页: 《Operating Systems: Three Easy Pieces》 (作者Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau)。感谢原作者这么好的书。 本篇笔记是书的第一部分(Virtualization)的上半部分,讲述了操作系统是怎么通过进程的抽象,将CPU计算资源进行虚拟化的。 第一节 进程的抽象 1.1. 策略和机制 Policy在mechanism(策略和机制)在操作系统中通常是分开设计的。比如如何切换上下文是一个low-level的mechanism,指底层的方法或者协议。当前时刻应该让哪个进程运行更好是一个high-level的policy的问题,指一些“智能”的调度。 1.2. 虚拟化和OS OS本身就可以看做是“虚拟机”(virtual machine)。系统通过在可以被接受的合理开销(时间、空间)范围内,将计算机CPU、内存、存储等资源进行虚拟化(抽象),目的是为了用户的方便使用。 CPU虚拟化 主要体现在将任务抽象为 进程(process) ,将资源按进程隔离,然后多个进程轮转使用计算资源; 内存虚拟化 主要体现在 虚拟地址空间(virtual address space 或 adress space) ;而对于持久化的 存储 ,OS让文件共享,没有那么多私有隔离,OS假定用户会用文件来共享数据。 (????) 第二节 进程API 2.1. fork和exec exec()函数组和fork()的区别是:fork()复制当前进程为一个子进程执行,exec()会执行一个新的程序;exec()执行以后就再也不会返回。 2.2. shell和stdout redirect 例如shell就是一个普通的用户程序,利用了fork和exec的组合。给出一个提示符,你输入可执行程序时,会先fork(),然后在fork出的子进程中exec()这个命令,然后调用wait()来结束。这种fork和exec的组合使用,可以在让shell在fork后运行一些其他代码:fork后的子进程可以redirect操作再exec;fork后的父进程可以执行wait操作在exec结束后再显示提示符prompt。 stdout redirect的原理很简单,stdout一般的fd都是0,在fork后的子进程中close掉stdout,然后打开要redirect到的文件,这个文件就会获得为0的fd,这时执行新命令的exec,则会把这个为0的文件看做stdout进行输出,程序清单如下图: 2.3. 其他 kill()可以向进程发送信号,让程序sleep, die。 第三节 […]

谁来定义虚拟化? 一个比较抽象的词,很难给出一个比较明确的定义,而且我也一直认为没有人可以对一个抽象的概念给出绝对正确的定义。如果一个公司想争夺一种技术的定义权,我会毫不吝啬的给它扣上“技术独裁者”的帽子。 但是理解一个词在当前这个时代、这个世界的用法还是很有必要的,比如“虚拟化”这个词,对虚拟化开始重点关注以来,对“虚拟化”这个词的理解的确是在逐渐变化的,今天就来说一说我目前为止的理解。 从虚拟机说起 关注虚拟化方向之前,我对虚拟化这个词的理解是经历了几个阶段的。最开始可能“虚拟机”啊,有了虚拟机,大家就能方便地在Windows下用Linux系统啦。后来云计算火起来了,云服务器的底层技术是什么啊,是虚拟机和虚拟化啊,所以感觉虚拟化真是很牛逼。后来Docker火了,Docker是什么,人家都说是“轻量级的虚拟化”,哇,虚拟化这么牛逼,虚拟机、云服务器、Docker都用了虚拟化啊。 虚拟机就像下面的图中,如果把一个计算机软硬件系统比作一个从地基垒起的金字塔(用户和应用在金字塔顶),那么虚拟机就像一个倒立的金字塔,虚拟机的用户就像是倒立的金字塔上的金字塔。要让一个虚拟机正常运转,就要在软件层面上模拟各种计算机硬件,涉及到计算,存储,网络等各种设备。 以QEMU为例,虚拟机的CPU是以QEMU创建的vCPU线程进行模拟的,这些线程仍然由Host操作系统进行调度;虚拟机的网络和存储IO是通过virtio这种半虚拟化机制达成的,Guest的IO通过内存的循环队列传递数据和消息;虚拟机的存储设备通过Host中的一个文件来进行模拟……但是虚拟机(Virtual Machine)所用的技术就是虚拟化技术(Virtualization)吗?我认为不一样。 虚拟机之外的虚拟化 以存储虚拟化(Storage Virtualization)为例,Linux逻辑卷管理技术(LVM),有了这种技术,我们可以将多个分区虚拟成一个大的逻辑分区,或者将一个分区虚拟成多个小的逻辑分区。LVM之外,Linux中的loop回环设备(将一个文件虚拟成一个块设备)、tmpfs(将一段内存虚拟成一个文件系统分区)、RAM DISK(将一段内存虚拟成一个块设备)、UnionFS(将多个文件系统虚拟成一个文件系统)在我理解都应该属于虚拟化之列。而QEMU虚拟机中,我们多是把一个文件虚拟成一个磁盘,其灵活性无法代表整个存储虚拟化技术,或者说仅关注QEMU,我们不能关注到所有的存储虚拟化技术。 QEMU VM的vCPU是Host操作系统中的线程,可以看做是计算的虚拟化。其实按照广义的虚拟化,Intel的超线程(Hyper-threading)技术岂不更加“虚拟”?比如它可以将一个核抽象成操作系统看来的两个核。 虚拟化,无处不在 更广义(准确)的,其实操作系统技术本身就是虚拟化技术,没有操作系统的进程调度时间片切换,也许CPU是1核的就只能同时跑1个程序,但有了操作系统,你不仅能同时照着网页抄作业、还能看着paper听着歌。这一切都是系统将计算资源虚拟化的结果啊。 再说编程语言,程序员之所以不用编写二进制代码,便是因为有工具可以将高级语言翻译成机器码,如果不去研究编译器,这种翻译也是透明的;编程语言,形形色色,不管你用哪种,其实背后都是0,1在执行(电位在变化)。我可以在这篇博客里写一串二进制码011110100110101001100011,你看到的0和1并不是底层的0和1,但还是0和1,不也是类似虚拟机软件反向搭建金字塔的过程吗?同样,人类语言也可以看成是思想的“虚拟化”。 所以,什么是虚拟化?虚拟化,道也,什么是道?老子说,“道可道,非常道”。