Linux运维 第二阶段(十六)OS优化(1

一、相关概念:

OS optimization

1understanding the linux operating system

CPUcentral processing unit)三大核心部件:运算器、控制器、寄存器

运算器(ALUarithmetic logic unit算术逻辑单元,算术运算、逻辑运算等)

控制器(control unit,控制指令,数据的存取过程,到什么地方加载数据,加载完成后放到什么地方通知运算器计算,计算出的结果如何取出来放到什么地方,由控制指令完成,程序=指令+数据,数据取出来由运算器运算)

寄存器(register,指令寄存器instruction register和程序计数器program counter,数据取出来CPU要在本地完成运算,计算出的中间数据要找一个位置存下来,这就是寄存器要干的事,数据只是暂存很短的时间,而且这个空间是不断被刷新的,只是拿来保存计算的中间结果(运算结果),register固然可实现数据存储,但它并非是核心部件(或核心存储部件),因为它的空间太小了,CPU内部的计数器要具备锁存数据的能力,它的构造结构要比外部的RAMrandom access memory)芯片复杂的多的多,同一个存储单元,若内存RAM中只需一个存储电路,而在CPU寄存器中则需要N个存储电路用来实现暂存,而RAM不具备锁存这种能力,无论读还是写都要与外部的存储建立关联关系)

 

大容量的存储设备就是内存(RAM设备),RAM本身也是一个个的存储单元,通常8bit1byte)为一个单元(一个cell),这些数据最终要能被CPU读取,CPU必须要对应的的找每一个cell,所以CPU在内部要具备寻址数据的能力(到RAM中按地址去寻址数据的能力)

RAM中的每一个cell有自己的存储地址,具有唯一性(按十六进制编址),若CPURAM交互(都是电路单元)中间要通过某一芯片(north bridge)负责将CPU的电路与RAM的存储电路兼容而建立关联关系,RAM上的金手指与主板建立连接并映射到电路上

CPU字长有32bit64bit之分,32bit可理解为CPU出来32根线,这32个电路分为有电和没电两种,这将有2^32=4G个变化,意味着只能找到4G个存储单元,32bitOS只能理解4G的内存空间,64bitOS可理解4G*4G的内存空间(海量)

CPU完成寻址的线路和CPU完成数据存取的线路是同一个线路,线路是复用的,需要一个电路完成控制指令,如用一种标记(000110等)来识别这个线路是如何使用的

 

PAEphysical address extention,给32bit的寻址总线再加4bit,使得最大可寻址内存空间扩展为64GOS要支持这4bit寻址总线的能力,虽有4bit扩展,但并非所有OS都支持寻址这么大,如redhat56就算使用PAE也不支持这么大内存,所以当内存大于4G直接使用64bitOS

若硬件CPU32bit的体系结构OS只能用32bit,若同时使用了PAE技术,无论内存本身是多大,单个进程所能使用的内存空间是不会超过3G的,这只不过使得多个进程总共可使用更多的内存空间,单进程的寻址能力仍是3G,另1G给内核了,这样对单进程的服务来说再大内存也没意义,如mysql是单进程多线程模型,在运行时可能会用更大内存提高更快的速度,而mysql32bitOS上最大只能使用2.7G内存,就算PAE扩至可用64G内存也没意义,这种情况只能使用64bitOS

 

CPU的寄存器与时钟频率几乎是同步的,可与CPU一样的速度完成对数据的存取,而RAM1333MHZ1600MHZ)与CPU的速度是不匹配的,比CPU时钟频率要慢的多,利用缓存可有效提高总体速度,假设的前提是程序具有局部性,空间局部性(某数据被访问到,则认为离它近的数据也会很快被访问到,缓存在加载数据时会将该数据周边的数据一并加载进来,一旦这些数据被访问到则会提高命中率)和时间局部性(一段数据被访问到则认为很快又要被访问到),程序本身很大,缓存器的空间很小比原始程序存放的空间小的多,缓存器比原始存储器要快,缓存器的造价要高,缓存中没有时则到外部的内存中去加载,缓存中通常放内存中的热区数据,根据二八法则放内存中访问最多最频繁的数据,若访问的数据,缓存中没有,当缓存空间未满则补进去即可,当缓存空间已满通过置换策略(MRULRU要好,但要依赖额外的标记位)将之前不用的数据置换出去,若程序在开发时就利用了缓存的原理,那这个程序性能会好很多,那些开发高性能程序的程序员是对计算机原理有着非常精深的理解

CPU引入缓存的机制使得CPU的速度与RAM的巨大差异得到弥补,但又不能最大限度的发挥CPU的性能,所以通常都有多级缓存,一级缓存、二级缓存、三级缓存,一级缓存分一级指令缓存和一级数据缓存,二级缓存用来弥补CPU和三级缓存之间的差异,同理所以,寄存器比一级缓存快,一级比二级快,二级比三级快,三级比内存快,若CPU是多核心的话一级缓存和二级缓存是每核CPU独有的(直接整合在CPU中),三级缓存是共享的,一般缓存离CPU越近造价越高(使用SRAMstatic)静态RAM技术)

CPU若是多核为与内存匹配衔接引入缓存,一级缓存(一级指令缓存I1、一级数据缓存D1)、二级缓存L2、三级缓存L3,其中一级缓存、二级缓存是每核CPU独有的,三级缓存是共享的

RAM是要不断的需要充电,不能有一点供电减小,数据对硬件来说就是有电没电,读一个数据就是将里面的电荷取出来,所以要按某种频率不停的刷新,而SRAM不需要不停的刷写读一次还可再次重复的读,它使用的电路元器件比RAM要复杂的多所以造价要高速度会快的多,CPU的芯片非常小,可操作的空间范围也很小,因此CPU的缓存空间是有限的,一般一级缓存以K为单位(128K,256K等),CPU性能在于主频和缓存能力上(缓存空间翻一倍,价格几乎要翻一倍)

 

RAM的空间(就算热区数据20%)比缓存空间也要大的多的多,而缓存的数据有限,会导致缓存频繁刷新,命中率会很低:

方式一:直接映射,RAM中的每个存储单元都可缓存到缓存空间中的任何位置,这种方式性能低,命中率低

方式二:N路关联技术(1way,2way,4way,8way),8路也称完全关联fully associative,路越多则命中的可能性就高,缓存置换不至于太过频繁

如图:将RAM划分为4片区域00-31,缓存则有8个区域

1way指可存00,08,16,24上其中一个的数据,同一时刻只存这其中的一个,若读到08中的数据,08将覆盖01,同理,使用16上的数据时16会覆盖08

2way指可存00,08,16,2400,08可同时存放到缓存中的第1和第2个区域

4way指可存00,08,16,24上的数据,分别存放在缓存第1234的区域

8way指可存00,08,16,24,01,09,17,25上的数据,分别对应缓存的全部区域

注:00,08,16,24是多个内存单元

wKioL1aqF4OznG3mAABbT7NSLMg551.jpg

 

CPU直接打交道的是一级缓存,一级缓存中没有则从二级缓存中转换过来,二级缓存没有则逐级转换,若CPU更新了一级缓存中的数据(写操作),则要随之更新二级-->三级-->内存-->硬盘,对于读只要能命中速度会快的多,要是写操作更新内存相对要慢很多,若不更新万一断电数据就没了,所以要写到内存中

OS内核中有策略会持续不断的将数据从内存写到磁盘上,若没有写入内存的话会导致性能过低,若在写缓存数据时同时更新内存这叫通写write through;若CPU在更新缓存数据后不会立即更新内存,等到缓存中的数据要被丢弃时(需要腾空出来转换其它的数据)才更新,这叫回写write backwrite backwrite through性能要高

现在的CPU对于写操作都是write backCPU要与内存交互大量数据,norith bridge要足够好,通过north bridge这是一种架构,还有一种架构是通过一芯片直接将内存与CPU建驳

