《linux内核设计与实现》学习笔记——中断中断处理程序

xcy6666 xcy6666     2022-07-30     521

关键词:

中断和中断处理程序

  • 中断随时可能产生,打断CPU的执行,CPU转而处理中断。
  • 不同的设备对应的中断不同,每个中断都通过一个唯一的数字标志。
    • 这些中断值称为中断请求(IRQ)线,每个irq线关联一个数值。
  • 中断处理程序
    • 响应中断时,内核会执行一个函数,中断处理程序/中断服务例程ISR, 一个设备的中断处理程序是他的设备驱动的一部分。
  • IO资源包括 : 中断,I/O端口,共享RAM,DMA。驱动程序需要管理注册释放这些资源。

上半部:接收到中断就立即执行,只做有严格时限的工作,如对中断应答或复位硬件。
下半部 : 能够被允许稍后完成的工作推迟到下半部执行。

  • 注册中断处理程序
    • Request_irq(uint irq, irq_handlet_t handler, ulong flasgs,void* dev)注册中断处理程序,激活给定的中断线;这个函数可能睡眠,不能在中断上下文/其他不允许阻塞的代码中执行。
  • 卸载驱动程序时
    • 注销相应中断处理程序,释放中断线。 void free_irq(uint irq, void* dev);
  • Linux的中断处理程序是无需重入的,给定的中断处理程序在执行时,相应中断线在所有处理器上都会被屏蔽

中断上下文:没有后备进程,不可睡眠。中断处理程序打断了其他的代码。

中断处理机制的实现

这里写图片描述

  • 设备产生中断,通过总线把电信号发送给中断控制器
  • 如果中断线是激活的,中断控制器发送中断到处理器(处理器特定管脚)
  • 如果处理器没有禁止该中断,处理器会停止正在做的事关闭中断系统,调到预定义的中断处理程序入口。
  • 每条中断线调到一个唯一位置。初始入口点保存这个中断线号,存放寄存器的值调用do_irq()
    • 计算出中断号,对中断应答,禁止这条线上的中断传递
    • 确保这条中断线上有一个有效的处理程序,已经启动,但没有执行。
    • 调用handle_IRQ_event()调用中断线安装的中断处理程序。
    • 将中断禁止,返回到do_IRQ。
    • 做清理工作,返回初始入口点,跳到ret_from_intr()
  • 检查是否调度挂起;恢复寄存器;内核恢复到中断的点。

禁止当前处理器中断和激活中断,

Local_irq_disable();local_irq_enable();
Unsigned long flags; local_irq_save(flags);local_irq_restore(flags);

禁止指定中断

Disable_irq(int);禁止中断向所有处理器的中断。

中断处理程序上、下半步处理逻辑分配原则:

  • 上半部:
    • 任务对时间非常敏感
    • 任务和硬件相关
    • 任务保证不被其他中断打断,不并发,不阻塞
  • 下半部:
    • 对时间不敏感
    • 和硬件无关
    • 可以被其他中断打断,可以睡眠,可以并发

Linux的上半部就是中断处理程序,下半部有多种机制:

软中断

软中断是一组静态定义的下半部接口,有32个,可以在所有处理器上同时执行,类型相同也可以;在编译时静态注册。

实现:

struct softirq_action{ //<linux/interrupt.h> 表示软中断
    void (*action)(struct softirq_action*);
}

32个目前用了6个。

static struct softirq_action soft_irq_vec[NR_SOFTIRQS];//kernel/softirq.c软中断数组
  • 中断处理程序:内核运行软中断处理程序的时候,执行action函数。
    一个软中断不会抢占另外一个软中断。唯一可以抢占软中断的是中断处理程序。其他的软中断甚至同类型的可以在其他处理器上同时执行
  • 执行软件中断:一个注册的软件中断在标记后才会执行,这称作触发中断。
    中断处理程序在返回前标记软中断。
    在:硬件中断代码返回时;在ksoftirq内核线程中;显示检查执行软中断 处,待处理的软中断会被检查和执行

