精华内容
下载资源
问答
  • 中断控制主要优点是只有在I/O需要服务时才能得到处理器响应,不需要处理器不断地进行查询。由此,最初的中断全部是对外部设备而言,称为外部中断(或硬件中断)。 内部中断(或异常),是为解决机器运行时...

    中断控制,最初是为克服对I/O接口控制采用程序查询所带来的处理器效率低而产生的。中断控制的主要优点是只有在I/O需要服务时才能得到处理器的响应,不需要处理器不断地进行查询。由此,最初的中断全部是对外部设备而言的,称为外部中断(或硬件中断)

    内部中断(或异常),是为解决机器运行时出现的某些随机事件以及编程方便出现的。形成一个完整的中断系统。

    主要讨论在80x86保护模式下中断机制在Linux中的实现。

    一、中断是什么

    16位实地址模式32位保护模式,这两种模式之间最本质的差别是在保护模式引入中断描述符表。

    1、中断向量

    x86系列微机支持256种向量中断,为使处理器识别每种中断源,从0到255编号,赋予一个中断类型码n,Intel把这个8位的无符号整数叫做一个向量,也叫中断向量。256种中断分两大类:异常和中断。异常分为故障和陷阱,其共同特点是既不使用中断控制器,也不能被屏蔽(异常是CPU发出的中断信号)。中断分为外部可屏蔽中断外部非屏蔽中断,所有I/O设备产生的中断请求引起屏蔽中断,紧急的事件(如硬件故障)引起的故障产生非屏蔽中断。

    非屏蔽中断的向量和异常的向量是固定的,而屏蔽中断的向量可通过对中断控制器的编程来改变。Linux对256个向量的分配如下:

    从0~31的向量对应异常和非屏蔽中断。

    从32~47的向量(由I/O设备引起的中断)分配给屏蔽中断。

    从48~255的向量用来标识软中断。Linux只用了其中的一个(128或0x80)用来实现系统调用。

    备注:

    可以在proc文件系统下的interrupts文件中,查看当前系统中各种外设的IRQ命令:

    cat /proc/interrupts

    2、外设可屏蔽中断

    Intel x86通过两片中断控制器8259A来响应15个外中断源,第一级(主片)的第二个中断请求输入端与第二级(从片)的中断输出端INT相连,如图所示。

    与中断控制器相连的每条线叫做中断线,要使用中断线,要进行中断线的申请,即IRQ,也把申请一条中断线称为申请一个IRQ或者是申请一个中断号。IRQ线从0开始顺序编号,第一条中断线表示成IRQ0。IRQn的缺省向量是n+32,IRQ和向量之间的映射可通过中断控制器端口来修改。

    并不是每个设备都可以向中断线上发中断信号的,只有对某一条确定的中断线拥有控制权,才可以向这条中断线上发送信号。中断线资源非常宝贵,只有当设备需要中断时才申请占用一个IRQ,或者是在申请IRQ时采用共享中断的方式,这可以让更多的设备使用中断。

    对于外部I/O请求的屏蔽可分两种情况。

    一种是从CPU的角度,清除EFLAG(标志寄存器)的中断标志位(IF),决定CPU是否响应外部可屏蔽中断请求。当IF为0时,禁止任何外部I/O的中断请求,即关中断;当IF为1时,CPU允许响应外部的可屏蔽中断请求。

    另一种是从中断控制器角度,因为中断控制器中有一个8位的中断屏蔽寄存器,每位对应中断控制器8259A中的一条中断线,如果要禁用某条中断线,则把中断屏蔽寄存器相应的位置1,要启用,则置0。

    3、异常及非屏蔽中断

    异常是CPU内部出现的中断,在CPU执行特定指令时出现的非法情况。非屏蔽中断是计算机内部硬件出错时引起的异常情况。二者与外部I/O接口没有任何关系。Intel把非屏蔽中断作为异常的一种来处理,异常也包括非屏蔽中断。在CPU执行一个异常处理程序时,不再为其他异常或可屏蔽中断请求服务,当某个异常响应后,CPU清除EFLAG(标志寄存器)中的IF(中断标志位),IF为0,禁止任何可屏蔽中断。但如果又产生异常,则由CPU锁存CPU具有缓冲异常的能力),待这个异常处理完毕后,才响应被锁存的异常。

    备注:这里讨论的异常中断向0~31之间(异常和非屏蔽中断),不包括系统调用(中断向量为0x80)。

    x86处理器发布大约20种异常(具体数字与处理器模式有关)。Linux内核必须为每种异常提供一个专门的异常处理程序。

    4、中断描述符表

    在实地址模式中,CPU把内存中从0开始的1KB存储中断向量表。表中的每个表项占4B,由2B的段地址和2B的偏移量组成,构成的地址是相应的中断处理程序的入口地址。但在保护模式下,由4B的表项构成的中断向量表不能满足要求。这是因为:

    (1)除2B的段描述符,偏移量必须用4B来表示;

    (2)要有反映模式切换的信息。

    因此,在保护模式下,中断向量表中的表项由8B组成,如下图所示,中断向量表也叫做中断描述符表。其中每个表项叫做一个门描述符,门的含义是当中断发生时必须先通过这个门,才能进入相应的处理程序。

    类型占3位,表示门描述符的类型,主要门描述符有以下几种。

    1)中断门(Interrupt Gate):类型码为110,中断门包含一个中断或异常处理程序所在段的选择符和段内偏移量。当控制权通过中断门进入中断处理程序时,处理器清除EFLAG(标志寄存器)中的IF(中断标志位)标志,IF=0,即关中断,避免嵌套中断的发生。中断门中的请求特权级(DPL)为0,用户态的进程不能访问Intel的中断门。所有的中断处理程序都由中断门激活,并全部限制在内核态。

    2)陷阱门(Trap Gate):类型码为111,与中断门类似,唯一的区别是控制权通过陷阱门进入处理程序时维持IF标志位不变,不关中断。

    3)系统门(System Gate):是Linux内核特别设置的,用来让用户态的进程访问Intel的陷阱门,因此,门描述符的DPL为3。系统调用通过系统门进入内核

    在保护模式下,中断描述符表在内存的位置不再局限于从地址0开始的地方,而是可以放在内存的任何地方。CPU中增设了一个中断描述符表寄存器IDTR,用来存放中断描述符表在内存的起始地址。中断描述符表寄存器是一个48位的寄存器,其低16位保存中断描述符表的大小,高32位保存中断描述符表的基地址。

    5、相关汇编指令

    1)调用过程指令 CALL

    指令格式:CALL 过程名

    说明:LIDT将指令中给定的48位伪描述符载入中断描述符表寄存器IDTR。

    说明:在取出CALL指令之后及执行CALL指令之前,使指令指针寄存器EIP指向紧接CALL指令的下一条指令。CALL指令先将EIP值压入栈内,再进行控制转移。当遇到返回指令IRET时,栈内信息可使控制权直接回到CALL的下一条指令。

    2)调用中断过程的指令INT

    指令格式:INT 中断向量

    说明:EFLAG、CS及EIP寄存器被压入栈内。控制权被转移到由中断向量指定的中断处理程序。在中断处理程序结束时,IRET指令又把控制权送回到刚才执行被中断的地方。

    3)中断返回指令 IRET

    指令格式:IRET

    说明:IRET将EIP、CS及EFLAG寄存器内容从栈中弹出,并将控制权返回到发生中断的地方。IRET用在中断处理程序的结束处。

    4)加载中断描述符表的指令 LIDT指令格式:LIDT 48位的伪描述符

    展开全文
  • 51单片机之中断的实现过程

    万次阅读 多人点赞 2017-05-08 16:15:58
    中断的优点? 1.分时操作。 2.实时响应 3.可靠性高 中断中用到的寄存器: IE寄存器: 1.单路开关:EX0 ET0 EX1 ET1 ES EX0:外部中断0允许位 ET0:定时/计数器T0中断允许位 EX1:外部中断1允许位

    我们首先需要了解什么是中断?

    中断是指cpu在执行某一过程中由于外界原因必须暂停现在的事情,处理别的事情,处理完了再回去执行暂停的事情。

    中断的优点?

    1.分时操作。
    2.实时响应
    3.可靠性高

    中断中用到的寄存器:
    IE寄存器:
    1.单路开关:EX0 ET0 EX1 ET1 ES
    EX0:外部中断0允许位
    ET0:定时/计数器T0中断允许位
    EX1:外部中断1允许位
    ET1:定时/计数器T1中断允许位
    ES:串行口中断允许位
    2.总开关
    EA:CPU中断允许位
    EA=1打开
    IP寄存器:
    作用是选择优先级的,解决中断优先级问题。

    IT0寄存器:选择低电平有效还是下降沿有效,上电默认电平触发方式,IT0=1是下降沿有效
    T0 T1为定时器中断,RX TX为串行口中断

    中断优先级顺序由高到低:
    外部中断0(IE0)->定时/计数器0(TF0)->外部中断1(IE1)->定时/计数器1(TF1)->串行口(R1或T1)

    首先响应的是优先级高的中断请求,正在中断的不会被新的中断请求打断,正在进行低优先级中断服务,能被高优先级中断请求中断。

    中断函数一般形式:void 函数名() interrupt   n{}
    n代表中断号,中断号是编译器识别不同中断的唯一编号

    中断函数和普通函数的异同:
    同:函数的形式非常类似,中断响应过程和普通函数调用过程也非常相似
    异:中断函数不需要声明,普通函数需要声明

    使用中断函数需要遵循以下规则:
    (1)中断函数不能进行参数传递
    (2)在任何情况下,都不能直接调用中断函数

    简单中断程序:
    #include <reg51.h>
    
    void main()
    {
    	P1=0x55;//p1口初始值
    	EA=1;//全局中断开
    	EX0=1;//外部中断0开
    	IT0=0;//电平触发,一般写1
    	while(1)
    	{}
    
    }
    //外部中断程序
    void interves(void) interrupt 0 using 1
    {
    
    	P1=~P1//进入中断程序执行程序
    	//此时可以通过EA = 0指令暂时关掉中断
    
    }






    展开全文
  • 前言本文主要《Linux内核设计与实现》这本书读书笔记,这本书我读了不下十遍,但依然...目录运维为什么要了解内核进程系统调用中断内核同步定时器和时间管理内存分配虚拟文件系统块I/O层I/O算法页高速缓存和页回...

    前言

    本文主要是《Linux内核设计与实现》这本书的读书笔记,这本书我读了不下十遍,但依然感觉囫囵吞枣。我结合自己的理解,从这本书中整理出了一些运维应该了解的内核知识,希望对大家能够有所帮助。另外,推荐大家读下这边书,这本书主要讲内核设计、实现原理和方法,有利于理解内核的一些机理。

    目录

    1. 运维为什么要了解内核
    2. 进程
    3. 系统调用
    4. 中断
    5. 内核同步
    6. 定时器和时间管理
    7. 内存分配
    8. 虚拟文件系统
    9. 块I/O层
    10. I/O算法
    11. 页高速缓存和页回写
    12. 关于内核的几个概念

    一、运维为什么要了解内核

    运维为什么要了解内核

    大神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是奇数。

    二、进程

    先来聊聊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 。

    注意,再强调下,实时进程总会抢占普通进程。

    三、系统调用

    用户空间进程不是和硬件设备直接通讯的,而是有一个中间层,这样做的好处有三个:

    • 第一, 为用户空间提供了一种硬件的抽象接口,这样用户空间进程就不用关心具体的硬件信息。
    • 第二,限制了用户空间进程的行为,防止对其他进程造成影响,保证了系统的稳定和安全。
    • 第三,隔离进程使用的资源, 方便内核调度。

    一般的进程调用是通过API实现的,不是直接调用内核,API有一套标准,叫POSIX,Unix,Linux,甚至Windows都支持POSIX,只是大家支持的程度不一样。

    具体内核的API如何实现,这个要依靠Linux内核程序员,关于系统调用,运维了解到这些知识就可以了。

    四、中断

    还是通过几个概念来了解中断。

    第一 什么是中断

    中断就是键盘、鼠标、硬盘、显卡、网卡等硬件和处理器的通讯。

    大部分硬件的运行速度和处理器比起来低很多,硬件要和处理器通讯,有两种方式,一种方式是处理器轮询各个硬件,一种方式是硬件主动来找处理器,实际上是硬件给处理器主动上报,因为这种方式效率更高,硬件在需要的时候给处理器发出信息,处理器来响应,这个就是中断处理。

    中断信息实际就是电信号,硬件,比如键盘控制器,在你敲击键盘的时候会发出中断,信号进入中断控制器,然后进入处理器,处理器再通知操作系统。

    第二 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是性能和易用性之间平衡的产物,可以处理大部分下半部中断处理。像网络这样对性能要求比较高的情况,才需要使用软中断。

    五、内核同步

    我们还是通过几个概念来了解下什么是内核同步。

    第一个概念 为什么会有内核同步问题

    在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问,防止多个线程同时访问和操作数据,造成数据互相覆盖,和数据不一致。

    在单一处理器的时候,这个还好办,只有在中断发生,或者重新调度另一个任务的时候,数据才可能被并发访问。

    到了多处理的时代,问题变的复杂,多处理器意味者着内核代码可以同时在两个或者两个以上的处理器上运行,为了防止同时改写内存数据的情况发生,就必须引入内核同步机制。

    第二个概念 临界区和竞争条件

    临界区是指访问和操作共享数据的代码段,多个执行线程并发访问同一个资源通常是不安全的,为了避免在临界区中并发访问,编程者必须保证这些代码是原子的执行,也就是说,操作在执行结束前不可被打断,就如同整个临界区是一个不可分割的指令一样。如果两个执行线程有可能处于同一个临界区中同时执行,那么就是程序包含的bug。如果这种情况确实发生了,我们就称它为竞争条件,这种情况出现的机会非常小,就是因为竞争引起的错误非常不容易重现,所以调试这种错误才会非常困难,避免并发和防止竞争条件称为同步。

    第三个概念,加锁

    为了防止一个处理器的进程在处理数据,而另外一个处理器上的进程也同时修改这些数据,就需要给这块数据加锁,确保同时只能有一个进程访问数据。

    加锁也是技术活,锁有多种多样的形式,加锁的粒度和范围也各不相同。

    第四个概念 伪并发和真并发

    在单处理器上,用户进程可能在任何时刻被抢占,也可能造成共享内存被修改,两个进程是交叉进行的,所以被称为伪并发。

    在多处理器上,有可能真的两个进程在同时访问共享内存,因此被称为真并发。

    内核中有以下类似的可能,造成并发执行,他们是:

    • 中断,中断可能随时打断正在执行的代码。
    • 软中断和tasklet,内核能在任何时刻唤醒或者调度软中断和tasklet,打断当前正在执行的代码。
    • 内核抢占,因为内核具有抢占性,内核中的任务可能会被另一任务抢占。
    • 睡眠及用户空间的同步,在内核执行的进程可能睡眠,这就会唤醒调度程序执行另外一个进程。
    • 对称多处理,两个或者多个处理器同时执行代码。

    第五个概念,死锁

    死锁的产生需要一定条件,要有一个或多个执行线程和一个或者多个资源,每个线程都在等待其中的一个资源,但所有的资源都被占用了。所有线程都在等待,但他们永远不会释放已经占有的资源,于是所有线程都无法继续,这便意味着死锁的发生。如何防止死锁的发生,也是程序设计的时候要考虑的问题。

    第六个概念 争用和扩展性

    一个资源被锁定,多个进程都在竞争这个资源,被称为锁的争用,锁的争用会造成系统瓶颈,严重降低系统性能。

    解决办法就是扩展性,将锁的范围尽量精细,这样就可以减少锁的争用,但是过于精细,也会额外消耗系统资源,所以掌握好平衡就需要技巧。

    六、定时器和时间管理

    时间管理在内核中占有非常重要的位置,内核中的函数驱动方式,可以分为事件驱动和时间驱动,其实时间驱动也可以认为是特殊的事件驱动,但是内核中,时间驱动的频率特别高。

    时间驱动也可以分为周期驱动,比如每秒100次,或者推后执行,比如500ms以后执行某个任务。

    另外,内核还必须管理系统的运行时间以及当前日期和时间。

    这里还有一个概念,相对时间和绝对时间,如果某个事件在5s之后被执行,那么系统需要的是相对时间,相反,如果要求管理当前日期和当前时间,则内核不但要计算流逝的时间而且还要计算绝对时间。

    周期性产生的事件,比如每10ms一次,都是由系统定时器产生的,系统定时器是一种硬件可编程芯片,可以固定频率产生中断,这个中断就是定时器中断.

    在x86体系中,系统定时器默认频率是100Hz,也就是说i386处理器上每秒中断100次,即10ms一次,注意,每种体系的频率可能不一样,有的是250,有的是1000。频率可以在编译内核时指定。

    从2.5内核版本开始,中断频率被设定为1000Hz,使用高频率的好处是准确度,精确性更高,但是同时系统负担更重,也更耗电,但是处理器性能越来越高,这点消耗不会对系统造成过大的影响。

    七、内存分配

    内核把物理页作为内存管理的基本单元,尽管处理器的最小可寻址单元通常为字(甚至字节),但是,内存管理单元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以上的内存。

    八、虚拟文件系统

    虚拟文件系统作为内核的子系统,简称VFS,为用户空间程序提供了文件和文件系统相关的接口,系统中的所有文件系统不但依赖VFS共存,并且依靠VFS协同工作。

    VFS提供了通用的接口和方法,比如open(),read(),write(),系统调用的无需考虑具体文件系统和实际物理介质。

    之所以可以这样,是因为内核在底层文件系统接口上建立一个抽象层,抽象层使Linux能够支持各种文件系统。VFS抽象层定义了所有文件系统都支持的、基本的、概念上的接口和数据结构。任何新的文件系统和新介质只要符合VFS规范,都可以直接使用。

    unix系统使用四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和挂载点。从本质上讲文件系统是特殊的数据分层存储结构,包含文件、目录和相关的控制信息。

    VFS采用面向对象的设计思路,使用一组数据结构来代表通用文件对象。

    VFS有四个主要的对象类型:

    • 超级块对象,代表一个具体的已安装文件系统;
    • 索引节点对象,代表一个具体文件;
    • 目录项对象,代表一个目录项,是路径的一个组成部分;
    • 文件对象,代表由进程打开的文件。

    另外,说明下,因为VFS将目录作为文件来处理,所以不存在目录对象。

    我们总结下,Linux支持了多种类型的文件系统,从本地文件系统,例如ext3,ext4,到网络文件系统比如NFS。LInux在标准内核中已支持的文件系统超过60种。VFS层提供给这些不同文件系统一个统一的实现框架,而且也提供了能和标准系统调用交互工作的统一接口。由于VFS层的存在,使得Linux上实现新文件系统的工作变得简单起来,它可以轻松地使这些文件系统通过标准Unix系统调用而协同工作。

    九、块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调度器,按照磁盘旋转方向访问,也缩短了所有请求的磁盘寻址时间。

    十、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。

    第五种算法 空操作

    之所以这样命名,是因为这种算法基本不作什么事情,基本就是先进先出,当然,如果相邻的操作能够合并,还是会合并,空操作懒惰是有道理的,因为这种算法是用在闪存设备上,如果设备没有寻址负担,那么也没有必要对其排序。

    十一、页高速缓存和页回写

    07491130479f1b2b9b52cfea017c9a87.png

    我们还是通过解答几个问题,来了解页高速缓存和页回写。

    第一个问题,为什么会有页高速缓存

    这个主要原因是因为内存和磁盘的速度差距非常大,磁盘的读写速度是毫秒级别的,内存的读写速度是纳秒级别的,如果能够通过内存缓存磁盘数据,就可以大大提高系统速度。另外,被访问的数据,很有可能再次被访问,如果能够把数据缓存到内存中,那么数据如果再次被频繁访问,就可以提高系统性能。

    第二个问题,写磁盘如何缓存

    写缓存有三种方式,第一种是不缓存,就是当写数据时,直接写到磁盘,这种方式数据最安全,但是性能最低。第二种方式是透写,数据先写到缓存,然后立刻写磁盘,这样数据也是很安全,但是性能也比较低。第三种方式是回写,writeback,数据写到缓存,就认为成功,到一定时间,或者数据比较多的时候,再写盘,这种方式性能很好,但是如果数据在缓存中的时候,机器突然断电,有可能数据丢失。

    第三个问题,读缓存的回收策略是什么

    因为内存有限,不可能把整个磁盘的数据缓存到内存中,只能保证把比较热的数据缓存起来,那么如何确认数据比较热呢,有两种算法,一种是根据时间,系统扫描页面,没有被访问的,时间比较久的页面,就会被释放掉,还有一种算法,是双链表,或者多链表,增加了一些统计的概念,更精确一些。

    第四个问题,笔记本电脑模式

    在笔记本电脑上,因为有电池,同时为了提升性能,一般启用的都是回写模式,并且刷新磁盘的时间间隔更长,这样还可以省电。目前的大部分系统也可以在笔记本电脑启用电池时,自动修改回写策略。

    另外也可以执行命令sync,强制系统刷盘。一般在个人版的系统上,默认都是开启回写,这样性能会好很多,但是在服务器系统上,一般默认都是透写模式,因为在服务器上,数据更重要。这也是为什么有时候你会发现,在个人PC上,磁盘写性能居然要好于服务器的原因。

    十二、关于内核的几个概念

    第一个概念,Linux的设备类型

    在Linux及Unix中,设备被分为三种类型:

    • 块设备
    • 字符设备
    • 网络设备

    块设备缩写为blkdev,块设备以块为单位,并且是可以寻址的,即可以随机访问任何位置的数据。块设备通常被挂载为文件系统来使用。

    字符设备缩写为cdev,字符设备不可寻址,只能流式访问,与块设备不同,应用程序通常直接和块设备交互。

    网络设备通常是通过物理设备和IP协议提供的,网络设备打破了unix一切皆文件的设计原则,对网络设备的访问是通过套接字API实现的。

    Linux还提供了其他设备类型,但都是针对单个任务,而非通用的。

    另外,并不是所有设备驱动都表示物理设备,有些设备驱动是虚拟的,称之为“伪设备”,最常见的是内核随机数发生器/dev/urandom,空设备/dev/null,零设备/dev/zero等。

    尽快Linux内核是单块内核的操作系统,但是整个内核是模块化的,允许在运行时动态的插入或者删除代码,即所谓的可装载内核模块。

    第二个概念,内核的可移植性

    Linux是可移植性非常好的操作系统,支持许多不同体系的计算机。可移植性是指操作系统代码从一套体系迁移到另外一套体系的方便程度。

    在操作系统可移植性方面,设计有两种思路。

    • 一种思路是尽量追求通用性,尽量少的使用汇编语言,这样设计出来的操作系统可移植性非常高,但是缺点是不能针对某种体系深入优化。
    • 还有一种思路就是基本不考虑可移植性,只对一种体系深度优化,Windows系统就是这样的系统,主要就是针对x86系统优化,但是可移植性极差。

    Linux系统走了一条中间道路,差不多所有的接口和核心代码都是独立于硬件的,但是,对于性能要求很严格的部分,内核会针对不同体系调整,这使得linux在可移植性和性能之间取得比较好的平衡。

    第三个概念 社区

    大家都知道,linux是开源的,社区和代码随时可以访问,只要有兴趣,也可以随时参与社区活动。但是linux入门门槛比较高。需要一个比较长的过程,只要坚持,最终会跨过这个门槛。

    后记

    本文通过十二部分蜓蜓点水式的介绍,希望能够帮助大家能记住并理解几个概念。如果有兴趣更深入的了解,推荐阅读下《Linux内核设计与实现》这本书。

    展开全文
  • 1.1 什么是MSI中断? MSI, message signal interrupt, PCI设备通过写一个特定消息到特定地址,从而触发一个CPU中断。特定消息指的是PCIe总线中Memory Write TLP, 特定地址一般存放在MSI capability中。 和传统...

    在 DPDK 中使用的PCIe 网卡、中断类型为 MSI-X 类型,在此描述 PCIe 中断。

    1. MSI/MSI-X概述
      PCIe有三种中断,分别为INTx中断,MSI中断,MSI-X中断,其中INTx是可选的,MSI/MSI-X是必须实现的。

    1.1 什么是MSI中断?
    MSI, message signal interrupt, 是PCI设备通过写一个特定消息到特定地址,从而触发一个CPU中断。特定消息指的是PCIe总线中的Memory Write TLP, 特定地址一般存放在MSI capability中。
    和传统的INTx中断相比,MSI中断有以下几个优点:
    (1) 基于引脚的传统中断会被多个设备所共享,中断共享时,如果触发了中断,linux需要一一调用对应的中断处理函数,这样会有性能上的损失,而MSI不存在共享的问题。
    (2) 设备向内存写入数据,然后发起引脚中断, 有可能会出现CPU收到中断时,数据还没有达到内存。 而使用MSI中断时,产生中断的写不能越过数据的写,驱动可以确信所有的数据已经达到内存。
    (3) 多功能的PCI设备,每一个功能最多只有一个中断引脚,当具体的事件产生时,驱动需要查询设备才能知道是哪一个事件产生,这样会降低中断的处理速度。而一个设备可以支持32个MSI中断,每个中断可以对应特定的功能。

    1.2 什么是MSI-X中断?
    MSI-x是MSI的扩展和增强。MSI有它自身的局限性,MSI最多支持32个中断,且要求中断向量连续, 而MSI-x没有这个限制,且支持的中断数量更多。此外,MSI-X的中断向量信息并不直接存储在capability中,而是在一块特殊Memory中.

    MSI和MSI-X的规格对比:

    对比项 MSI MSI-X
    中断向量数 32 2048
    中断号约束 必须连续 可以随意分配
    MSI信息存放 capability寄存器 MSI-X Table(BAR空间)

    总之,PCIe设备在提交MSI中断请求时,都是向MSI/MSI-X Capability结构中的Message Address的地址写Message Data数据,从而组成一个存储器写TLP,向处理器提交中断请求。

    在arm64中,MSI/MSI-X对应的是LPI中断, 外设通过写GITS_TRANSLATER寄存器,可以发起LPI中断, 所以相应的,如果在没有使能SMMU时,MSI的message address指的就是ITS_TRANSLATER的地址。

    1. MSI/MSI-X capability
      2.1 MSI capability
      MSI Capability的ID为5, 共有四种组成方式,分别是32和64位的Message结构,32位和64位带中断Masking的结构。
      以带bit mask的capability register为例:
      在这里插入图片描述
      Capability ID :记录msi capability的ID号,固定为0x5.
      next pointer: 指向下一个新的Capability寄存器的地址.
      Message Control Register: 存放当前PCIe设备使用MSI机制进行中断请求的状态和控制信息

    在这里插入图片描述
    MSI enable控制MSI是否使能,
    Multiple Message Capable表示设备能够支持的中断向量数量,
    Multi Message enable表示实际使用的中断向量数量,
    64bit Address Capable表示使用32bit格式还是64bit格式。

    Message Address Register: 当MSI enable时,保存中断控制器种接收MSI消息的地址。
    Message Data Register: 当MSI enable时,保存MSI报文的数据。
    Mask Bits: 可选,Mask Bits字段由32位组成,其中每一位对应一种MSI中断请求。
    Pending Bits: 可选,需要与Mask bits配合使用, 可以防止中断丢失。当Mask bits为1的时候,设备发送的MSI中断请求并不会发出,会将pending bits置为1,当mask bits变为0时,MSI会成功发出,pending位会被清除。
    2.2 MSI-X capability
    MSI-x的capability寄存器结构和MSI有一些差异:
    在这里插入图片描述
    Capability ID:记载MSI-X Capability结构的ID号,其值为0x11
    Message Control: 存放当前PCIe设备使用MSI-x机制进行中断请求的状态和控制信息
    在这里插入图片描述
    MSI-x enable,控制MSI-x的中断使能 ;
    Function Mask,是中断请求的全局Mask位,如果该位为1,该设备所有的中断请求都将被屏蔽;如果该位为0,则由Per Vector Mask位,决定是否屏蔽相应的中断请求。Per Vector Mask位在MSI-X Table中定义;
    Table Size, 存放MSI-X table的大小
    Table BIR:BAR Indicator Register。该字段存放MSI-X Table所在的位置,PCIe总线规范规定MSI-X Table存放在设备的BAR空间中。该字段表示设备使用BAR0 ~ 5寄存器中的哪个空间存放MSI-X table。
    Table Offset: 存放MSI-X Table在相应BAR空间中的偏移。
    PBA(Pending Bit Array) BIR: 存放Pending Table在PCIe设备的哪个BAR空间中。在通常情况下,Pending Table和MSI-X Table存放在PCIe设备的同一个BAR空间中。
    PBA Offset: 该字段存放Pending Table在相应BAR空间中的偏移。

    通过Table BIR和Table offset知道了MSI-Xtable在哪一个bar中以及在bar中的偏移,就可以找到对应的MSI-X table。
    查找过程如下:
    在这里插入图片描述
    查找到的MSI-X table结构:
    在这里插入图片描述
    MSI-X Table由多个Entry组成,其中每个Entry与一个中断请求对应。
    除了msg data和msg addr外,还有一个vector control的参数,表示PCIe设备是否能够使用该Entry提交中断请求, 类似MSI的mask位。

    1. 确认设备的MSI/MSI-X capability
      lspci -v 可以查看设备支持的capability, 如果有MSI或者MSI-x或者message signal interrupt的描述,并且这些描述后面都有一个enable的flag, “+”表示enable,"-"表示disable。
    [root@localhost linux]# lspci -s 00:16.0 -v
    00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01) (prog-if 00 [Normal decode])
    	Flags: bus master, fast devsel, latency 0, IRQ 32
    	Bus: primary=00, secondary=0b, subordinate=0b, sec-latency=0
    	I/O behind bridge: 00005000-00005fff
    	Memory behind bridge: fd300000-fd3fffff
    	Prefetchable memory behind bridge: 00000000e7900000-00000000e79fffff
    	Capabilities: [40] Subsystem: VMware PCI Express Root Port
    	Capabilities: [48] Power Management version 3
    	Capabilities: [50] Express Root Port (Slot+), MSI 00
    	Capabilities: [8c] MSI: Enable+ Count=1/1 Maskable+ 64bit+
    	Kernel driver in use: pcieport
    	Kernel modules: shpchp
    
    1. 设备怎么使用MSI/MSI-x中断?
      传统中断在系统初始化扫描PCI bus tree时就已自动为设备分配好中断号, 但是如果设备需要使用MSI,驱动需要进行一些额外的配置。
      当前linux内核提供pci_alloc_irq_vectors来进行MSI/MSI-X capablity的初始化配置以及中断号分配。
    int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
                    unsigned int max_vecs, unsigned int flags);
    

    函数的返回值为该PCI设备分配的中断向量个数。
    min_vecs是设备对中断向量数目的最小要求,如果小于该值,会返回错误。
    max_vecs是期望分配的中断向量最大个数。
    flags用于区分设备和驱动能够使用的中断类型,一般有4种:

    #define PCI_IRQ_LEGACY		(1 << 0) /* Allow legacy interrupts */
    #define PCI_IRQ_MSI		(1 << 1) /* Allow MSI interrupts */
    #define PCI_IRQ_MSIX		(1 << 2) /* Allow MSI-X interrupts */
    #define PCI_IRQ_ALL_TYPES   (PCI_IRQ_LEGACY | PCI_IRQ_MSI | PCI_IRQ_MSIX)
    

    PCI_IRQ_ALL_TYPES可以用来请求任何可能类型的中断。
    此外还可以额外的设置PCI_IRQ_AFFINITY, 用于将中断分布在可用的cpu上。
    使用示例:

     i = pci_alloc_irq_vectors(dev->pdev, min_msix, msi_count, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
    

    与之对应的是释放中断资源的函数pci_free_irq_vectors(), 需要在设备remove时调用:

    void pci_free_irq_vectors(struct pci_dev *dev);
    

    此外,linux还提供了pci_irq_vector()用于获取IRQ number.

    int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
    
    1. 设备的MSI/MSI-x中断是怎样处理的?
      5.1 MSI的中断分配pci_alloc_irq_vectors()
      深入理解下pci_alloc_irq_vectors() --> pci_alloc_irq_vectors_affinity()
    int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
    				   unsigned int max_vecs, unsigned int flags,
    				   struct irq_affinity *affd)
    {
    	struct irq_affinity msi_default_affd = {0};
    	int msix_vecs = -ENOSPC;
    	int msi_vecs = -ENOSPC;
    
    	if (flags & PCI_IRQ_AFFINITY) {                        
    		if (!affd)
    			affd = &msi_default_affd;
    	} else {
    		if (WARN_ON(affd))
    			affd = NULL;
    	}
    
    	if (flags & PCI_IRQ_MSIX) {
    		msix_vecs = __pci_enable_msix_range(dev, NULL, min_vecs,
    						    max_vecs, affd, flags);               ------(1)
    		if (msix_vecs > 0)
    			return msix_vecs;
    	}
    
    	if (flags & PCI_IRQ_MSI) {
    		msi_vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs,
    						  affd);                             ----- (2)
    		if (msi_vecs > 0)
    			return msi_vecs;
    	}
    
    	/* use legacy IRQ if allowed */
    	if (flags & PCI_IRQ_LEGACY) {
    		if (min_vecs == 1 && dev->irq) {
    			/*
    			 * Invoke the affinity spreading logic to ensure that
    			 * the device driver can adjust queue configuration
    			 * for the single interrupt case.
    			 */
    			if (affd)
    				irq_create_affinity_masks(1, affd);
    			pci_intx(dev, 1);                                 ------ (3)
    			return 1;
    		}
    	}
    
    	if (msix_vecs == -ENOSPC)                
    		return -ENOSPC;
    	return msi_vecs;
    }
    
    

    申请为MSI-X中断

    __pci_enable_msix_range()
    	+-> __pci_enable_msix()
    		+-> msix_capability_init()
    			+-> pci_msi_setup_msi_irqs()
    

    msix_capability_init会对msi capability进行一些配置。
    关键函数pci_msi_setup_msi_irqs, 会创建msi irq number:

    static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
    {
    	struct irq_domain *domain;
    
    	domain = dev_get_msi_domain(&dev->dev);      
    	if (domain && irq_domain_is_hierarchy(domain))
    		return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
    
    	return arch_setup_msi_irqs(dev, nvec, type);
    }
    

    这里的irq_domain获取的是pcie device结构体中定义的dev->msi_domain.
    这里的msi_domain是在哪里定义的呢?
    在drivers/irqchip/irq-gic-v3-its-pci-msi.c中, kernel启动时会:

    its_pci_msi_init()
    	+-> its_pci_msi_init()
    		+-> its_pci_msi_init_one()
    			+-> pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,parent)
    

    pci_msi_create_irq_domain中会去创建pci_msi irq_domain, 传递的参数分别是its_pci_msi_domain_info以及设置parent为its irq_domain.
    所以现在逻辑就比较清晰:
    在这里插入图片描述
    gic中断控制器初始化时会去add gic irq_domain, gic irq_domain是its irq_domain的parent节点,its irq_domain中的host data对应的pci_msi irq_domain.

            gic irq_domain --> irq_domain_ops(gic_irq_domain_ops)
                  ^                --> .alloc(gic_irq_domain_alloc)
                  |
            its irq_domain --> irq_domain_ops(its_domain_ops)
                  ^                --> .alloc(its_irq_domain_alloc)
                  |                --> ...
                  |        --> host_data(struct msi_domain_info)
                  |            --> msi_domain_ops(its_msi_domain_ops)
                  |                --> .msi_prepare(its_msi_prepare)
                  |            --> irq_chip, chip_data, handler...
                  |            --> void *data(struct its_node)
    

    pci_msi irq_domain对应的ops:

    static const struct irq_domain_ops msi_domain_ops = {
            .alloc          = msi_domain_alloc,
            .free           = msi_domain_free,
            .activate       = msi_domain_activate,
            .deactivate     = msi_domain_deactivate,
    };
    

    回到上面的pci_msi_setup_msi_irqs()函数,获取了pci_msi irq_domain后, 调用msi_domain_alloc_irqs()函数分配IRQ number.

    msi_domain_alloc_irqs()
    	// 对应的是its_pci_msi_ops中的its_pci_msi_prepare
    	+-> msi_domain_prepare_irqs()
    	// 分配IRQ number
    	+-> __irq_domain_alloc_irqs()
    

    msi_domain_prepare_irqs()对应的是its_msi_prepare函数,会去创建一个its_device。
    __irq_domain_alloc_irqs()会去分配虚拟中断号,从allocated_irq位图中取第一个空闲的bit位作为虚拟中断号。
    至此, msi-x的中断分配已经完成,且msi-x的配置也已经完成。

    5.2 MSI的中断注册
    kernel/irq/manage.c

    request_irq()
        +-> __setup_irq()
        	+-> irq_activate()
       			+-> msi_domain_activate()
       				// msi_domain_info中定义的irq_chip_write_msi_msg
            		+-> irq_chip_write_msi_msg()
            			// irq_chip对应的是pci_msi_create_irq_domain中关联的its_msi_irq_chip
                		+-> data->chip->irq_write_msi_msg(data, msg);
                				+-> pci_msi_domain_write_msg()
    
    

    从这个流程可以看出,MSI是通过irq_write_msi_msg往一个地址发一个消息来激活一个中断。

    参考链接:
    https://blog.csdn.net/yhb1047818384/article/details/106676560

    展开全文
  • 中断系统中断概念什么是中断中断系统中断优点51单片机中断分类中断控制位中断请求标志中断优先级51单片机的中断优先级有三条原则中断处理过程 中断概念 什么是中断 CPU在处理某一事件A时,发生了另一事件B请求CPU...
  • 2.FIQ或IRQ异常返回指令是什么?答案见下面。。。。3.什么类型异常优先级最高?复位(Reset)4.什么指令可以放在中断向量表?①B 指令(优点:操作方便;缺点:范围PC正负32MB) 为什么BL不可以?②MOV (优点4G范围内...
  • (四)中断与异常

    2020-10-06 16:43:20
    1、 中断控制主要优点: CPU只有在I/O需要服务时才响应 2、什么是外部中断、内部中断中断向量、异常? 外部中断:外部设备所发出I/O请求 内部中断:也称之为“异常”,为解决机器运行时所出现某些随机事件...
  • 中断系统的优点: 分时操作: CPU可以分时为多个I/O社保服务,提高了计算机的利用率; 实时响应: CPU能够及时处理应用系统的随机事件,系统的实时性增强,没有中断,CPU必须循环处理,等到下次循环到了才能处理...
  • 什么是协程

    万次阅读 2019-05-09 21:22:49
    协程相对于多线程的优点 多线程编程比较困难的, 因为调度程序任何时候都能中断线程, 必须记住保留锁, 去保护程序中重要部分, 防止多线程在执行的过程中断。 而协程默认会做好全方位保护, 以防止中断。我们...
  • 1) 中断控制是为克服对I/O接口采用程序查询控制服务方式所带来处理器低效率而产生,它主要优点是只有在I/O接口需要服务时才能得到处理器响应,而不需要处理器不断地进行查询;因此,最初
  • 我们首先需要了解什么是中断?...中断的优点? 1.分时操作。 2.实时响应 3.可靠性高 中断中用到的寄存器: 中断优先级寄存器IP 位序号 D7 D6 D5 D4 ...
  • 什么是GIT

    2020-11-06 17:36:29
    与 SVN 等其他版本控制系统(VCS)相比,其分布式架构具有许多优势,一个主要优点是它不依赖于中央服务器来存储项目文件所有版本。 每个开发人员都可以“克隆”我在图中用“Local repository”标注存储库...
  • 什么是NAPI

    千次阅读 2012-12-10 13:24:34
    数据量很低与很高时,NAPI可以发挥中断方式与轮询方式的优点,性能较好。如果数据量不稳定,且说高不高说低不低,则NAPI会在两种方式切换上消耗不少时间,效率反而较低一些。   下面会用到netdev_priv()这个函数...
  • 在前两篇文章中我们介绍了IO口模拟串口发送数据和接收数据,前两种方法都是使用定时器来进行发送和接收,没有用到中断优点是逻辑简单,但是缺点很明显,只能进行单个字节发送和接收,而且不能同时工作。...
  • 说到双机热备也许很多人都不是很了解,但是对于技术管理人员来说这十分常见。当业务系统非常重要,不允许出现中断或故障,往往就需要用到双机热备。以添添呼呼叫中心系统为例,我们为了保障客户在使用系统时候...
  • 当初ftp提出主要为了解决在恶劣网络环境下文件传输,比如文件传输中断怎么办,文件传输错误怎么办,文件传输异常中断退出了怎么办,文件传输是否加密、安全?等等这些问题,ftp协议都能够处理比较好。鉴于...
  • 目录锁类型可中断锁在等待获取锁过程中可中断Lock就是可中断锁公平锁/非公平锁公平锁指多个线程按照申请锁的顺序来获取锁。非公平锁指多个线程获取锁的顺序...非公平锁的优点在于吞吐量比公平锁大。对于Synchro...
  • 优点是1、更直观 2、程序运行占用资源少 直接对寄存器某些位进行置1或清0操作,能清晰看到驱动代码使用了什么寄存器,没有库函数层,省去代码为分层而消耗资源 库函数开发方式:用结构体封装寄存器参数,用宏...
  • 真空断路器具有维护工作量小,断流容量大,适宜频繁操作等许多优点,在电力系统中逐渐取代了其它类型开关,得到了广泛应用。但是,真空断路器在使用中发生操作过电压问题,亦引起了人们注意和担心。因此探讨...
  •  STC单片机一款增强型51单片机,完全兼容MCS-51,还增加了新功能,比如新增两级中断优先级,多一个外中断,内置EEPROM,硬件看门狗,具有掉电模式,512B内存等。还支持ISP下载,不用编程器,只要一个MAX232和...
  • 当然已知不利之处在于PHP由于开放源码项目,没有什么商业支持,并且由此而带来执行速度缓慢(直到PHP4之前)。但是PHP邮件列表很有用而且除非你正在运行像Yahoo!或者Amazon.com这样极受欢迎站点,你...
  • 优点是什么? 3、程序并发执行时特征是什么? 4、进程与程序是两个完全不同概念,但又有密切联系,试写出两者区别。 5、进程有哪几种基本状态?试画出进程状态转换图。 6、作业调度...
  • 内核版本:Linux-2.6.34 网卡驱动:B4401 ...数据量很低与很高时,NAPI可以发挥中断方式与轮询方式的优点,性能较好。如果数据量不稳定,且说高不高说低不低,则NAPI会在两种方式切换上消耗不少时间,效率反
  • 什么是中断中断有何优点 中断是指计算机在执行程序过程中由于计算机系统外界或内部发生某一紧急事件要求CPU暂时中止当前工作转去处理所发生紧急事件待处理完后再回到原来被中止地方继续原来工作过程 在...
  • 线程介绍

    2019-08-15 21:08:12
    2:多线程的优点 1:有效利用线程睡眠的时间(例如等待网络相应) 2:有的操作会消耗大量时间,如果只有一个线程,人机交互会被中断;而多线程可以分别负责交互和计算 3:程序逻辑本身要求并发操作 4:多cpu...
  • erlangGC(一)--binary

    2015-08-31 17:02:01
      我自己写的文章居然被别人的博客转载了,然后大言不惭的说作者他,真是服了。。。...所以GC时整个系统不会产生中断,这的优点。那么既然有GC为什么还会有内存的增长呢?? 当我们运...
  • 本文旨在通过对恩智浦半导体智能电表解决方案介绍,使大家对非接触式预付费智能表系统有一个整体认识。本文还对一些常见问题... 电网老化和近年来屡次出现电力中断现象很清楚地显示出输电网使用已经达到极
  • 异地多活的优点:功能强大、提供更好的体验、可以减少业务中断带来的损失 异地多活的缺点:代价高昂、设计复杂 二、应用场景 无法承受异地多活带来的复杂度和成本时可以只做异地备份,不做异地...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 175
精华内容 70
关键字:

中断的优点是什么