RAM外显卡video card通过north bridge也要与CPU交互大量数据,需要CPU处理大量数据,并快速完成交换,数据通过显存带宽(与芯片和CPU交互的通路)交互完成后一帧帧的显示在显示器上,显示的数据需要显存缓存下来,显存不够时还要借助内存来缓存,现在CPU都内置了显存芯片(GPU),独立显卡性能要好点可腾出CPU完成其它工作

注:video card(既要处理大量显存数据,完成硬件加速,又要CPU渲染)

 

wKioL1aqF5Si_mwBAABLS4mqdrY236.jpg

计算机通常只需要三个核心:运算器、控制器、存储器,与外部交互的其它都称作IO设备,显卡总线就是个IOIO分:高速IO(如PCI-EIO,通常是与north bridge连接)和低速IO(通常与south bridge连接),south bridge将慢速总线的IO设备汇总起来一并接入north bridge,这些桥接芯片汇总外部IO数据总线以完成与CPU的交互;这么多的外围设备最后都通过同一总线(总线是复用的)与CPU交互,为不混淆通过IO port实现

注:PCIperipheral component interconnect外部总线互联,这是统称,有IDESATASCSIUSB等总线)在south bridgePCI-Enorth bridgemousekey是串行速度更慢,

IO port65535个),任何一个硬件设备通过IO总线接入到计算机的总线上,在开机BIOS自检时就向CPU申请一批连接的port,每次注册可能都不一样,之后CPU与某一硬件设备交互,CPU就将数据发到已注册的一批连接的port上,硬件设备从这些连接的port上读过来

各种硬件设备内部的电路与计算机内部的电路不总是一致的,所以外部设备都有自己的控制器(或叫适配器),如某一SATA口就是一个控制器(可理解为翻译官,将设备上所能理解的信号转到计算机总线上能通信的信号,控制信号的传输速率、检验等额外功能);驱动通常就是驱动芯片指挥着硬件工作;除内部就有的控制器(mouse,key,disk,usb)之外,有些设备计算机上不会标配,可通过PCI槽(为其它设备预留的空间)接入适配器,这些PCI槽都保留有IO port,没接入适配器时是不需要向CPU注册port

 

所有设备的操作都在CPU的控制下完成,若在键盘上输入数据或网卡上来了数据,如果不响应那电信号就没了,CPU如何知道来了信号:

1pull拉取机制倒简单,但不能用,CPU要在极短的时间不断的向各控制器要数据,CPU同时要处理各种工作,不能在这上面浪费大量时间;

2)通知机制,某个IO设备上来了数据自己要能通过控制器通知到CPU,有很多硬件设备CPU如何知道是谁通知的,通过CPU的中断控制器解决,通常每个硬件设备在启动时要向CPU的中断控制器(CPU外置的芯片,帮CPU识别哪一个硬件设备发送的中断信号)申请使用一个中断向量(有地址的通知机制,硬件设备发的中断信号叫中断向量),中断控制器上有中断线(中断通路,是复用的,可接多个设备),中断控制器在接到信号后会告诉CPU要将当前工作停下先处理紧急的事情,是哪个设备发起的中断请求CPU就通过该设备的IO port与之交互

 

任何外部设备一旦有数据信息,通过中断控制器通知CPUCPU与硬件设备交互把数据读进来,该响应就响应该处理则处理,若某一时刻CPU正忙,将数据读进来的操作这是紧急的,之后的处理过程就不紧急了,CPU该忙其它的就忙其它的去了,等到轮流过来再执行

中断处理过程分为中断的上半部和下半部

上半部(尽可能把中断信号接收下,读到内存中,CPU再去忙其它的事,忙完后再回来处理)

RAM和硬盘比CPU速度要慢的多,CPU要不断的接进来数据同时还要忙其它事情,CPU这样忙也不合理,通过DMA协助完成(可理解为DMA是助理),数据接进来要占据总线,总线是CPU资源(可理解为CPU是领导),若任何硬件都能占据总线并传输数据那CPU就被架空了,所以总线这个资源要被CPU授权后其它设备才能使用,若某硬件设备被授权使用总线,CPU和这个被授权的硬件设备就有竞争了,有竞争就要有锁机制,否则恶意竞争就混乱了,计算机内部若有多颗CPU的话很麻烦,要共享内存等资源,彼此间就有竞争,任何有竞争的地方称作临界区(或关键性区域)

注:硬件设备->south bridge->north bridge->cpu,硬件设备->south bridge->north bridge->RAM

一旦产生中断,CPU接收到中断信号与该设备交互并将数据读进来,发现这个硬件设备有DMA机制,CPU在内存中划分一段连续空间并将总线使用权授权给DMA,于是CPU退出,DMA控制着将数据读到内存中,CPU访问RAM的所有存储单元要有寻址能力(32bit最多能寻址4G的空间),DMA若也有32bit那就相当于是CPU了,所以DMA对内存的寻址能力是很窄的,寻址一小段连续的内存空间,位于内存中低地址的空间预留给DMA使用,并很容易被寻址到的那段起始地址,这段内存空间一般是16M16M下面还有1M空间供BIOS使用;CPU的寻址空间是线性地址空间,并不是直接访问物理内存

计算机开机后本身并不会做其它事情,只有在程序的指挥下才做事,CPU在运行指令后才智能,指令在硬盘,硬盘在哪CPU不知道,CPU有自举的过程,开机后直接把CMOS中的BIOS程序(对硬件做health check)直接映射到线性地址空间的最起始处(内存低地址的那1M区域),CPU去加载该内存区域的指令,开始运行从而进入系统

DMA帮助CPU将数据装载进内存,完成后向CPU发起中断,说明已装载完成,于是CPUDMA待一边去它收回这段总线的使用控制权,负责处理之后的工作,这一切的工作由CPU执行的指令完成,指令来自内存,内存从辅存(硬盘)中获得

 

 

2CPU在某一时刻只运行一个程序,若要运行多个程序,某一时刻CPU从一级指令缓存中取指令,从一级数据缓存中取数据,取一条执行一条,CPU的处理速度要比IO快的多,而程序都是从外部设备加载的,加载后运行,若运行只要1s,而加载过程需要10s9s的时间CPU就要处于等待状态,加载一个执行一个,同时运行的有很多个指令,同时运行的有很多进程,让CPU每时每刻都不闲着,边加载边执行,使得CPU计算资源尽可能利用起来

CPU指挥着硬件同步的协调工作,若CPU100圈,RAM才跑了一圈,各个设备之间要步调一致才行,设备之间交互取决于慢的,为了等待RAM转一圈,CPU就要白转99圈(时钟周期,单位时间内转的圈数),CPU内部有时间产生器(晶体振荡器)产生时钟脉冲,一高一低有波峰有低谷,CPU就以这个时钟脉冲为基准产生时间,大家都以这个时间为准,CPU与这个时钟脉冲同步调,CPU在走完一个周期后而内存可能才走了一点点,CPU与内存的交互一般在时钟周期的上升沿(有上升沿和下降沿,上升沿是从低价到波峰这段,下降沿是波峰到低谷)进行(切换过程)

若只一颗CPU且单核,给我们的假象是多个程序在同时运行,实际上并不是并行,某一时刻仅一个程序中的指令在运行,有一个管理系统负责从硬盘加载一个程序到内存才运行,早期叫monitor(监控程序)-->后来发展越来越大叫OSoperation system-->VMvirtual machine),所有进程要运行都要向OS申请资源(避免某程序独占资源),程序有局部性,每次加载程序的一部分到内存中才执行,这个执行的实体叫processprocess是一个独立的运行单位,这个运行单位又没法直接在硬件上运行,要有OS(或monitor)调度运行,这样可同时有很多运行单位在运行并由OS完成调度,这个独立的运行单位还需要系统资源,如CPU时间、存储空间、运行的结果要保存到内存,因此要实现多个程序独立运行,但CPU、内存只一个如何分配,OS可称作VM,把一个CPU虚拟成多个,把CPU所提供的能力给虚拟出来,按时间进行切片,同时CPU在运行一个进程时还依赖各级缓存,缓存当前程序数据,一旦把CPU时间切出去了,当前使用CPU的权力就在另一进程上了,而另一程序也要使用缓存,若缓存空间足够用倒好,若没有足够的空间之前上个程序缓存的数据就都没了,进程中有很多指令组成,若指令没运行完但分给它的时间到了,如这个进程中有100条指令已执行到第73条,下一次回来运行时就要从第74条开始,CPU通过内部register的程序计数器就知道下次运行第多少条指令,程序计数器始终指向下一条指令的位置(如当正在运行第72条指令时,程序计数器就指向第73条指令),程序切换要保存现场(在内存中保存)、恢复现场,恢复现场时若缓存中没有,数据再加载即可,寄存器中的状态数据一定要能够恢复

 