软中断在do_softirq中执行

u32 pending;
pending = local_sofqirq_pending();
if(pending){
    struct softirq_action* h;
    set_softirq_pending(0);
    h = softirq_vec;
    do{
        if(pending & 1){
            h->action();
        }
        h++;
        pending >>=1;
    }while(pending);
}

使用软中断

软中断留给对时间要求最严格及最重要的下半部使用。目前只有网络,scsi使用内核定时器和tasklet都建立在软中断上。

  • 通过枚举类型静态声明软中断,并分配索引
  • 注册处理程序
    • open_softirq(NET_TX_SOFTIRQ,net_tx_action);软中断处里程序执行时,允许响应中断,但不能睡眠。由于只禁止当前处理器上的运行,其他处理器可以同时运行处理程序,需要加锁保护。
  • rase_softirq(NET_TX_SOFTIRQ)将软中断设置为挂起状态,下次再调用do_softirq时执行。

Tasklet

基于软件中断实现的,灵活性强,动态创建的下半部实现机制。两个不同类型的tasklet可以在不同处理器上运行,但相同的不可以。可以通过代码动态注册。

实现:基于软中断

struct tasklet_struct {
  struct tasklet_struct *next;
  unsigned long sate;//(0/TASKLET_STATE_SCHED/TASKLET_STATE_RUN)
  atomic_t count;/*引用计数器,0允许执行,否则禁止*/
  void (*func)(unsigned long);/*执行函数*/
  unsigned long data;//func的参数
};

调度:每个处理器有tasklet_vec和tasklet_hi_vec结构,分别为低、高优先级tasklet_strucu链表

由tasklet_schedule()和tasklet_hi_schdule()进行调度

  • 检查tasklet是否为TASKLET_STATE_SCHED.如果是返回
  • 调用_tasklet_schedule
  • 保存中断状态,禁止本地中断
  • 把需要调度的处理器加到tasklet_vec 或tasklet_hi_vec链表的头上
  • 唤起TASKLET_SOFTIRQ或TASKLET_HI_SOFTIRQ软中断
  • 恢复中断状态并返回

软中断处理程序:

tasklet_action(),tasklet_hi_action()的操作

  • 禁止中断
  • 将当前处理器置为null,清空链表
  • 允许中断
  • 循环遍历链表每一个待处理的tasklet
    • 如果是多处理器系统,判断是否TASKLET_STATE_SCHED,如果在运行,跳过。
    • 如果未在执行,设置TASKLET_STATE_RUN。
    • 检查count==0,否则tasklet被禁止,跳过。
    • 执行tasklet,清空TASKLET_STATE_RUN标志
    • 执行下一个tasklet

使用tasklet

  • 声明tasklet:
    • DECLARE_TASKLET(name,func,data)DECLARE_TASKLET_DISABLED(.)
      tasklet_init(t, tasklet_handler, dev);
  • 编写处理程序:因为是依靠软中断实现的,处理程序不能睡眠。Tasklet允许响应中断。
  • 调用tasklet_schedule(&my_tasklet);调度tasklet,实际上是标记/挂起,只要有机会,my_tasklet就会尽快执行。

tasklet_disable(&my_tasklet); tasklet_enable(&my_tasklet);禁止和激活

折衷

频繁中断或tasklet频繁发生的时候:尽快处理,用户进程得不到响应;滞后执行,中断处理也不快。
==》》使用低优先级核心进程专门处理软中断ksoftirqd/n

for(;;){
    if(!softirq_pending(cpu)){
        schedule();
    }
    set_current_state(TASK_RUNNING);
    while(softirq_pending(cpu)){
        do_softirq();
        if(need_schdule())schedule();
    }
}

工作队列:

将下半部功能交由内核线程执行,有着线程上下文环境,可以睡眠。
提供创建worker threads的接口,提供接口把需要推后执行的任务排到队列里,提供默认的工作者线程处理排到队列里的下半部工作。

实现:

数据结构

  • 每种工作者线程有一个workqueue_struct结构
  • 里面有NR_CPUS个cpu_work_queue_strcut对应于每个处理器的一个工作者线程
  • 工作用work_struct表示,含有一个执行函数fuc,每个cpu的工作线程都对应一个work_struct链表。

worker_thread()的核心,是一个死循环

  • 线程将自己放置为休眠状态
  • 若链表为空,休眠
  • 不为空,调用run_workqueue函数执行工作。
    • 链表不为空时,循环执行
      • 选取下一个节点对象,获取执行函数和参数
      • 待处理标志清0
      • 调用函数
    • 重复执行

使用:

  • 创建推后的工作
    DECLARE_WORK(name, void(func)(void), void*data);静态
    INIT_WORK(strut work_struct* task,…);动态
  • 工作队列处理函数
    运行于进程上下文,允许响应中断,不持有锁,可以睡眠。不能访问用户空间
  • 对工作进行调度
    schedule_work(&work);提交给工作者进程
    schedule_delay_work(&work,delay)
  • 刷新操作
    flush_scheduled_work(),函数等待队列中所有对象都被执行以后返回
  • 创建新的工作队列
    如果缺省的队列不能满足你的需要,你应该创建新的工作队列和与之相对应的工作线程。

各种机制的比较

下半部上下文顺序执行保障
软中断中断没有
Tasklet中断同类型不能同时执行
工作队列进程没有(和进程上下文一样,被调度)

如果任务需要推后到进程上下文完成,有休眠的需要 工作队列
任务队列接口简单,同种类型不能同时执行 tasklet
软中断提供的执行序列化的保障最少,必须格外小心采取一些步骤确保共享数据

《linux设计与实现》学习笔记——定时器和时间管理

...驱动事件的准确度;调度的粒度更细,进程抢占更准确;内核定时器以更高的频度和准确度执行。 查看详情

《linux内核设计与实现》笔记——内核同步简介

相关概念竞争条件多个执行线程(进程/线程/中断处理程序)并发(并行)访问共享资源,因为执行顺序不一样造成结果不一样的情况,称为竞争条件(racecondition)举例说明#include<thread>usingnamespacestd;inti=0;voidthread1(){//for(intx=0;x&... 查看详情

《linux内核设计与实现》学习笔记——i/o调度算法

I/O调度子系统用于调度来自多个进程对块设备的I/O请求。电梯调度首先,如果队列中已存在一个对相邻磁盘扇区操作的请求,那么新请求将和这个已经存在的请求合并为一个请求。2.如果队列中存在一个驻留时间过长的请求,那... 查看详情

linux驱动学习笔记(一linux架构)

...备管理等。Linux框架:应用程序通过系统调用来使用内核资源,系统调用一般通过软中断的方式。系统调用:Linux内核中提供的一组用于实现各种系统功能的子程序,由操作系统核心提供,运行于内核态。C库函... 查看详情

stm32f103xdatasheet学习笔记---interruptsandevents

...章主要介绍STM32中断和事件相关的内容2.NVICNVIC管理着包括内核异常等中断主要特性68个外部中断源(不包含16个内部中断线)可编程优先级为16级低延迟异常和中断处理电源管理控制系统控制寄存器的实现嵌套向量中断控制器(NVIC)... 查看详情

gd32f103学习笔记——exti(外部中断)接口使用(代码片段)

...低延迟的异常和中断处理,以及电源管理控制。它和内核是紧密耦合的。更多关于NVIC的说明请参考《Cortex-M3技术参考手册》。EXTI(中断/事件控制器)包括20个相互独立的边沿 查看详情

51单片机学习笔记5--外部中断(代码片段)

