本文主要是《Linux内核设计与实现》这本书的读书笔记,这本书我读了不下十遍,但依然感受囫囵吞枣。我结合本身的理解,从这本书中整理出了一些运维应该了解的内核知识,但愿对你们可以有所帮助。另外,推荐你们读下这边书,这本书主要讲内核设计、实现原理和方法,有利于理解内核的一些机理。linux
目录程序员
运维为何要了解内核算法
进程编程
系统调用缓存
中断安全
内核同步服务器
定时器和时间管理网络
内存分配数据结构
虚拟文件系统并发
块I/O层
I/O算法
页高速缓存和页回写
关于内核的几个概念
1、运维为何要了解内核
运维为何要了解内核
大神Linus说了解内核的方法就是阅读源码(*Read The Fucking Source Code*),可是linux内核学习曲线公认的陡峭,对于运维来讲难度很是大,并且现代Linux已经很是庞大,别说运维了,就是专门从事Linux内核开发的人,也不可能了解到内核的所有代码。
可是运维应该了解内核的工做原理,设计哲学,了解CPU、网络的调度方法,了解内存、文件系统的结构。
了解了Linux系统如何工做,咱们才能更好的使用它,让它为咱们服务。
Linux的由来
内核为何吸引人,很重要的一个缘由是自由精神,能够随手拿到源码,只有愿意,能够了解到每一个功能很是细微的地方。
Linux内核是如何来的,1991年,芬兰的大学生Linus热衷于使用Minix,一种教学用的Unix系统,可是他不能随意修改和发布该系统的源代码,这令他对这个系统的设计理念感到失望,因而就本身在386上设计了一款系统,并发布到了互联网上,很快就流行了起来。
顺便说下,Linux的吉祥物为何是企鹅,那是由于Linus小的时候,被一只企鹅咬过,令他印象深入。关于Linus还有一本书,叫作《只是为了好玩--linux之父林纳斯自传》,你们有兴趣能够阅读下。
我这里有一些数据,来自2017年度Linux内核开发者报告,经过这些数字,你们对目前的内核生态会有简单的了解。
目前,已有超过1400家公司的15600名开发人员参与了Linux内核的开发。仅就2016年到2017年,超过500家公司的4300多名开发人员对内核作出了贡献;其中有1670个开发者是第一次贡献,约占贡献者的三分之一。
2017年度,赞助Linux内核开发的十大组织包括英特尔、Red Hat、Linaro、IBM、三星、SUSE、谷歌、AMD、Renesas和Mellanox。
Linux开发的速度继续增长,参与开发的人员和公司的数量也在不断增长。内核每小时的平均变化量为8.5,比2016年报告中的7.8个变化显著增长,这意味着天天有204个变化,每周超过1400个变化。
从2016年的66天开始,平均每一个版本的开发天数从去年的66天增长到67.66天,每个版本的间隔时间分别为63或70天,提供了显著的可预测性。4.9和4.12开发周期的特色是,在内核项目历史上看到的最高补丁率。
未领取薪酬的开发者可能正在趋于稳定,这些开发者贡献了8.2%的贡献,比去年的7.7%有所增长。这一数字仍远低于2014年的11.8%。这多是因为内核开发人员短缺,致使那些有能力提交必定质量补丁的人,在找到工做时没有困难。
新加入内核开发的前三名是英特尔、谷歌、华为,其中华为投入33名工程师。
Linux内核的设计哲学
Linux内核设计参考了Unix,而且兼容Unix API,可是Linux内核吸取了Unix系统的优势,摒弃了一些缺点。
先来了解一个概念,单内核和微内核。
单内核是总体单独的一个过程,存储方式每每也是一个大的二进制文件,使用的也是连续的一整块内存。全部服务都运行在内核态,内核之间的通讯就很容易,内核能够直接调用函数。
微内核是按照功能划分为多个独立过程,这个过程叫作服务器,只有少数特权服务的服务器才运行在特权模式下,大部分服务运行在用户空间。大部分服务都使用本身的内存地址,不可能像单内核那样直接调用函数,而是要经过消息传递,系统采用进程间通信的机制,专业术语叫IPC机制。这样的好处是一项服务失效,并不会影响到其余服务,由于彼此隔离。
由于IPC机制的开销多用于函数调用,有大量的内核空间和用户空间的上下文切换,所以,消息传递须要必定的周期,而单内核就没有这个问题。
这样还形成一个结果,就是实际上,微内核为了提升效率,会让大部分服务位于内核态。
Windows NT内核系统,包括Windows7 Windows10 Windows Server系列,MacOS都是典型的微内核系统。
前段时间,华为推出的鸿蒙系统,也宣称是微内核系统。
Linux系统是单内核系统,也就是说Linux系统运行在单独的内核地址空间上,不过Linux吸收了微内核的精华,引入了模块化设计,抢占式内核,支持内核线程,及动态装载内核的能力。同时还避免了微内核设计上的性能损失。
可见Linux的设计哲学是实用主义优先。
再解释下什么是内核抢占,抢占指的是内核具备容许在内核运行的任务优先执行的能力,大部分Unix系统是不支持这个能力的。
再来介绍下内核的版本,内核有两种版本,稳定版和开发版,稳定版有工业级的强度,能够普遍部署,开发版主要用于实现新的功能。
Linux内核经过简单的命名机制区分稳定版和开发版,使用3个或者4个点分隔数字,表明不一样的版本,第一个数字是主版本号,第二个数字是从版本号,第三个数字是修改版本号,第四个数字是可选,是稳定版本号。从第二个数字能够看出是稳定版仍是开发版,若是是偶数就是稳定版,若是是奇数就是开发版。
好比内核版本2.6.26.1就是稳定版,由于它的第二个数字是6,是偶数。内核版本4.9就是开发版,由于9是奇数。
2、进程
先来聊聊Linux内核开发,内核开发和普通应用开发有两个地方不同:
本身要管理内存,普通应用跑在内核之上,内核能够帮你管理内存,可是你本身就是内核,你必须本身作好内核管理,要不很容易就内核溢出了。
没有库文件,普通应用程序有不少库文件能够调用,内核开发则没有,内核开发就是标准的C。
由此看见,作内核开发仍是要对内核有深入的理解才能够,请注意,这里的内核开发指的是内核核心功能的开发。
咱们再来看看进程,进程简单的讲,就是运行中的程序,我我的理解,进程是一种生命形式,就像一我的的生命,从呱呱坠地开始一直到生命的终结,中间须要不停的从周围的环境吸取资源,而且对环境也施加影响。
进程须要的资源就是CPU、内存、文件、网络等资源,进程虽然是从程序文件开始,可是不等于程序文件,一个程序文件能够启动多个进程,一个进程也多是由多个程序文件产生的,因此进程是一种运行中的状态。
内核用一个双向循环链表来描述进程的状态,这一链表在32位的机器上是1.7KB大小,链表中的每一项都是类型为task_struct,称为进程描述符的结构。进程描述符就不详细介绍了。
下面咱们来看看进程的状态标志,进程有5种状态标志:
第一 task_running 运行,进程正在运行,或者正在队列中等待运行,运行的进程能够在用户空间,也能够在内核中。
第二 task_interruptible 可中断,或者被阻塞,等待某些条件,一旦达到条件就被唤醒,而后进入运行状态。
第三 task_uninterruptible 不可中断,这种状态,即便收到外部的信号,也不会被唤醒,这种状态通常用的比较少。
第四 task_traced 被其余进程跟踪,例如经过ptrace对进程进行跟踪调试。
第五 task_stoped 中止,进程没有运行也不能运行的状态。
下面在介绍几个概念
第一个概念,进程上下文
进程从可执行文件载入进程的内存地址空间运行,通常是在用户空间,当进程调用了系统接口,或者触发了某种异常,它就进入了内核空间,此时,咱们称内核表明进程执行,并处于进程上下文中,总结下,就是内核和进程交互的时候,就是上下文状态,请注意,后面咱们还会介绍中断的上下文,和进程的上下文是有区别的,中断上下文中,系统不表明进程执行,而是执行一个中断程序。
第二个概念,进程家族树
Linux系统中,全部的进程都是PID为1的init进程的后代,内核在系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本,并执行其余的相关程序,最终完成系统的启动。
系统中的每一个进程必有一个父进程,每一个进程也会拥有灵感或者多个子进程,拥有同一父进程的全部进程被称为兄弟进程,进程间的关系也保存在前面提到的进程描述符中。
第三个概念,写时拷贝
Linux系统建立新的进程的时候,使用的是写时拷贝的技术,这样的好处是能够推迟甚至免除数据拷贝,子进程共享父进程的资源,只有当须要写入的时候
咱们经过几个概念的解释,来清晰化下内核对进程的调度
第一 什么是进程调度
进程调度就是决定进程何时运行,能够运行多长时间,进程调度程序的使命就是尽量的让进程多运行,提升效率。
第二 什么是多任务
多任务就是可以并发的执行多个进程,在单处理器上,这是一个假象,其实就是多个进程快速的在处理器上快速切进切出。
第三 什么是抢占式内核
多任务系统能够划分两类,非抢占式多任务和抢占式多任务,抢占式多任务就是由内核决定何时中止进程的运行,这个强制的动做就叫抢占。相反,除非进程主动中止,不然就一直运行,就是非抢占式多任务,显然,非抢占式多任务要依靠进程的自觉和良好设计,很古老的Windows3.1就是这样的系统,我大概是20年前接触到的,1996年的时候,这样的系统一个特色就是容易死机,可是当时看惯了黑黑屏幕的dos,看到窗口式的Windows,给人仍是很是震撼的感受。
第四 时间片
进程被抢占以前的时间是预先设置好的,有一个专门的名字,就是进程的时间片,调度策略必须规定一个默认的时间片,这里须要平衡,时间片太长影响系统的交互体验,时间片过短,会增长进程切换的频率,引发过多处理器消耗。
许多操做系统有默认的时间片长度,好比10ms,可是linux没有默认的时间片长度,Linux按照比例来划分,这样负载大的进程得到的处理器使用时间就更长。
第五 Linux的调度算法
在2.4内核之前,Linux内核调度很简陋,2.5内核中引入了Q(1)的调度程序,能够完美支持几十个处理器的进程调度,可是Q(1)算法对对时间敏感的程序有一些先天不足,所以Q(1)适合服务器,可是不适合桌面系统。
2.6内核中,引入了彻底公平算法,简称是CFS,目前Linux系统默认使用的都是CFS算法。
第六 IO消耗型和处理器消耗型的进程
IO消耗型的进程总在等待IO请求,占有处理器时间比较少,大部分用户图形界面程序都是IO消耗型。相反,若是处理器消耗型进程,就是把时间大多用于代码执行上,IO请求比较少。
固然也有便是IO消耗型也是处理器消耗型的进程,好比字处理程序,大部分时间是IO消耗型,可是当执行拼写检查的时候,就是处理器消耗型。
进程调度策略常常要在进程响应速度和最大系统利用率之间找平衡,这个背后是复杂的算法,不一样操做系统的倾向性也不同,Linux系统倾向io消耗型,这样响应速度快,用户体验好。
第七 进程优先级
Linux采用两种不一样的优先级范围:
第一种是nice值,范围是-20到+19,默认是0,越低的nice值,能够得到更多的处理器时间。
第二种是实时优先级,变化范围是0到99,和nice值相反,越高的值,优先级越高。
两种优先级划分有什么区别,任何实时进程优先级高于普通进程,就是说两种优先级处于互不交互的两个范畴。
进一步说明下, 进程分为普通进程和实时进程,普通进程使用CFS算法调度,优先级按照nice值区分。
实时进程有两种调度算法,FIFO,即先进先出,这种进程一直占用处理器,直到本身受阻塞或者释放处理器,若是有多个FIFO优先级进程,则会轮流执行。
另一种实时进程调度算法是RR,RR进程是按照时间片分配的,优先级范围就是0到99 。
注意,再强调下,实时进程总会抢占普通进程。
3、系统调用
用户空间进程不是和硬件设备直接通信的,而是有一个中间层,这样作的好处有三个:
第一, 为用户空间提供了一种硬件的抽象接口,这样用户空间进程就不用关心具体的硬件信息。
第二,限制了用户空间进程的行为,防止对其余进程形成影响,保证了系统的稳定和安全。
第三,隔离进程使用的资源, 方便内核调度。
通常的进程调用是经过API实现的,不是直接调用内核,API有一套标准,叫POSIX,Unix,Linux,甚至Windows都支持POSIX,只是你们支持的程度不同。
具体内核的API如何实现,这个要依靠Linux内核程序员,关于系统调用,运维了解到这些知识就能够了。
4、中断
仍是经过几个概念来了解中断。
第一 什么是中断
中断就是键盘、鼠标、硬盘、显卡、网卡等硬件和处理器的通信。
大部分硬件的运行速度和处理器比起来低不少,硬件要和处理器通信,有两种方式,一种方式是处理器轮询各个硬件,一种方式是硬件主动来找处理器,其实是硬件给处理器主动上报,由于这种方式效率更高,硬件在须要的时候给处理器发出信息,处理器来响应,这个就是中断处理。
中断信息实际就是电信号,硬件,好比键盘控制器,在你敲击键盘的时候会发出中断,信号进入中断控制器,而后进入处理器,处理器再通知操做系统。
第二 IRQ
不一样的设备对应的中断不一样,每一个中断都有惟一的数字标志,这样系统就能区分具体的设备,这些中断值被称做IRQ,中文的意思就是中断请求线。
好比,在经典的PC机上,IRQ0是时钟中端,IRQ1是键盘中断,可是这样也有问题,设备愈来愈多,原来的设计,中断号有限,常常会引发冲突,我记00年初,刚有声卡的时候,常常声卡由于中断冲突而不能使用,解决方法就是更换一个PCI插槽。
因此后来就有了动态分配中断值的方法,PCI设备都是动态分配中断号的,最终的目标关键是硬件能和处理器通信,可以引发处理器注意。
第三 异常
异常简单的说,就是程序出错,须要内核来处理的时候,一般因为编程失误而致使的错误指令,好比被0除,或者是在执行期间出现特殊状况,好比缺页。这时候就须要内核来处理,由于处理器体系结构处理异常与处理中断方式相似,所以,内核对他们的处理也很相似,实际上,异常也经常被称为同步中断。
第四 中断处理程序
在响应一个特定的中断的时候,内核会执行一个函数,这个函数就是中断处理程序interrput handler ,或者中断服务例程interrupt service routine,简称ISR。产生中断的每一个设备都一个相应的中断处理程序。
第五 中断的上半部和下半部
又想中断处理程序运行的快,又想中断处理程序完成的工做量多,这是矛盾的,为了解决这个矛盾,咱们把中断处理切为两个部分,中断处理程序是上半部top half,接收到中断,当即开始执行,但只作严格时限的工做,例如对接收的中断进行应答和复位硬件,这些工做都是在全部中断被禁止的状况下完成的,因此必须尽量快的完成。可以被容许稍后完成的工做推迟到下半部,bottom half.
用网卡作一个例子解释下,当网卡接收到网络的数据包的时候,须要通知内核数据包到了,网卡须要当即完成这件事,从而优化网络的吞吐量和传输周期,以免超时。这时候中断开始执行,通知硬件,拷贝最新的网络数据包到内存,而后读取网卡更多的数据包,这些都是重要、紧张而又与硬件相关的工做。
内核须要快速拷贝网络数据包到内存,由于网卡的缓存的大小是固定的,若是速度不够快,就会形成溢出,网卡就会丢弃数据包。
当数据拷贝到内存,中断的任务就完成了,它将控制权交还给系统系统中断前运行的程序,数据处理在随后的下半部进行。
第六 中断上下文
当执行一个中断处理程序的时候,内核处于中断上下问interrput context,咱们回忆下前面提到的进程上下文,进程上下文是内核所处的操做模式,此时内核表明进程执行。
与进程上下文相反,中断上下文和进程没有关系,由于没有后备进程,因此中断上下文不能够睡眠,中断上下文有严格的时间限制,由于它打短了其余代码,
在Linux系统中,查看中断的状况,可使用命令,能够看出详细的中断状况:
cat /proc/interrputs
中断上半部处理须要紧急处理的任务,包括对时间敏感,和硬件息息相关,不但愿被其余中断打断的任务,其余不紧急的任务,都交给下半部处理。
一般咱们但愿尽量的将任务交给中断下半部处理,由于上半部处理的时候,会形成其余中断被屏蔽,那么下半部是如何处理的呢,有三种方法。
第一种方法, BH,即bottom half,这是最先的中断处理机制,也是早期的惟一方法,同时只能有一个BH处理,即便有多个处理器。从内核2.5 版本开始,BH方法已经被放弃。
第二种方法,任务队列,为了充分使用多处理性能,内核开发者引入了任务队列的机制,task queue,内核定义了一组队列,驱动程序来和队列匹配,任务队列的方案在处理性能要求比较高的子系统,好比网络部分,也不能胜任。
第三种方法,软中断和tasklet,这种方法是在内核2.3版本中引入的,软中断能够在全部处理器上同时执行,tasklet是一种基于软中断实现的灵活性强、动态建立的下半部实现机制,两个不一样类型的tasklet能够同时在不一样的处理器上执行,可是类型相同的tasklet不能同时执行,tasklet是性能和易用性之间平衡的产物,能够处理大部分下半部中断处理。像网络这样对性能要求比较高的状况,才须要使用软中断。
5、内核同步
咱们仍是经过几个概念来了解下什么是内核同步。
第一个概念 为何会有内核同步问题
在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问,防止多个线程同时访问和操做数据,形成数据互相覆盖,和数据不一致。
在单一处理器的时候,这个还好办,只有在中断发生,或者从新调度另外一个任务的时候,数据才可能被并发访问。
到了多处理的时代,问题变的复杂,多处理器意味者着内核代码能够同时在两个或者两个以上的处理器上运行,为了防止同时改写内存数据的状况发生,就必须引入内核同步机制。
第二个概念 临界区和竞争条件
临界区是指访问和操做共享数据的代码段,多个执行线程并发访问同一个资源一般是不安全的,为了不在临界区中并发访问,编程者必须保证这些代码是原子的执行,也就是说,操做在执行结束前不可被打断,就如同整个临界区是一个不可分割的指令同样。若是两个执行线程有可能处于同一个临界区中同时执行,那么就是程序包含的bug。若是这种状况确实发生了,咱们就称它为竞争条件,这种状况出现的机会很是小,就是由于竞争引发的错误很是不容易重现,因此调试这种错误才会很是困难,避免并发和防止竞争条件称为同步。
第三个概念,加锁
为了防止一个处理器的进程在处理数据,而另一个处理器上的进程也同时修改这些数据,就须要给这块数据加锁,确保同时只能有一个进程访问数据。
加锁也是技术活,锁有多种多样的形式,加锁的粒度和范围也各不相同。
第四个概念 伪并发和真并发
在单处理器上,用户进程可能在任什么时候刻被抢占,也可能形成共享内存被修改,两个进程是交叉进行的,因此被称为伪并发。
在多处理器上,有可能真的两个进程在同时访问共享内存,所以被称为真并发。
内核中有如下相似的可能,形成并发执行,他们是:
中断,中断可能随时打断正在执行的代码。
软中断和tasklet,内核能在任什么时候刻唤醒或者调度软中断和tasklet,打断当前正在执行的代码。
内核抢占,由于内核具备抢占性,内核中的任务可能会被另外一任务抢占。
睡眠及用户空间的同步,在内核执行的进程可能睡眠,这就会唤醒调度程序执行另一个进程。
对称多处理,两个或者多个处理器同时执行代码。
第五个概念,死锁
死锁的产生须要必定条件,要有一个或多个执行线程和一个或者多个资源,每一个线程都在等待其中的一个资源,但全部的资源都被占用了。全部线程都在等待,但他们永远不会释放已经占有的资源,因而全部线程都没法继续,这便意味着死锁的发生。如何防止死锁的发生,也是程序设计的时候要考虑的问题。
第六个概念 争用和扩展性
一个资源被锁定,多个进程都在竞争这个资源,被称为锁的争用,锁的争用会形成系统瓶颈,严重下降系统性能。
解决办法就是扩展性,将锁的范围尽可能精细,这样就能够减小锁的争用,可是过于精细,也会额外消耗系统资源,因此掌握好平衡就须要技巧。
6、定时器和时间管理
时间管理在内核中占有很是重要的位置,内核中的函数驱动方式,能够分为事件驱动和时间驱动,其实时间驱动也能够认为是特殊的事件驱动,可是内核中,时间驱动的频率特别高。
时间驱动也能够分为周期驱动,好比每秒100次,或者推后执行,好比500ms之后执行某个任务。
另外,内核还必须管理系统的运行时间以及当前日期和时间。
这里还有一个概念,相对时间和绝对时间,若是某个事件在5s以后被执行,那么系统须要的是相对时间,相反,若是要求管理当前日期和当前时间,则内核不但要计算流逝的时间并且还要计算绝对时间。
周期性产生的事件,好比每10ms一次,都是由系统定时器产生的,系统定时器是一种硬件可编程芯片,能够固定频率产生中断,这个中断就是定时器中断.
在x86体系中,系统定时器默认频率是100Hz,也就是说i386处理器上每秒中断100次,即10ms一次,注意,每种体系的频率可能不同,有的是250,有的是1000。频率能够在编译内核时指定。
从2.5内核版本开始,中断频率被设定为1000Hz,使用高频率的好处是准确度,精确性更高,可是同时系统负担更重,也更耗电,可是处理器性能愈来愈高,这点消耗不会对系统形成过大的影响。
7、内存分配
内核把物理页做为内存管理的基本单元,尽管处理器的最小可寻址单元一般为字(甚至字节),可是,内存管理单元MMU一般以页为单位进行处理。
体系不一样,页的大小也不同,大部分32位体系结构支持4KB的页,64位体系结构通常支持8KB的页,这意味着,在1GB物理内存的机器上,4KB页大小,物理内存会被划分为262144个页。
因为硬件的限制,内核并不能对全部的页一视同仁,有些页位于特定的物理地址上,因此不能将其用于特定的任务,因为存在这种限制,因此内核把页划分为不一样的区zone。
Linux必须处理以下两种因为硬件存在缺陷引发的寻址问题:
一些硬件只能用某些特定的内存地址来执行DMA,即直接内存访问。
一些体系结构的内存物理寻址范围比虚拟寻址范围大得多,这样,就有一些内存不能永久地映射到内核空间上。
由于存在这些限制条件,Linux主要使用了四种区:
ZONE_DMA 这个区包含的页用来执行DMA操做
ZONE_DMA32 和ZONE_DMA类似,可是这个区只能被32位设备访问
ZONE_NORMAL 这个区包含的都是能正常映射的页
ZONE_HIGHEM 这个区包含高端内存,其中的页不能永久映射到内核地址空间。
通常DMA区使用0-16MB的内存,NORMAL区使用16-896MB的内存,HIGHEM区使用896MB以上的内存。
8、虚拟文件系统
虚拟文件系统做为内核的子系统,简称VFS,为用户空间程序提供了文件和文件系统相关的接口,系统中的全部文件系统不但依赖VFS共存,而且依靠VFS协同工做。
VFS提供了通用的接口和方法,好比open(),read(),write(),系统调用的无需考虑具体文件系统和实际物理介质。
之因此能够这样,是由于内核在底层文件系统接口上创建一个抽象层,抽象层使Linux可以支持各类文件系统。VFS抽象层定义了全部文件系统都支持的、基本的、概念上的接口和数据结构。任何新的文件系统和新介质只要符合VFS规范,均可以直接使用。
unix系统使用四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和挂载点。从本质上讲文件系统是特殊的数据分层存储结构,包含文件、目录和相关的控制信息。
VFS采用面向对象的设计思路,使用一组数据结构来表明通用文件对象。
VFS有四个主要的对象类型:
超级块对象,表明一个具体的已安装文件系统;
索引节点对象,表明一个具体文件;
目录项对象,表明一个目录项,是路径的一个组成部分;
文件对象,表明由进程打开的文件。
另外,说明下,由于VFS将目录做为文件来处理,因此不存在目录对象。
咱们总结下,Linux支持了多种类型的文件系统,从本地文件系统,例如ext3,ext4,到网络文件系统好比NFS。LInux在标准内核中已支持的文件系统超过60种。VFS层提供给这些不一样文件系统一个统一的实现框架,并且也提供了能和标准系统调用交互工做的统一接口。因为VFS层的存在,使得Linux上实现新文件系统的工做变得简单起来,它能够轻松地使这些文件系统经过标准Unix系统调用而协同工做。
9、块I/O层
咱们仍是经过五个概念了解块IO层
第一个概念 块设备和字符设备。
系统可以随机访问固定大小数据片的硬件设备称为块设备,数据片的英文术语是chunk,硬盘、软盘、光盘、SSD、U盘都属于块设备,由于系统随时能够访问这些介质上的任意位置数据,另外,说明下,对这些介质的访问,是经过访问文件系统实现的。
字符设备是按照字符流的方式被有序访问的设备,像串口和键盘就属于字符设备。
块设备和字符设备主要的区别就是随机访问方式仍是顺序访问方式。
第二个概念 扇区和块。
块设备中最小的可寻址单元是扇区,扇区大小通常是2的整数倍,硬盘最多见的扇区大小是512字节,CD-ROM的扇区通常是2KB。
每种文件系统都有本身最小的逻辑可寻址单元,块。块是文件系统的抽象,只能基于块来访问文件系统。
扇区和块的区别是,物理磁盘寻址是按照扇区级进行的,文件系统是按照块来进行的。块大小必须是扇区的倍数,通常是2的整数倍,而且不能超过一个内存页大小,由于文件块须要被缓存到内存中。因此通常文件块的大小是512字节,1KB,4KB。
另外,磁盘还有一些术语,好比簇,柱面,磁头,请你们本身找资料看下。
第三个概念 缓冲区。
当一个块被调入内存时,就存储在一个缓冲区中,每一个缓冲区与一个块对应,至关于磁盘块在内存中的表示。像前面介绍的,块包含一个或多个扇区,可是大小不能超过一个页面,因此一个内存页能够容纳一个或者多个内存中的块。
第四个概念 请求队列。
块设备将它们挂起的块IO请求保存在请求队列中,请求队列只要不为空,队列对应的块设备驱动程序就会从队列头部获取请求,而后将其送入对应的块设备上去。
第五个概念 IO调度程序。
若是简单的之内核产生请求的次序直接将请求发向块设备的话,性能确定让人难以忍受,磁盘寻址是整个计算机中最慢的操做之一,每次寻址,定位磁头到特定的块上的某个位置,须要花费很多时间,因此尽可能缩短寻址时间无疑是提升系统性能的关键。
为了优化寻址操做,内核既不会简单的按请求接收文件,也不会马上将其提交给磁盘。相反,内核会在提交前,先执行合并与排序的操做,这种操做能够极大的提升系统性能,在内核中负责提交IO请求的子系统,称为IO调度程序。
IO调度程序将磁盘IO资源分配给系统中全部挂起的块IO请求,这种资源分配是经过请求队列中挂起的请求合并和排序来完成的。
IO调度器的工做是管理块设备的请求队列,它决定队列中的请求排列顺序以及在什么时刻发送请求到块设备,这样作有利于减小磁盘寻址时间,从而提升全局吞吐量。注意,全局这个定语很重要,由于IO调度器可能为了提升系统总体性能,会对某些请求不公。
IO调度器经过两种方法减小磁盘寻址时间,合并与排序。举个例子,文件系统接到多个请求队列,IO调度器能够按照磁盘扇区顺序进行排序,那么相邻扇区的访问就能够合并为一次,这样就大大减小了磁盘寻址消耗。即便没有相邻扇区的访问,经过IO调度器,按照磁盘旋转方向访问,也缩短了全部请求的磁盘寻址时间。
10、I/O算法
第一种算法 linus电梯
在2.4版本内核中,linus是默认的IO调度程序,linus算法可以执行合并与排序预处理,当有新的请求加入队列时,它首先检查其余每个挂起的请求是否能够和新请求合并。linus电梯算法能够执行向前和向后合并,若是新的请求没有合适的插入点,则会被放入队列尾部。
另外,系统中若是有驻留时间过长的请求,新的请求也会被放到队列尾部,这样作的目的是防止对一个磁盘位置访问的过多,形成对其余磁盘位置的请求被饿死。可是这样的作法,由于仅仅是改变队列排序,没有队列的时间检测,不能彻底避免有队列被饿死的状况。
第二种算法 最终期限
最终期限deadline IO调度算法是为了解决linus电梯算法所带来的饥饿问题而提出的。出于减小磁盘寻址时间的考虑,对某个磁盘区域的频繁操做,会使对磁盘其余位置的操做请求饿死。
更糟糕的是,普通的请求还会形成写-饥饿-读这种问题。
写请求一般能够缓存,可是读请求的时候,程序会被阻塞,直到拿到请求的读数据,也就是写请求是异步的,读请求是同步的,若是有大量的读请求的时候,写请求就会被饿死。
问题可能还会更严重,若是读请求和写请求是相互依靠的,写请求没有操做,读操做又去请求数据,就会形成应用更长时间的等待。
最终期限算法中,每一个请求都有一个超时时间,默认读请求的超时时间是500ms,写请求的超时时间是5s。
最终期限算法有三个队列,在超时时间内,调度相似于linus电梯,有一个排序队列,另外维护两个按照时间顺序的读fifo队列,和写fifo队列。
在超时时间内,按照排序队列派发操做,若是读写队列的列头请求超时,那么IO调度程序便从队列中提取请求进行服务,这样就能保证不发生磁盘操做请求超时的状况。
经过最终期限算法,能够避免写操做饿死,同时由于读操做超时时间短,这种算法也优化了大量读操做的响应。
第三种 算法 预测IO调度
预测IO调度和最终期限同样,也是维护三个同样的队列,不一样的是,在提交请求的以前,会有意等待一段时间,默认是6ms,若是有新的请求来,在将相邻扇区的请求合并,这样能够优化磁盘操做。固然,若是没有操做请求,会浪费几毫秒的时间。
第四种算法 彻底公平的排队IO调度CFQ
CFQ调度程序把进入IO的请求放去特定的队列中,这种队列请求是根据引发IO请求的进程组织的,在每一个队列中,刚进入的请求和相邻请求进行合并。
CFQ调度程序以时间片轮转调度队列,从每一个队列中选取请求固定数字的操做,默认为4,而后进入下一轮调度。这样在进程级实现了公平。
目前内核默认的调度算法是CFQ。
第五种算法 空操做
之因此这样命名,是由于这种算法基本不做什么事情,基本就是先进先出,固然,若是相邻的操做可以合并,仍是会合并,空操做懒惰是有道理的,由于这种算法是用在闪存设备上,若是设备没有寻址负担,那么也没有必要对其排序。
11、页高速缓存和页回写
咱们仍是经过解答几个问题,来了解页高速缓存和页回写。
第一个问题,为何会有页高速缓存
这个主要缘由是由于内存和磁盘的速度差距很是大,磁盘的读写速度是毫秒级别的,内存的读写速度是纳秒级别的,若是可以经过内存缓存磁盘数据,就能够大大提升系统速度。另外,被访问的数据,颇有可能再次被访问,若是可以把数据缓存到内存中,那么数据若是再次被频繁访问,就能够提升系统性能。
第二个问题,写磁盘如何缓存
写缓存有三种方式,第一种是不缓存,就是当写数据时,直接写到磁盘,这种方式数据最安全,可是性能最低。第二种方式是透写,数据先写到缓存,而后马上写磁盘,这样数据也是很安全,可是性能也比较低。第三种方式是回写,writeback,数据写到缓存,就认为成功,到必定时间,或者数据比较多的时候,再写盘,这种方式性能很好,可是若是数据在缓存中的时候,机器忽然断电,有可能数据丢失。
第三个问题,读缓存的回收策略是什么
由于内存有限,不可能把整个磁盘的数据缓存到内存中,只能保证把比较热的数据缓存起来,那么如何确认数据比较热呢,有两种算法,一种是根据时间,系统扫描页面,没有被访问的,时间比较久的页面,就会被释放掉,还有一种算法,是双链表,或者多链表,增长了一些统计的概念,更精确一些。
第四个问题,笔记本电脑模式
在笔记本电脑上,由于有电池,同时为了提高性能,通常启用的都是回写模式,而且刷新磁盘的时间间隔更长,这样还能够省电。目前的大部分系统也能够在笔记本电脑启用电池时,自动修改回写策略。
另外也能够执行命令sync,强制系统刷盘。通常在我的版的系统上,默认都是开启回写,这样性能会好不少,可是在服务器系统上,通常默认都是透写模式,由于在服务器上,数据更重要。这也是为何有时候你会发现,在我的PC上,磁盘写性能竟然要好于服务器的缘由。
12、关于内核的几个概念
第一个概念,Linux的设备类型
在Linux及Unix中,设备被分为三种类型:
块设备
字符设备
网络设备
块设备缩写为blkdev,块设备以块为单位,而且是能够寻址的,便可以随机访问任何位置的数据。块设备一般被挂载为文件系统来使用。
字符设备缩写为cdev,字符设备不可寻址,只能流式访问,与块设备不一样,应用程序一般直接和块设备交互。
网络设备一般是经过物理设备和IP协议提供的,网络设备打破了unix一切皆文件的设计原则,对网络设备的访问是经过套接字API实现的。
Linux还提供了其余设备类型,但都是针对单个任务,而非通用的。
另外,并非全部设备驱动都表示物理设备,有些设备驱动是虚拟的,称之为“伪设备”,最多见的是内核随机数发生器/dev/urandom,空设备/dev/null,零设备/dev/zero等。
尽快Linux内核是单块内核的操做系统,可是整个内核是模块化的,容许在运行时动态的插入或者删除代码,即所谓的可装载内核模块。
第二个概念,内核的可移植性
Linux是可移植性很是好的操做系统,支持许多不一样体系的计算机。可移植性是指操做系统代码从一套体系迁移到另一套体系的方便程度。
在操做系统可移植性方面,设计有两种思路。
一种思路是尽可能追求通用性,尽可能少的使用汇编语言,这样设计出来的操做系统可移植性很是高,可是缺点是不能针对某种体系深刻优化。
还有一种思路就是基本不考虑可移植性,只对一种体系深度优化,Windows系统就是这样的系统,主要就是针对x86系统优化,可是可移植性极差。
Linux系统走了一条中间道路,差很少全部的接口和核心代码都是独立于硬件的,可是,对于性能要求很严格的部分,内核会针对不一样体系调整,这使得linux在可移植性和性能之间取得比较好的平衡。
第三个概念 社区
你们都知道,linux是开源的,社区和代码随时能够访问,只要有兴趣,也能够随时参与社区活动。可是linux入门门槛比较高。须要一个比较长的过程,只要坚持,最终会跨过这个门槛。
后记
本文经过十二部分蜓蜓点水式的介绍,但愿可以帮助你们能记住并理解几个概念。若是有兴趣更深刻的了解,推荐阅读下《Linux内核设计与实现》这本书。