内存如何虚拟?不能让某一进程直接使用内存,某个进程会认为它没使用的内存空间都是自己的可用空间,这些它认为空闲的内存空间很有可能被其它进程在使用,若这时某一进程直接使用会导致系统崩溃的,让多个进程同时使用,彼此间又不会挤占:

1)按内存空间切片,先给内核预留一部分,再将其它的所有空间分给进程,进程随时会启动或终止,过段时间发现内存中漏洞(碎片)很多,通过这种机制分配,若进程所需的空间不够用时,并不能避免覆盖并占用其它进程的内存空间,要引入内存保护机制,分给某进程多少内存空间它就使用多少,这时不够用时就无法解决了(32bitOSCPU可寻址内存4G空间),因为每个用户的计算机配置不同、程序员开发时并不知道要运行这个程序的主机有多大内存等,这种机制不符合CPU的寻址能力

2)将内存事先划分好固定的存储单元或叫存储槽或叫页框page frame4K为一个单位,byte太小分配回收太复杂使用K为单位),每个page frame存储的一个数据叫一个页面page,引入内存空间的映射机制,每个进程都认为自己有4G的内存空间可用,进程认为的这个空间是模拟出的线性地址是虚拟地址(或叫进程描述结构,该结构由内核在内存中负责维护),通过控制芯片将线性地址映射为内存的物理地址(将每个进程的页数据映射到物理内存的页框);例如一个进程的线性地址中指令区、数据区、BSS区、堆区、栈区各占1个页,共占5个页的数据,通过控制芯片在物理内存中找5个空闲的页框来存这5个区的数据,而且在物理内存中很有可能是不连续的页框;进程在启动时由内核告诉进程你的结构是这样的,真正的数据是在物理内存不连续的页框中,在线性地址中看是规整的但在物理内存上是不规整的;进程在CPU上运行,CPU从内存中取出指令运行,指令要加载页中的某个数据,页的地址是虚拟的线性地址与物理地址并不是一一对应的,CPU在取线性地址中的数据时要通过控制芯片将线性地址翻译为物理地址才能真正拿到数据,这个芯片要保存每个进程线性地址与物理地址的映射关系(通过页目录,一级目录、二级目录、三级目录,目录是映射而不是容器)

 

OSVM):

CPU(按时间切片,各级缓存(缓存当前程序数据),进程切换(保存现场、恢复现场))

内存(线性地址<--物理地址,通过空间映射完成,引入控制芯片还能起到保护内存的能力,芯片在翻译时发现空间不允许访问就访问不了,免得多进程间覆盖使用地址空间)

OS上最主要的虚拟就是CPURAM了,另外还有IO设备(disk,key,mouse,networkcard等)

 

3、若只有一个进程运行时IO设备就不需要虚拟了,谁要获得CPU的使用权通过中断CPU就给谁用了,谁有CPU的使用权焦点就在谁身上,CPU完成了切换IO就完成了切换

若在当前OS上又安装使用了虚拟机,问题就复杂了,CPU已切好片能让多个进程运行,在进程上又装了虚拟机(虚拟机上的OS运行时表现在前主机上就是个进程,除这个进程外当前主机还有其它进程),虚拟机上的CPU又有自己的管理机制,虚拟机的OS上运行的多个进程在管理时如中断、内存管理、CPU管理等都要由当前虚拟机完成管理,给这个虚拟机的CPU本来就是当前主机的OS虚拟出来的,虚拟机内部的多个进程切换时当前主机并不知道切换了没有

OS的内核必须要按CPU走过的时间来计时,虚拟机的内核获得CPU的时间不是完整的时间,它被调度过一次,但它认为它获得的时间是完整的时间,X86的机器设计就是运行一个OS的,而不适合在OS上再运行一个虚拟机(早期X86的架构是不适合运行虚拟机的),不过现在CPU支持硬件虚拟化,有这样的机制才能再次虚拟化,在当前主机OS上的进程的这个层次是已被虚拟过一次了,IO虚拟不需要专门去做,谁获得在CPU上运行通过中断就给那个进程,IO设备都是硬件设备,而真正的硬件控制只有内核才能实现,一旦有IO中断产生有IO交互一定由内核操作,再由内核转给进程

 

当前CPU只一个,要运行一个内核和多个进程,某时刻要么内核在CPU上运行,要么是某个进程在CPU上运行,只会是其中的一种情况,当内核在CPU上运行时叫内核模式,当进程在CPU上运行时叫用户模式;在内存中内核占据的空间叫内核空间,进程占用的空间叫用户空间

CPU有两种模式(内核模式和用户模式,区别在于,用户是不能直接控制硬件的)

CPU权限在CPU内部将指令划分为4层,ring0ring1ring2ring3ring0-3,环0到环3),最核心的ring0叫特权指令模式(内核模式),ring1ring2基于历史原因在现有的OS上没有用,linux上只用了ring0ring3ring3即用户模式,内核运行时能执行特权指令,而用户的进程只能运行的ring3用户模式上

若在OS上运行了虚拟机,这个虚拟机有自己的内核,这个内核若在ring3上则不能执行特权指令(不能将内部的用户程序执行并获取特定的外部资源),不在ring0上运行就不能向内部的用户进程申请资源,让虚拟机运行在限制模式下或使用特殊机制给虚拟机模拟出一层硬件,模拟emulation是仿真,通过软件模拟,让虚拟机上的OS认为这就是个真正的硬件,当要用到特权指令时,由当前主机的OS翻译到硬件上,虚拟机上的OS认为是运行在ring0上事实上是在ring3上运行的,这种虚拟化是完全虚拟化,虚拟机上的OS也能执行特权指令,最终要由真正主机上的OS内核审核才行,虚拟机内部执行的所有指令都要由当前主机的内核翻译,这样把一个计算机的指令用软件模拟,模拟完成后又要由内核翻译才执行,这会非常慢,虚拟机性能能有当前主机性能的六七成就不错了

若能将虚拟机直接运行在ring0上性能就会好很多,将一些敏感的指令不让虚拟机执行,但可让虚拟机直接运行在硬件上,给CPU再加一环(负1环)ring-1-1环由当前主机的内核来运行,虚拟机运行在ring0上,ring0上重要的部分特权指令已剥离,若虚拟机要运行重要的一些特权指令向当前主机的内核申请,硬件虚拟化重要的一点就是提供了ring-1,还有其它额外的机制

 

某一进程在CPU上运行,需要访问资源,资源在内存中,通过线性地址到物理地址映射,程序运行具有局部性,装载这个程序运行时可能只装载了一部分数据,但现在某个进程要额外加载一个文件,文件在辅存上,这就要与硬盘打交道,需要执行特权指令,向内核申请发起系统调用system call,一旦有system call进程就从CPU退出转为内核在CPU上运行(模式切换,用户模式-->内核模式),由内核负责将数据装载进物理内存再映射到进程地址空间(线性地址上),进程通过线性地址就能访问到内存物理地址空间中的数据,内核会为每一段IO准备缓冲区buffer,数据先从磁盘复制到内核的内存空间再复制到进程的地址空间,一旦到进程的内存空间映射关系就建立起来了,进程的请求就已满足,内核唤醒进程,内核退出,进程在CPU上执行后续的工作,OS上总是有多个进程在运行,有可能内核准备好数据了却发现轮不到这个进程运行了,要在进程队列中选一个进程来运行,选哪一个进程运行,ready状态的进程是随时可运行的进程(数据已准备好了),还有睡眠态进程sleelp分可中断睡眠interruptible sleelp(一个阶段的工作已结束下个阶段的工作还没来)和不可中断进程uninterruptible sleelp(内核正在准备数据还没加载完就算唤醒也不能正常工作)