外部中断1.外部中断概念2.引脚功能复用3.中断号和中断类型4.外部中断编程1.外部中断概念外部中断是单片机实时地处理外部事件的一种内部机制。当某种外部事件发生时,单片机的中断系统将迫使CPU暂停正在执行的程序࿰... 查看详情

t6中断编程(代码片段)

1.中断设备树1.1概述在linux内核中通过中断号管理中断,与外设一一对应中断发生后会有中断回调函数,在linux中通过``函数处理中断外围设备大部分会产生中断,soc为了方便管理与区分中断,故设计了中断控制器GIC(**GenericInterruptControll... 查看详情

《内核设计与实现》第一章读书笔记

《内核设计与实现》第一章读书笔记第一章:Linux内核简介1.1Unix的历史Unix强大的特点A.简洁,几百个系统调用,明确的设计目的B.文件对待所有东西C.移植性强(C语言)D.进程创建快,使用fork()系统调用。E.进程间通信元语,进程... 查看详情

小林coding阅读笔记:操作系统篇之硬件结构,中断问题(代码片段)

...中断?学习意义学习CPU与外设如何去提升处理效率的设计思想,异步机制的理解与借鉴掌握相关的Linux命令,帮助问题排查相关说明该篇博文是个人阅读的重要梳理,仅做简单参考,详细请阅读小林coding的原文... 查看详情

《linux内核设计与实现》读书笔记从内核出发(代码片段)

内核源码获取①可以直接登录linux内核官方网站http://www.kernel.org,可以随时获取当前版本的linux源代码②也可以使用git工具从远程仓库下载,地址:LinuxKernel:Linux内核源码镜像如:gitclonegit@gitee.com:mirrors/linux_old1.git这... 查看详情

《linux内核设计与实现》读书笔记linux内核简介

Unix的历史①Unix诞生于1969年,至今仍然被认为是现存操作系统中最强大和最优秀的系统。②Unix起源于一个失败的多用户操作系统Multics,Multics终止而Unix萌生。③1973年整个Unix操作系统用C语言进行了重写,为后面各种... 查看详情

一起分析linux系统设计思想——05中断框架剖析(代码片段)

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系... 查看详情

linux驱动开发-中断分层机制笔记6(代码片段)

...急处理的,任务繁琐的)则等待CPU空闲时,既内核线程觉得有能力运行时才去执行。注意事项i.中断上下文代码绝对不可以停止运行中断处理程序不能调用schedule_timeout()等随眠函数放弃CPU在中断处理函数中调用一个内... 查看详情

linux驱动开发-中断分层机制笔记6(代码片段)

...急处理的,任务繁琐的)则等待CPU空闲时,既内核线程觉得有能力运行时才去执行。注意事项i.中断上下文代码绝对不可以停止运行中断处理程序不能调用schedule_timeout()等随眠函数放弃CPU在中断处理函数中调用一个内... 查看详情

《linux内核设计与实现》读书笔记-内核同步方法(代码片段)

...3.读写自旋锁4.信号量5.读写信号量6.互斥体7.完成变量8.大内核锁9.顺序锁10.禁止抢占11.顺序和屏障12.总结内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我们在编写内核代码 查看详情

《linux内核设计与实现》笔记——vfs

关于VFS有一篇很好的博客http://www.ibm.com/developerworks/cn/linux/l-vfs/建议先阅读本文为基础,然后继续阅读该文章。VFS,虚拟文件系统,为用户提供了文件和文件系统相关的接口。这些接口可以跨越各种文件系统和不同介质执行。VFS... 查看详情

socket与系统调用深度分析(代码片段)

...们简单分析了这个模型,本节里面我们将在此基础上深入内核里分析。 二.系统调用与中断相关概念这里我们会涉及到一些概念,先让我们熟悉一下它们!用户空间:指的就是用户可以操作和访问的空间,这个空间通常存放我... 查看详情