• Linux内核中断处理机制


    <什么是中断>
    计算停下当前处理任务,并保存现场,转而去处理其他是任务,当完成任务后再回到原来的任务中去。
    <中断的分类>
    a:软中断
        软中断时执行中断指令产生的,软中断不用施加中断请求信号,因此中断的产生的不是随机的而是由程序安排的。内核线程是实现软中断的助手。
    b:硬中断
        硬中断时由外部硬件产生的,具有随机性。
     
    <中断的实现>
    int request_irq(unsigned int irq,irq_handler_t handler,unsigned long intflags,const char devname, void *dev_id)
    解释:
    irq:申请的中断号。
    handler:中断处理函数指针
    irqflags:中断处理属性,与上半段和下半段有关系
    devname:中断设备名字
    dev_id:与共享中断号有关系。
    注:该函数的主要作用是在内核中的一个重要的结构体"irq_desc"中注册中断号与对应的中断处理函数。
     
    <释放中断线>
    void free_irq(unsigned int irq ,void dev_id);
    注:一般操作硬件的端口都会调用ioremap()函数,该函数用来将计算实际的物理地址映射成虚拟地址,这样系统才能访问。
     
    <中断处理机制之上半部分和下半部>
    a:Linux中中断处理是一种很霸道的东西,只要没有屏蔽中断,CPU就会立即相应。为了加开处理的数据,Linux中通常将 中断处理中的硬件相关的操作放在上半部分,耗时的操作放在下半部分——linux 内核一般将中断处理分为两不份:上半部(top_half)和下半部(bottom_half)。
    b:上半部分
        一般调用中断处理函数,一进入中断处理函数就是进行相应的硬件操作,这些都是放在上半部分。然后将耗时的操作放在下半部分中。
    c:下半部分
    Linux中实现下半部分的处理有:softirq机制,tasklist机制(小任务片段),workqueue机制
    ----tasklet将任务延迟到安全时间执行,每个tasklet都和一个函数相关联,当tasklet运行时,
    ----该函数就被调用,并且tasklet可以调度自己。
    d:Tasklet的实现
    (1)定义一个处理函数:
    void tasklet_func(unsigned  long);
    定义一个tasklet结构my_tasklet与tasklet_func(data)函数关联
    struct tasklet_struct  my_tasklet;
    DECLARE_TASKLET(my_tasklet,tasklet_func,data);
    调度tasklet
    tasklet_schedule(&my_tasklet);
    e:工作队列和下半部处理
    工作队列使用方法和tasklet非常相似
    (1)定义一个工作队列:
    (2)struct work_struct  my_wq;
    定义一个处理函数
    void my_wq_func(struct work_struct  *work);
    (3)初始化工作队列并将其处理函数绑定
    INIT_WORK(&my_wq,my_wq_func);
    (4)调度工作队列执
    schedule_work(&my_wq);//该函数将其提交给内核默认工作者队列线程
    queue_work();//该函数会将其提交给专用的工作者线程(可以是自己定义的)
    f:Tasklet和工作队列的区别
    (1)tasklet工作在中断上下文
    (2)工作队列工作在进程上下文
    (3)tasklet处理函数中不能睡眠
    (4)工作队列中允许有睡眠
     
    注:软中断和tasklet
    (1)软中断定义
    struct softirq_action{
    Void(*action)(struct softirq_action *)
    并且当前内核中的软中断总数固定为32个,由数组Static struct softirq_action softirq_vec[NR_SOFTIRQS]来表示。
    目前只用到了其中的9个。包括定时器、网络、tasklet等。一旦有软中断产生就会查询soft_vec[]看查看哪个软中断被挂起了。然后执行:
    h=softirq_vec;
    h->action(h) ;//执行对应的软中断action函数。
    (2)软中断的使用
    open_softirq()挂起软中断处理函数:
    open_softirq(TASKLET_SOFTIRQ,tasklet_action);//tasklet_action位软中断处理函数
    (3)触发软中断
    rase_softirq(TASKLET_SOTFIRQ);//处理器就会在适当的时候执行软中断处理函数tasklet_action
    总结:tasklet(小任务)是对软中断的一种个封装。
     

    linux 驱动中的中断处理程序:

    当发生中断的时候,无论是裸机程序还是Linux系统都会有一个统一的入口.

    裸机中的中断入口是代码:ldr pc,_irq;

    linux 系统中的统一入口:irq_svc

     

    Linux系统注册中断处理程序想:

    requst_irq(unsigned int irq ,void(*handler)(int ,void *,struct pt_regs*),unsigned long flags,const char *devname ,void *dev_id )

     

    参数分析:

    unsigned long flags:参数还是一个宏,用于决定是快速中断还是慢速中断,或者表明该中断是多少个设备共享。

    unsigned int irq:

     

    Linux系统注销中断处理程序:

    void free_irq(unsigned int irq ,void *dev_id)

     

    参数分析:

    int irq:中断号,注意这里的中断号和裸机中的中断号有一定的不同,因为系统预留了16个的软中断号,所以硬件中断号需要加上16,系统通过中断号找到相应的描述符表,在描述符表中找到相应个处理函数。

     

    dev_id:对于共享中断号的设备,需要提供相应的中断号才能准确的注销掉。

     

    中断嵌套:

    为了解决中断处理速度,Linux中将硬件处理函数和非硬件处理函数进行了分开,将非硬件操作放到工作队列中。

     

    注意:工作队列中的结构体数组名都是使用一个struct 在后面

    定义和描述中断队列:

    struct workqueue_struct {

    struct cpu_workqueue_struct *cpu_wq;

    struct list_head list;

    const char *name; /*workqueue name*/

    int singlethread;

    int freezeable; /* Freeze threads during suspend */

    int rt;

    }

     

    定义和描述一个工作:

    struct work_struct {

    atomic_long_t data;

    struct list_head entry;

    work_func_t func;

    };

     

    创建一个工作队列:
    srtuct workqueue_struct * =create_workqueue(workqueue_name)

     

    初始化工作:

    INIT_WORK(struct work_struct *,func)

    注意:创建工作实质是将创建的工作和相应的操作函数关联起来

     

    Linux系统提交工作

    queue_work(struct workqueue_struct , func)

     

     

    在大多数情况下,并把需要定义工作队列,Linux内核中已经有一个默认的工作队列keventd_wq,所以只需要创建工作,并初始化工作。

     

    提交默认工作队列:

    schedule_work()

     

     

    按键去抖之内核定时器:

     

    定义并描述定时器:

    struct timer_list {

    struct list_head entry;

    unsigned long expires;

    void (*function)(unsigned long);

    unsigned long data;

    struct tvec_base *base;

    };

     

    初始化定时器:

    init_timer(struct list_timer * keytimer)

    keytimer.function = key_timerfunc()

     

    找一个地方定义timerfunc()

     

    Linux注册定时器:

    add_timer(struct timer_list *)

     

    启动定时器:

    mod_timer(struct list_timer * ,jiffes+Hz/2)

     

     

    阻塞型驱动:

    背景:

    当计算机驱动访问一个硬件的时候发现访问条件不满足,称之为阻塞,这时就需要将该驱动放入到等待队列中。

     

    定义等待队列:

    wait_queue_head_t  my_queue

    初始化等待队列:  

    init_waitqueue_head( wai_queue_head_t* )

     

    备注:定义并初始化等待队列

    DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t  my_queue)

     

    进入等待队列:

    wait_event(queue,condition)  //TASK_UNINTERRUPT 模式

     

    wait_event_interrupt(queue,condition)//TASK_INTERRUPT  模式

     

     

    int wait_event_killable(queue,condition) //TASK_KILLABLE 模式

     

    函数分析:

    以上两个函数,在条件满足时,直接返回,并继续执行相应的下面的程序,当条件不满足时则挂载到等待队列中,知道被唤醒。

     

    唤醒等待队列:

    wake_up(wait_queue_t *q)  //从等待队列中唤醒,所有TASK_UNINTERRUPT ,TASK_INTERRUPT ,TASK_KILLABLE 状态的所有进程。

     

    wake_up_intterruptible(wait_queue_t *q)//从等待队列q中唤醒状态仅为INTERRUPTIBLE 状态的队列。

     

     

     

     

    设备驱动在内存中开辟内存:

    kmalloc()

    注意:该函数返回的是物理地址

     

    释放内存:

    kfree()

     

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

  • 相关阅读:
    python学习之模块补充二
    MySQL的表关系
    初识数据库
    MySQL基础
    死锁 递归锁 信号量 Event事件 线程q
    进程池/线程池与协程
    线程
    进程相关知识点
    python 之多进程
    socket 基础
  • 原文地址:https://www.cnblogs.com/big-devil/p/8589953.html
Copyright © 2020-2023  润新知