使用公平的方法让进程能被轮流的在CPU上执行,进程有优先级,紧急的事件或关键性任务要先占用CPU运行,优先级高的被调度到CPU上执行的可能性就高且有长时间使用CPU时间的能力,进程除优先级还有先后次序,要有一种设计精良的算法(内核调度进程的算法用时间复杂度衡量)从进程队列中选择一个进程到CPU上运行,算法有标准衡量,符合O1)的算法就是优良(队列无论多长挑选花费的时间是固定的),若队列越长挑选的速度越慢则认为这个算法很差,2.6以上的kernel算法都是O1)的(需要适合的机制(按优先级)将进程排队)

如图:进程运行位置在Applications

wKiom1aqHfjiuzRHAAAk2NqiXNs314.jpg

linuxOS内核上,进程是通过双向链表来管理,表之间有次序,通过一个可找到下一个,进程间是靠一个独立的结构来管理,这个结构叫task_struct(由C语言描述的独立组织的数据结构),其中一个单个的结构也称进程描述符process descriptor,每个进程都有process descriptor,里面存的是进程的元数据(类似文件的元数据),每个进程的描述符间有关联性,在内核内部通过双向链表进行组织,在创建一个进程时首先就创建一个process descriptor并将其添加到此链表上,删除一个进程,process descriptor要被删掉,之后内核将不能追踪这个进程,创建一个进程除要给进程分配CPU、内存等资源外,还要在内核的内存空间中维护一个进程描述符文件,里面保存的有这个进程的所有相关信息,如图:

wKioL1aqHlDBZqu5AABldXyQ3UA234.jpg

stateready,stopped,sleep(interruptible sleep,uninterruptible sleep),running,zombile

现在的OS都是多任务多用户,多任务是在有限的CPU上超负荷运行多个进程,因此进程切换(CScontext switch上下文切换)不可避免,如CPU上进程A切至进程B,进程A将挂起suspend,各种在CPU内部维持的信息stack pointer,other registers都必须要保存下来(保存现场),保存在process descriptor中,process descriptor的文件大小是固定的,进程A切出去,进程B要调度上来(恢复现场)resume运行,将Bstack pointer,other registers等相关数据要装载到CPU并运行,一旦恢复则程序计数器将指向下一条指令,下一条指令将被装载并执行

wKiom1aqHhbjCGQqAAAxMkb9O7M185.jpg

CS由内核完成,也要花费时间,每次CS都要从用户模式转为-->内核模式再到-->用户模式,整体上CPU就分为两个时间,用户模式所占据的CPU时间和内核模式所占据的CPU时间,可使用top命令查看第三行显示,%us%sy,如:

Cpu(s): 0.0%us0.0%sy, 0.0%ni,100.0%id,  0.0%wa,  0.0%hi, 0.0%si,  0.0%st

正常情况下,内核模式不应占据过多的CPU时间,如web server的工作主要由web进程来完成,如果大量的时间耗费在内核模式下,很有可能CS、中断次数过多导致,对外提供的服务主要是用户空间提供的,这样在响应用户的请求会很慢,影响用户体验等

CS时间太短、太长都不好

linux支持进程抢占,如给进程A运行5msA刚上来没多久就来了进程BB的优先级比A高,B可把A挤下去,优先级高的可抢占CPU,若刚上来就被优先级高的其它进程抢占了这不合理,如果要是等A运行完了再抢那就不是抢了,并不是随时都能抢,而要根据内核内部的时钟频率产生的时钟中断来抢,每次的tick就有可抢的时钟中断,tick来了就能抢,如A进程可在CPU上运行5msOS的内核是100HZ,也就是10ms一次滴答,A可从容的运行完,若内核是1000HZ1ms一次滴答,则A刚上来1ms就被B抢占了,剩下的4ms等下次轮到时再上来运行,这会有问题,优先级高的始终在运行,优先级低的就饿死了

注:时钟频率(clock tick,时钟滴答,1s内的时间跨度,100HZ1s内滴答100次,也就是10ms滴答一次;1000HZ1ms滴答一次),1stick几次叫时间解析度,解析度越高tick越精准,优先级高的进程可抢占的机会也就越多,OS就是根据tick的次数来计算时钟的,如100HZtick100次就认为走了1slinux有两种时钟(硬件时钟;系统时钟),系统时钟就是靠tick实现

CPU足够快,clock tick的频率高无所谓,经验证clock tick快了性能未必好,redhat早期使用100HZ,后调为1000HZ发现不行又改回了100HZredhat6.4已实现了完全无tick的内核,tick less,若要进行tick的话,按100HZ计算,每秒100次的时钟中断必须要进行无论程序有无在运行,若在非常空闲的应用程序当中依然非常消耗电源,更重要的是有大量的时钟中断在进行(空载),引入的stick less可让system进入深度睡眠,要完全靠中断驱动interrupt driven实现(软中断(soft IRQ从用户模型切至内核模式)和硬中断),这样在系统非常空闲的情况下可节约电源不至于让CPU发热量过高等

 

linux进程类别class

交互式进程(通常是IO密集型的,例如编辑器大多数时间都用在等待IO上了)

批处理进程(通常是CPU密集型的,如守护进程,不需与管理员交互,有用户通过网络IO进来它就要响应)

实时进程(随时运行都要能够运行,如×××中的程序)

 

分配策略:

时间片短,优先级高;

时间片长,优先级低

 

linux优先级prority(调度就是根据优先级来的,实时优先级要比静态优先级要高):

实时优先级(1-99,数字越小,优先级越低,通常与内核相关或关键性任务相关)

静态优先级(100-139,数字越小,优先级越高,通常描述用户空间的优先级)

 

#top(第6行中,PR列中的RT表示实时优先级realtime20表示120NI表示nice value

  PIDUSER      PR  NI  VIRT RES  SHR S %CPU %MEM    TIME+ COMMAND

7090 root      20   0 15032 1200 952 R  0.7  0.5  0:04.35 top                                                                     

    7root      20   0    0    0    0 S 0.3  0.0   1:11.84 events/0 

 

[root@node1 ~]# ps -e -o class,rtprio,pri,nice,cmd-eSelect all processes-ouser-defined format自定义显示字段;classscheduling class of the process进程调度类别;rtpriorealtime priority

CLS RTPRIO PRI  NI CMD

TS      -  19   0 /sbin/init

TS      -  19   0 [kthreadd]

FF     99 139   - [migration/0]

 

linux内核的调度类别:

SCHED_FIFOFFfirst in first out,调度实时优先级1-99的进程)

SCHED_RRRRround robin,调度实时优先级1-99的进程)

SCHED_OTHERTS,调度静态优先级100-139用户空间的进程)

SCHED_IDLE

SCHED_BATCH

 

动态优先级:内核随时监控着SCHED_OTHER类别的进程,若有进程很长时间没运行,内核会临时性的调高它的优先级

dynamic priority = max ( 100, min ( static priority – bonus + 5, 139))

bonus的范围是0-10

 

手动调整优先级:

1100-139使用命令nicerenice调整nice值:

#renice NUM  PIDalter priority of running processes,调整已在运行的进程的优先级nice值,NUM-20~19对应100-139之间的数,默认0

#nice -n  NUM  COMMANDrun a program with modified scheduling priority启动某程序时就指定它的调度优先级,nice值)

21-139均可使用命令chrt调整:

