中断下半部机制和推后的工作

                                     《Linux内核的设计与实现》 第8章

8.1 下半部

8.1 划分上下半部的原因:

                        上半部,即中断处理程序,对时间反应迅速,及时响应;

                        下半部,具体处理对硬件中断反应需要做的事情,强调的是做更多的事情,是一种延后处理的机制

8.1.2 下半部四种实现策略

          分别是软中断tasklet工作队列workqueue一个普通线程   (忽略BH,任务队列task queues)

 

8.2 软中断

      软中断的实现      kernel/softirq.c 

struct softirq_action {
     void (*action)(struct softirq_action *);  
};

系统中最多注册32个软中断:
static struct softirq_action softirq_vec[32];                //kernel/softirq.c

步骤:

       1.注册一个软中断                                                     //分配索引,设置优先级

       2.写一个软中断处理程序softirq_handler

       3.执行软中断do_softirq()                                    //do_softirq的原理是检测当前pending位,不断判断移位,执行数组内softirq_action的已经注册的action函数。

软中断使用场景及原因:

       对时间严格并且程序员能高效加锁处理同步,常用的地方是网络子系统。其他地方用的很少。

       原因:软中断能响应中断,但不能休眠,也就是不能调度。缺点是多处理器时,一个cpu禁止了软中断,另一个cpu依旧可以触发软中断,有同步的问题,需要考虑加锁的问题。

8.3 tasklet

          tasklet是基于软中断实现的一种机制,对锁的要求比较低。

          优先级:HI_SOFTIRQ优先TASKLET_SOFTIRQ执行。

结构体:
struct tasklet_struct {
    struct tasklet_struct *next;                                //链表连起来
    unsigned long state;                                         //当前状态,三种
    atomic_t count;                                              //计数器
    void (*func)(unsigned long);                                //处理函数
    unsigned long data;                                        //传给处理函数的参数
};

state: 0, TASKLET_STATE_SCHED、TASKLET_STATE_RUN
count: 为0时,tasklet才被激活


//从这里可以看出tasklet的优点,它可以被调度(被打断的意思,有上下文保存与恢复)。

调度:

         tasklet_schedule(), tasklet_hi_schedule(), 传入一个tasklet_struct指针。

调度实现步骤:

         1.检查结构体state是否为TASKLET_STATE_SCHED,若是,则说明已经被调度了。

         2.调用_tasklet_schedule()                         //封装了一层

         3.保存中断状态,禁止本地中断。            //原理是softirq,呼应上文softirq可以在多处理上同时运行的缺点。禁止中断也是一种同步机制

         4.tasklet加到vec链表表头。                     //代表已经处理过

         5.唤起TASKLET_SOFTIRQ或HI_SOFTIRQ软中断,下次执行do_softirq,执行该tasklet的处理函数。

         6.恢复中断状态

总结:

        这里很清晰看到tasklet是基于softirq实现的,就是在softirq的基础上加上同步机制,保存并恢复中断状态

        另一个感悟就是,为什么书上说基本都建议使用tasklet,因为tasklet就是带同步机制的softirq,就不需要程序员处理锁机制。

         还有就是网络子系统,它是不要同步机制的,处理次数过多,会浪费很多时间,所以它要的是一种相对原始简单的结构,所以网络子系统选择的是softirq。

8.3.2 使用tasklet

1.声明tasklet

   DECLARE_TASKLET(name, func, data);                           //此处都为静态创建

   DECLARE_TASKLET_DISABLED(name, func, data);

例子:

    DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev) ;

    等价于

    struct tasklet_struct my_tasklet = {NULL, 0, AOTMOCI_INIT{0}, my_tasklet_handler, dev}       

动态创建:

    tasklet_init(t, tasklet_handler, dev);                      //程序运行时把 tasklet 传给 t

2.写处理函数handler

void tasklet_handler(unsigned long data);

3.调度自己的tasklet

  tasklet_schedule(&my_tasklet);                 //挂起tasklet, 有机会它自己会运行

8.4 工作队列

         工作队列把下半部推后,交由一个内核线程去执行,在进程上下文中执行,所以可以重新调度睡眠

softirq:        无锁机制,不能调度,不能睡眠。        

tasklet:       有锁机制,能调度,不能睡眠

工作队列:无锁机制,能调度,能睡眠

//这里书上也说了,工作队列可以用内核线程代替

8.4.2 使用工作队列

1.创建推后的工作                             //想法:用宏函数,快速填写参数

   //静态创建一个名为name, 处理函数为func, 参数为data的work_struct

    DECLARE_WORK(name, void(*func)(void*), void* data);  

   //动态创建

   INIT_WORK(struct work_struct *work, void(*func)(*void), void *data);

2.工作队列处理函数

    void work_handler(void *data)

   //书上说这个函数由一个工作者线程处理,运行在进程上下文,不持有锁,可以睡眠。

  //

3.对工作队列进行调度

  schedule_work(&work);     //调度了不一定立马运行,只有其处理器上的工作者线程被唤醒,才会执行

schedule_delay_work(&work, delay);   //调度,延迟执行

4.刷新操作

  void flush_scheduled_work(void);

   用于不确定队列中任务是否处理完了,刷新一下,直到所有对象被执行完才返回,用于卸载之前。(没执行完,表明会休眠)

5.取消延迟操作

   int cancle_delayed_work(struct work_struct *work);

6.自己创建新的工作队列

   struct workqueue_struct    *create_workqueue(const char *name);

   创建events队列:

             struct workqueue_struct  *keventd_wq = create_workqueue("events");

  创建后自己调度:                                                                                                                          (传入第一个参数)

             int queue_work(struct workqueue_struct *wq, struct_struct *work);

             int queue_delay_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay);

             flush_workqueue(struct workqueue_struct *wq);

 

 

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

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值