#chrt -f|-r|-o  -p  NUM PIDmanipulate real-time attributes of a process,调整已存在进程的优先级;-f--fifoset scheduling policy to SCHED_FIFO-r--rrset scheduling policy to SCHED_RR (the default)-o--otherset policy scheduling policy to SCHED_OTHER-p--pidoperate on an existing PID and do not launch a new task

#chrt -f|-r|-o  NUM  COMMAND(启动某服务时指定优先级)

例:

#chrt -f  -p  50 1000(将PID1000的进程设定为SCHED_FIFO,优先级设为50

#chrt -o  -p  0  1000(将PID1000的进程设定为SCHED_OTHER,优先级设为0

#chrt -f  36  /bin/my-app(启动my-app时设定为SCHED_FIFO,优先级设为36

 

如何从一个进程队列中挑选出一个进程运行,策略?若是FIFO,要是该进程运行很长时间,如一年,其它进程将不能运行;若是RR,看上去公平,有些进程并不需要CPU很长时间(IO密集型的),有些进程需要CPU很长时间(CPU密集型的),这保证了结果公平,但起点不公平了(干多干少一样多,导致大锅饭);linux2.6的调度算法是按照优先级别将所有进程分成139个队列,每个优先级有两个队列,活动队列和过期队列,开始选择时扫描每个队列的首部,从991,再从100139,若某个队列中有进程则挑选出来运行,若某个进程在给它分配的CPU时间内没运行完则放到该优先级的过期队列中,等到活动队列中的运行完,则将两个队列调换(不是将过期队列的进程放到活动队列中),过期队列转为活动队列,活动队列转为过期队列,所以无论队列中的进程有多少个,只用扫描139次,以此达到O1)的性能

linux内核的进程调度器至关重要,必须要做出良好的决策,决定下一个进程该由谁在CPU上运行,而且要尽可能保证公平,linux2.6.18后是CFQ(或CFScomplete fair queue|scheduler),保证能让每个进程都能运行,主要用来实现SCHED_OTHER类别的进程(SCHED_OTHER类别的进程使用CFQ进行调度),CFQ适合desktop不适合server

 

进程地址空间:从上至下低地址到高地址,text segmentdata segmentheap segment(低地址到高地址,打开的文件等),stack segment(高地址到低地址,本地变量、函数参数、返回地址等)

wKioL1aqIYWwW3bTAABNHIa_Sqk355.jpg

 

linux中如何创建一个新的进程?linux上每个进程都由父进程生成,init这个进程由kernel生成,之后所有的进程都由init进程通过fork诞生(或init的子进程诞生),每个进程都有父进程除init

fork()是个system call,用来创建新的子进程,每个进程只要调用fork()就能创建一个新的子进程

由内核完成创建一个进程,主要是创建process descriptortask_struct),完成后还需要memory spacepidppid等,创建的新进程的地址空间指向位置与父进程的地址空间指向相同,都是同一个位置,与父进程的资源一模一样,一旦父进程或子进程需要修改内容时就要有自己的地址空间(COWcopy on write写时复制),这时就分家了,这可大大降低系统开销(创建的新的子进程开始是与父进程内存空间一致,而不是创建新的地址空间,只有当需要写操作时地址空间才分开),很多子进程只是执行操作完后就退出了不需要修改额外的数据,如果频繁的创建进程撤销进程也需要资源,如httpdprefork模型,响应一个用户就要创建一个进程响应,当用户断开连接进程又要销毁,来一批用户创建-->销毁,再来一批再创建-->再销毁,这本身就需要很大的系统开销

 

4SMPsymetric multi processing对称多处理器:

对于多颗CPU,一块主板上有多个CPU插槽socket,例如有4块,每颗CPU都是4核心的,则该主机共16核;当有4CPU时它们访问的是同一内存(内存只一个,内存地址空间仅一个),当多颗CPU访问同一资源时产生资源竞争(临界区),解决要有仲裁机制,一颗CPU完成一次内存访问时钟周期有3个如下图(1、联系内存控制器,向内存控制器传输一个寻址指令,内存控制器并返回一个寻址指令,内存地址的编址是由内存控制器完成的;2、找到内存存储单元,取得内存的地址后施加一定的请求机制rw3、完成rw的操作)

注:CPU快的离谱,在2.3GHZCPU上,大部分简单指令的执行只需要一个时钟周期(1/3纳秒,即使是真空中传播的光,在1/3纳秒的时间里也只能走250px4英寸),把这个数字记住,在做程序优化时要能想到,执行指令的开销在当今的CPU上是多么微不足道

若第1CPU正在与内存控制器打交道,内存控制器正忙则不能与其它的CPU交互,这意味着并非CPU颗数越多性能越好,因为内存只一个,要访问内存的资源就有竞争了,若只两颗CPU则争用的机会要比四颗就小的多了,在SMP机制中内存只一个使得性能提升有限,原因:1、存在内存资源争用;2、内存总线带宽也是一方面

wKiom1aqIX3wYV8HAAAyrp3prg0466.jpg

NUMAnon uniform memory access architecture非一致性内存访问,每颗CPU中的每个核心都有自己专用的内存访问区域;专用的并不是不允许别人用只是对自己来说是主要的访问区域,对其它人是次要访问区域;每核CPU在访问它自己的本地存储器的速度比非本地存储器的速度要快):

如上图:每核CPU + 每核的memory controller + 该核可访问的内存区域称作一个node

1CPU每颗2核,每颗CPU2个核心是共享三级缓存的,三级缓存也是资源争用区域(临界区),但三级缓存是在同一颗CPU上比内存要快的多,若这颗CPU的两核分别为CPU0CPU1CPU0CPU1共用三级缓存,它们访问三级缓存的速度要比访问内存要快,三级缓存虽存在资源争用但影响较小

每核心CPU都有自己独有的内存访问区域,每核心的CPU也有自己的内存控制器memory controller,假如将内存区域分成两段,每核CPU使用自己的那段,这整个内存是系统级别的,内核在装载时,这两段区域都有可能要用到,CPU0访问的数据有可能在它的L1L2L3和它的内存段,也有可能在CPU1的内存段中,若在CPU1的内存段中,则要通过它自己的memory controller再到CPU1memory controller从而取得数据

2核心的CPU进程调度就不是一个队列了,每核CPU都有自己的队列,这两个队列会不断的被内核所平衡,如有200个进程,CPU0上分100个,CPU1上分100个,过一会,CPU0上有90个已退出,而CPU1只退出了10个,这样一个CPU忙死了一个CPU闲死了,避免这种情况内核会重新平衡rebalancing,从CPU1上分40个给CPU0,那这40个进程的数据呢,这时CPU0就需要访问CPU1的专用内存

NUMA架构在逻辑上遵循SMP架构,SMP架构对于内存是共享的;而NUMA架构上每核CPU都有自己的内存(且有自己的memory controllermemory controller很有可能就在CPU内部与cpu socket的位置很近),访问另一颗CPUmemory controller要跨越插槽、总线,要比访问自己的memory controller要慢,若访问自己的内存要3个时钟周期,访问别人的至少要6个时钟周期,NUMA架构保证每核CPU尽量只访问自己的内存而不访问别人的,内核会经常去平衡这两核CPU,进程会在这两核的CPU上转换,切换就意味着内存要交叉访问了,一旦出现这种情况性能下降再所难免,解决方法:禁止内核rebalance

对于非常繁忙的进程,如批处理进程、服务进程,可使用cpu affinityCPU绑定,姻亲关系),服务启动时就绑定在某颗CPU的核心上,这样就能在绑定的这个核心的内存上被访问,避免内存交叉访问

有些时候仍然要使用reblancing,否则一颗CPU很闲另一颗CPU很忙,这样总体性能仍然是下降的,所以必须要找一个平衡点

 

 

二、操作:

环境:

[root@node1 ~]# uname -a

Linux node1.magedu.com2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64GNU/Linux

 

1、在NUMA架构下,如果内存本身访问的次数非常少时使用,开启numad守护进程:

[root@node1 ~]# numa<TAB>

numactl   numad    numademo  numastat

 

#numastatShow per-NUMA-node memory statistics for processes and the operating systemnuma_hit在自己的内存找到的数据,numa_miss此项数据很大时就需要进程绑定至CPUnuma_foreigh被其它CPU访问到了)

 

#numastat -p  PIDShow per-node memory allocation information for the specified PID or pattern

 

#numastat -sNODE_NUMBERSort the table data in descending order before displaying it, so the biggest memory consumers  are listed first

 

numactlControl NUMA policy for processes or shared memory--showShow NUMA policy settings of the current process

 

#numactl --cpunodebind=NODES  COMMAND(或#numactl  -N NODES  COMMANDOnly  execute command  on the CPUs of nodes在某进程运行时,只访问自己node的内存,而不访问其它node的内存,这样找自己node内存时没有则不去找其它node的内存,直接让内核加载了,其它内存上有也用不上,之后若再次访问自己node中的内存就有数据;NODES的表示有all表示所有nodenumbernumber1,number2number1-number2

 

#numactl --physcpubind=CPUS  PROCESS_NAME(或#numactl  -C CPUS  COMMANDOnly execute process on cpus,正在运行的进程与某个CPU绑定)

 

#numactl --showShow NUMA policy settings of the current process显示当前使用的策略)

 

#service numad  start|stop(守护进程,能自动地将进程与某颗CPU绑定,自我监控并优化管理,若主机CPU支持NUMA架构,将numad启动起来是很有必要的,启动时也可设定监控哪些进程,在必要时还可停止,可指定观察级别(多久观察一次)和调整级别,经测试在一个非常繁忙的server上启动numad可让性能提升50%

 

[root@node1 ~]# numactl --show

policy: default

preferred node: current

physcpubind: 0

cpubind: 0

nodebind: 0

membind: 0

 

[root@node1 ~]# numastat

                           node0

numa_hit                 1051533

numa_miss                      0

numa_foreign                   0

interleave_hit             12506

local_node              1051533

other_node                     0

注:

numa_hit is memory successfully allocatedon this node as intended

numa_miss is memory allocated on this node despite the process preferring some different node. Each numa_miss has anuma_foreign on another node

numa_foreign is memory intended for thisnode, but actually allocated on some different node

 

[root@node1 ~]# numastat -s0

Per-node numastat info (in MBs):

                          Node 0           Total

                 --------------- ---------------

Numa_Hit                 4186.95         4186.95

Local_Node               4186.95         4186.95

Interleave_Hit             48.85           48.85

Numa_Foreign                0.00            0.00

Numa_Miss                   0.00            0.00

Other_Node                  0.00            0.00

 

[root@node1 ~]# pgrep nginx

8100

8102

[root@node1 ~]# numastat -p 8100

Per-node process memory usage (in MBs) forPID 8100 (nginx)

                           Node 0           Total

                  ------------------------------

Huge                         0.00            0.00

Heap                         0.29            0.29

Stack                        0.02            0.02

Private                      0.55            0.55

----------------  --------------- ---------------

Total                        0.86            0.86

 

2、对于非NUMA架构,专门将进程与CPU绑定:

命令tasksetretrieve or set a process’s CPU affinity

方式一:

#taskset -p  MASK  PID-p--pidoperate onan existing PID and not launch a new task,绑定某个进程至CPU上,用MASK掩码的方式来引用CPU

MASK用十六进制字节掩码表示:

0x00000001(表示第0CPU

0x00000003(表示第0号和第1CPU3换算成binary11,从右往左位数若是1相对应的就是从第0号到第N号)

0x00000005(表示第0号和第2CPU5binary101

0x00000007(表示第0号、第1号和第2CPU7binary111

0xFFFFFFFF(表示所有CPU,从0号到31号)

举例:

#taskset -p  0x00000004  8200(将8200这个进程绑定在第3CPU上)

#taskset -p  0x00000003  8200(将8200这个进程绑定在第0号和第1CPU上)

 

方式二:

#taskset -p  -c  CPU_LIST PID-c--cpu-listspecify a numerical list of processors instead of a bitmaskCPU_LIST可以是012等单个数字表示第012CPU,也可以用列表表示离散的或连接的多个,如0,5,7,9-11

举例:

#taskset -p  -c  3  8200(将8200这个进程绑定在第3CPU上)

#taskset -p  -c  0-2,7 8200(将8200这个进程绑定在第0号、1号、2号、7CPU上)

注:server一般很少关机,用以上命令直接绑定可以,但重启会失效,若写成脚本也不方便,重启后PID号很有可能会不一样,脚本文件还要改,所以nginx的配置文件中有选项可直接设定)

举例:

[root@node1 ~]# taskset -p -c 0 2408

pid 2408's current affinity list: 0,1

pid 2408's new affinity list: 0

 

3restrict length of acpu run queue

2中实现了将进程绑定至某颗CPU上,但同时这颗CPU还会运行其它的进程,没有绑定的进程仍会被调度到这颗CPU上,这颗CPU上仍然有CS,若当前主机的4CPU4核心共16核,可留出2核心供内核用(将这2个核心从进程队列中隔离出来),剩下的14核心专门用于运行进程(使用taskset将进程与CPU绑定),以上两步并不能保证某核CPU专属于某个进程,CPU还要处理中断,还要将中断隔离,使得这14个核心的CPU只运行进程,另外2个核心只运行内核和中断,这样进程绑定的某个核的CPU再也不用切换了

1isolate a cpu from automatic scheduling in /etc/grub.conf

isolcpus=CPU NUMBER1,CPU NUMBER2,...,CPUNUMBER(计算机启动后内核不会让进程使用这个列表中的CPU,以此将此处的CPU预留出来仅运行内核)

2pin tasks to that cpu with taskset(使用taskset将进程与CPU绑定)

3consider moving IRQs off the cpuIRQinterrupt request

echo CPU_MASK  >  /proc/irq/<irq_number>/smp_affinity(实现某个中断与某核CPU绑定,应该将中断绑定至隔离的CPU核心上,从而避免那些非隔离的CPU处理中断程序;注意手动绑定IRQCPU之前要将irqbalance这个服务关掉#service  irqbalance  stop,这个服务用来自动绑定和平衡IRQ的)

 

#cat  /proc/interrupts(不同的设备有自己的IRQ号码,一个设备可有多个IRQ号码)

#cat /proc/interrupts | grep  -e  “CPU\|eth0”

#cat  /proc/irq/default_smp_affinity”irq的默认smpaffinity

#cat /proc/irq/0/smp_affinity(显示ffffffff表示0IRQ可运行在所有CPU上,若将中断绑定至第0号和第1CPU其它将不处理此中断)

 

对于NUMA架构,使用#numastat查询,命中率低的需要将进程与CPU绑定;在非NUMA架构环境中,进程绑定CPU的情形:CPU核心很多,某个对外提供的服务非常繁忙,而且该服务在多个CPU上来回切换,CS率过高,导致响应用户请求过慢

对于网络非常繁重的情况下:(1)对于文件服务器、高流量的web server这样的应用来说,把不同的网卡IRQ均衡绑定到不同的CPU上将会减轻某个CPU的负担,提高多个CPU整体处理中断的能力;(2)对于数据库server这样的应用来说,把磁盘控制器绑定到一个CPU,把网卡绑定到另一个CPU将会提高数据库的响应时间,优化性能;合理的根据自己的生产环境和应用特点平衡IRQ中断请求有助于提高系统的整体吞能力和性能

 

4viewing cpuperformance data

wKioL1aqJDyyuOf6AAC7HToRek8321.jpg

1load average:average length of run queuesconsiders only tasks in TASK RUNNABLE and TASK UNINTERRUPTABLE平均负载,考虑运行的进程和不可中断进程

相关命令有:

#topdisplay Linux tasks

#wShow who is logged on and what they are doing,并有top显示的第一行)

#uptimeTell how long the system has been runningtop显示的第一行)

#vmstat 1  5Report virtual memory statistics,1表示delay5表示count,显示的有procs,memory,swap,io,system,cpu这几个方面的信息)

#sar -qCollect, report, or save system activity information-qReport queue length and load averages

#rpm -qf  `which  sar`sysstat-9.0.4-20.el6.x86_64,这个包重要,安装了很多命令)

#rpm -ql  sysstat/usr/bin/{iostat,mpstat,pidstat,sar}常用,/usr/lib64/sa/{sa1,sa2}很独特用于生成文件并分析过去的执行情况,如#vmstat  1  5只对当下的CPU负载进行采样;而sar  -q是对过去某个时间到现在每隔10min的负载情况;另#sar  -q 1  5也可实时采样,1delay5count

#ab -n  100000  -c 500  http://127.0.0.1/index.html(试压力测试,在另一终端查看负载情况)

#sar -q

 

2CPU utilizationCPU使用率):

#mpstat 1  2Report processors related statistics1表示interval2表示count

举例:

#mpstat -P  0  1  5Indicate the processor number for which statistics are to be reported0表示第0CPU显示的状态,1表示每秒采样,5表示共采样次数,显示的字段有%usr,%nice,%sys,%iowait,%irq,%soft,%steal,%guest,%idle

#mpstat -P  ON|ALL  1  5ON表示every online processorALL表示all processors

#mpstat -I  SUM|CPU|ALL  1  5-I表示report interrupts statistics CPU对中断的处理;SUM表示reports the total number of interrupts per processor使用字段CPUintr/s显示;CPU表示the number of each individual interrupt received per second by the CPU  or CPUs  is displayedALL表示SUMCPU都显示)

 

#sar -P  0  1  2(第0CPU使用状态,字段有%user,%nice,%system,%iowait,%steal,%idle

#sar -P  ALL  1  2(每个独立的CPU使用状态each individual processor

 

#iostat -c  1  2Report Central Processing Unit (CPU) statistics and input/output statistics for devices,partitions and network filesystems (NFS)-c表示Display the CPU utilization report,显示的字段有%user,%nice,%system,%iowait,%steal,%idle;若%iowait时间过长要检查IO状态,可能是磁盘性能瓶颈;%system时间过长,则是内核占用CPU时间过长,则提供服务的进程则占用CPU的时间短)

#iostat -d  1  2Display the device utilization report

 

#cat /proc/stat(很多命令都是从此文件提取的信息,并以用户直观的方式打印)

 

#dstat 1  2versatile tool for generating system resource statistics,显示的项目有total-cpu-usage,dsk/total,net/total,paging,system

#dstat --top-cpu  1  10show most expensive CPU process

#dstat --top-io  1  10show most expensive I/O process

#dstat --top-mem  1  10show process using the most memory

#dstat --top-cpu  --top-io  --top-mem 1  10(可同时使用)

#dstat -c  1  5--cpuenable cpu stats (system, user, idle, wait, hardware interrupt, software interrupt)

#dstat -d  1  5--diskenable disk stats (read, write)

#dstat -i|-l|-m|-net|-s|-y|--fs|--tcp|--udp 1  5-i,interrupt;-l,loadaverage;-m,memory;-net,network;-s,swap;-y,system等)

 

查看上下文切换CS

#vmstat 1  2Report virtual memory statistics

#sar -w  1  2Report task creation and system switching activity,字段有proc/scswch/s,进程创建的平均值和上下文切换的平均次数)

注:CS过多,则进程数过多;若中断次数过多,则外部硬件非常繁忙,通常作为网络server中断次数多算正常

 

5scheduler domains调度器域:

group processors into cpusets1each cpuset represents a scheduler domain2supports both multi-core and NUMA architectures3simple management interface through the cpuset virtual file system

the root cpuset contains all system resources

child cpusets1each cpuset must contain at least one CPU and one memory zone2child cpusets can be nested3dynamically attach tasks to a cpuset

consequences1control latency due to queue length ,cache,and NUMA zones2assign processes with different CPU characteristcs to different cpusets3scalable for complex performance scenarios

scheduler domainFS结构类似,它把CPU组成了倒状的树结构,所有CPU都属于根域,在根域中可划分域,域还可划分子域,若让某个进程运行在根域下也就是运行在所有CPU

事先将CPU划分成组,使用类似taskset命令这样的功能将某个进程绑定在某个组上,而不是绑定在特定的某颗CPU的某个核心上

对于NUMA架构,进程运行,除CPU外还有内存,划分时不仅要将CPU要归类还要将内存归类(第0CPU要带上第0号内存;第2CPU要带上第2号内存);对于非NUMA架构,内存只一段

configuring the root cpuset

1create a mount poing at /cpusets

2add an entry to /etc/fstab

cpuset  /cpusets   cpuset  defaults  0  0

3mount the filesystem to automatically create the cpuset

注:all CPUs and memory zones belong to the root cpuset

all existing PIDs are assigned to the rootcpuset

 

configuring a child cpuset

1create a sub directory of the root cpuset

#mkdir /cpusets/magedu

2assign resources as a range or comma separated list

#echo 0  >  /cpusets/magedu/cpus

#echo 0  >  /cpusets/magedu/mems

3attach one task at a time

#echo `pidof  PROCESS`  > /cpusets/magedu/tasks

4persist in /etc/rc.local

 

举例:

[root@node1 ~]# mkdir /cpusets

[root@node1 ~]# vim /etc/fstab

cpuset         /cpusets        cpuset defaults   0   0

[root@node1 ~]# mount –a

[root@node1 ~]# mount | grep cpuset

cpuset on /cpusets type cpuset (rw)

 

[root@node1 ~]# ls /cpusets

……

[root@node1 cpusets]# cat cpus(显示根域下的所有CPU,此虚拟机是1颗双核)

0-1

[root@node1 cpusets]# cat mems(关联到哪些内存)

0

[root@node1 cpusets]# cat tasks(进程有哪些)

……

[root@node1 cpusets]# mkdir domain1(重启后此目录将不存在,仅存在自动生成的)

[root@node1 cpusets]# cd domain1/

[root@node1 domain1]# ls

……

[root@node1 domain1]# cat cpus

 

[root@node1 domain1]# cat mems

 

[root@node1 domain1]# cat tasks

[root@node1 domain1]# service httpd start

正在启动 httpd                                           [确定]

[root@node1 domain1]# ps -e -o psr,pid,cmd| grep httpd

……

 0  2415 /usr/sbin/httpd

 0  2416 /usr/sbin/httpd

[root@node1 domain1]# echo 0 > cpus

[root@node1 domain1]# echo 0 > mems

[root@node1 domain1]# echo 2416 > tasks

[root@node1 domain1]# cat cpus

0

[root@node1 domain1]# cat mems

0

[root@node1 domain1]# cat tasks

2416

[root@node1 domain1]# ab -n 100000 -c 1000 http://127.0.0.1/index.html

[root@node1 ~]# watch -n 1 'ps -e -o psr,pid,cmd | grep http'(在另一窗口执行监控,查看指定的2416进程是否有CPU的切换,其它大多进程都有切换此进程没有)

 

CPU调优:观察CPU负载、使用率等各种统计数据(中断数、CS数、%usr%sys%io等都很关键),正常用户空间与内核空间的比例是7:3CPU的使用率不要超过80%,如果长时间超过80%极有可能出现性能瓶颈,因为太繁忙会在某一时刻挂掉;多核CPU的主机在非常繁忙的时刻,CPU使用率可能会达到百分之好几百,甚至百分之七八百,按具体情况而定;若CS过多,某个服务非常繁忙却总在切换,可实现进程与CPU绑定,在NUMA架构上要将进程绑定在某个nodeCPU某核心+可操作的内存段)上,进而实现只在当前node运行,而且数据也在当前node装载,对于非NUMA架构,手动绑定进程到某核CPU上(使用tastsetcpusets文件系统)

 

 


[root@VM_137_48_centos ~]# vmstat 1 5

procs -----------memory---------- ---swap-------io---- --system-- -----cpu-----

 r b   swpd   free  buff  cache   si  so    bi    bo  in   cs us sy id wa st

 1  0277996 150600 106576 9980436    0    0   15    28    0   0  2  1 97 0  0   

 0  0277996 168920 106556 9960892    0    0    0     0 6180 23583  2  198  0 0    

 0  0277996 168036 106556 9962096    0    0    0     0 12684 37287  4  294  0 0 

 8  0277996 167376 106560 9962220    0    0    0    20 6362 23786  2  197  0 0    

 0  0277996 167252 106560 9962352    0    0    0     0 5550 22901  3  196  0 0

FIELD DESCRIPTION FOR VM MODE

   Procs

      r: The number of processes waiting for run time.

      b: The number of processes in uninterruptible sleep.

   Memory

      swpd: the amount of virtual memory used.

      free: the amount of idle memory.

      buff: the amount of memory used as buffers.

      cache: the amount of memory used as cache.

      inact: the amount of inactive memory. (-a option)

      active: the amount of active memory. (-a option)

   Swap

      r: The number of processes waiting for run time.

      b: The number of processes in uninterruptible sleep.

   Memory

      swpd: the amount of virtual memory used.

      free: the amount of idle memory.

      buff: the amount of memory used as buffers.

      cache: the amount of memory used as cache.

      inact: the amount of inactive memory. (-a option)

      active: the amount of active memory. (-a option)

   Swap

      si: Amount of memory swapped in from disk (/s).

      so: Amount of memory swapped to disk (/s).

   IO

      bi: Blocks received from a block device (blocks/s).

      bo: Blocks sent to a block device (blocks/s).

   System

      in: The number of interrupts per second, including the clock.

      cs: The number of context switches per second.

   CPU

      These are percentages of total CPU time.

      us: Time spent running non-kernel code. (user time, including nice time)

      sy: Time spent running kernel code. (system time)

      id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time.

      wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.

      st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.

 

 

 

[root@VM_137_48_centos ~]# sar -q 1 5

Linux 2.6.32-504.el6.x86_64(VM_137_48_centos)       20160906        _x86_64_          (8CPU)

160818   runq-sz plist-sz   ldavg-1   ldavg-5 ldavg-15

160819         4      479      0.31      0.24     0.19

160820        10      479      0.29      0.24     0.19

160821         0      479      0.29      0.24     0.19

160822         3      479      0.29      0.24     0.19

160823         0      479      0.29      0.24     0.19

平均时间:           3       479     0.29      0.24      0.19

      -q     Report queue length andload averages. The following values are displayed:

              runq-sz

                     Run queue length (numberof tasks waiting for run time).

              plist-sz

                     Number of tasks in thetask list.

              ldavg-1

                     System load average forthe last minute.  The load average iscalculated as the average number of runnable or running tasks (R state), andthe number of tasks in uninterruptible sleep (D  state)

                     over the specifiedinterval.

              ldavg-5

                     System load average forthe past 5 minutes.

              ldavg-15

                     System load average forthe past 15 minutes.

 

 

 

[root@VM_137_48_centos ~]# sar -r 1 5

Linux 2.6.32-504.el6.x86_64(VM_137_48_centos)       20160906        _x86_64_          (8CPU)

 

162204 kbmemfreekbmemused  %memused kbbuffers  kbcached kbcommit   %commit

162205    166632 16165112     98.98    106740  9964828   9232144     50.10

162206    166508 16165236     98.98    106748  9964944   9232144     50.10

162207    165640 16166104     98.99    106752  9965852   9232144     50.10

162208    165392 16166352     98.99    106752  9966292   9232144     50.10

162209    164672 16167072     98.99    106752  9967112   9232144     50.10

平均时间:      165769  16165975    98.98    106749   9965806  9232144     50.10

    -r     Report memory utilization statistics.  The following values are displayed:

             kbmemfree

                     Amount of free memoryavailable in kilobytes.

              kbmemused

                     Amount  of used  memory  in kilobytes. This does not take into accountmemory used by the kernel itself.

              %memused

                     Percentage of used memory.

              kbbuffers

                     Amount of memory used asbuffers by the kernel in kilobytes.

              kbcached

                     Amount of memory used tocache data by the kernel in kilobytes.

              kbcommit

                     Amount of memory inkilobytes needed for current workload. This  is  an estimate  of  how much RAM/swap is needed to guarantee that there never is out of memory.

              %commit

                     Percentage  of memory  needed  for current  workload  in relation to the total amount of memory(RAM+swap).  This number may be greaterthan 100% because the kernel usually overcommits  memory.

 

 

[root@VM_137_48_centos ~]# sar -w 1 5

Linux 2.6.32-504.el6.x86_64(VM_137_48_centos)       20160906        _x86_64_          (8CPU)

165141    proc/s  cswch/s

165142      0.00 23810.20

165143      0.00 24069.00

165144      0.00 23485.71

165145      0.00 21990.10

165146      0.00 24050.00

平均时间:        0.00  23474.34

      -w     Report task creation andsystem switching activity.

              proc/s

                     Total number of taskscreated per second.

              cswch/s

                     Total number of contextswitches per second.

 

 

减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。

无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。

CAS算法。JavaAtomic包使用CAS算法来更新数据,而不需要加锁。

使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。

协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

 

 

 

改文件描述符:

#ulimit -n  #(默认1024

#ulimit -HSn 65535

#vim /etc/security/limits.conf

* soft nofile 65535

* hard nofile 65535

 

redis优化项:

[root@server1 redis-3.0.7]# vim/etc/sysctl.conf   #/proc/sys/net/core/somaxconn,监听队列的长度,对于一个TCP连接,ServerClient需要通过三次握手来建立网络连接,当三次握手成功后,端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了,每一个处于监听Listen状态的端口,都有自己的监听队列,对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了,大多数环境这个值建议增加到 1024 或者更多,服务进程(httpdsendmail)会自己限制侦听队列的大小(例如 sendmail(8),常常在它们的配置文件中有设置队列大小的选项,大的侦听队列对防止拒绝服务DoS ***也会有所帮助;/proc/sys/vm/overcommit_memory,默认0(启发式过量,当userspace请求更多的内存时,内核尝试估算出剩余可用的内存),1(内核允许过量使用内存,直到用完为止,主要用于科学计算),2(内核会用一个决不过量使用内存的算法设定,即系统整个内存地址空间不能超过swap+总内存的50%50/proc/sys/vm/overcommit_ratio的默认值,overcommit_memory2时要结合overcommit_ratio设定)

net.core.somaxconn = 1024

vm.overcommit_memory = 1

[root@server1 redis-3.0.7]# sysctl -p

……

#echo never > /sys/kernel/mm/transparent_hugepage/enabled   #redis3.0.7需调整,否则启动时会警告)

#cat/sys/kernel/mm/transparent_hugepage/enabled

always madvise [never]

#vim /etc/rc.local

echo never >/sys/kernel/mm/transparent_hugepage/enabled

 

 

# cat /proc/net/sockstat   #socket状态)

sockets: used 483   #(已使用的所有协议套接字总量)

TCP: inuse 17 orphan 10 tw 1590 alloc 317mem 116   #inuse正在使用(监听)的tcp套接字数量,其值<=#netstat-tnl | grep ^tcp | wc -lorphan无主的(不属于任何进程)tcp连接数,无用待销毁的tcp socket数;tw等待关闭的tcp连接数,其值=#netstat -atn | grep TIME_WAIT | wc -lalloc已分配(已建立、已申请到sk_buff)的tcp套接字数量,其值=#netstat-atn | grep ^tcp | wc -lmem套接字缓冲区使用量,单位不详,用scp实测速度在4803.9kB/S时,其值=11#netstat -atn中相应的22portRecv-Q=0Send-Q约等于400

UDP: inuse 8 mem 2

UDPLITE: inuse 0

RAW: inuse 0

FRAG: inuse 0 memory 0   #(使用的IP段数量)

 

 

 

以上是学习《马哥运维课程》做的笔记。