• (笔记)Linux内核学习(五)之中断推后处理机制


    一 中断

           硬件通过中断与操作系统进行通信,通过对硬件驱动程序处注册中断处理程序,快速响应硬件的中断。

    硬件中断优先级很高,打断当前正在执行的程序。有两种情况:

      硬件中断在中断处理程序中处理

      硬件中断延后再进行处理

      这个具体硬件相关,在中断处理程序中处理,打断了当前正在执行的程序;所有中断都将被屏蔽;如果占用时间太长不合适,

    造成系统交互性,反应能力都会受到影响。 需要在其中判断平衡:

           如果一个任务对时间非常敏感,将其放在中断处理程序中执行;

           如果一个人和和硬件相关,将其放在中断处理程序中执行;

           如果一个任务要保证不被其他中断打断,将其放在中断处理程序中执行;

           其余情况考虑延后机制中执行——下半部。

    二 中断推后执行机制—— 软中断

           软中断是在编译期间静态分配的,在程序执行前将软中断假如到表中。

    下面看一下这个过程:

    加入软中断类型:

           Linux3.5.3代码:

    复制代码
    enum
    {
           HI_SOFTIRQ=0,
    
           TIMER_SOFTIRQ,
    
           NET_TX_SOFTIRQ,
    
           NET_RX_SOFTIRQ,
    
           BLOCK_SOFTIRQ,
    
           BLOCK_IOPOLL_SOFTIRQ,
    
           TASKLET_SOFTIRQ,
    
           SCHED_SOFTIRQ,
    
           HRTIMER_SOFTIRQ,
    
           RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
     
           NR_SOFTIRQS
    
    };
    复制代码

     

     

     

     

    //软中断表:

    static struct softirq_action softirq_vec[NR_SOFTIRQS]

    //软中断结构体

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

     

     

    注册软中断处理函数:

    void open_softirq(int nr, void (*action)(struct softirq_action *))
    {
           //关联表中对应类型
          softirq_vec[nr].action = action;
    }

     

     

     

     

    触发软中断:     

    复制代码
    void raise_softirq(unsigned int nr)
    {
           unsigned long flags;
    
           //停止但保存中断标志
           local_irq_save(flags);
    
           //将相应软中断挂起状态
           raise_softirq_irqoff(nr);
    
           //恢复中断
           local_irq_restore(flags);
    }
    复制代码

     

     

     

     

    执行软中断:   

    复制代码
    void irq_exit(void)
    {            
      invoke_softirq();  //do_softirq();
    }
    
    void __do_softirq(void)
    {
           struct softirq_action *h;
           __u32 pending;
           int max_restart = MAX_SOFTIRQ_RESTART;
           int cpu;
          
           //获取CPU软中断状态标志位 32位代表最多32个软中断
           pending = local_softirq_pending();
     
    restart:
           /* Reset the pending bitmask before enabling irqs */
           set_softirq_pending(0);
           local_irq_enable();
           h = softirq_vec;
           do {
                  //被触发则执行软中断处理程序
                  if (pending & 1) {
              h->action(h);
                  }
    
                  //下一个软中断
                  h++;
    
                  //下一个软中断状态标志位
                  pending >>= 1;
           } while (pending);
     
           local_irq_disable();
           pending = local_softirq_pending();
           if (pending && --max_restart)
                  goto restart;
    
           if (pending)
                  wakeup_softirqd();
           lockdep_softirq_exit();
        __local_bh_enable(SOFTIRQ_OFFSET);
    }
    复制代码

     

     

     

    软中断的基本结构如下图表示:

          

        

     

     

    三  中断推后执行机制——tasklet

           软中断中表中有一种类型是:TASKLET_SOFTIRQ

    Tasklet就是利用软中断实现中断推后处理机制。通常使用较多的是tasklet而不是软中断。

    Tasklet数据结构:   

    复制代码
    struct tasklet_struct
    {
           //链表中下一个tasklet
           struct tasklet_struct *next;
    
           //tasklet状态
           unsigned long state;
    
           //引用计数器
           atomic_t count;
    
           //tasklet处理函数
           void (*func)(unsigned long);
    
           //处理函数参数
        unsigned long data;
    
    };
    复制代码

     

     

     

    state:

    enum
    {
          TASKLET_STATE_SCHED,   /* Tasklet is scheduled for execution */
          TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
    };

     

     

     

    count:为0允许激活执行

    声明tasklet:可以动态或者静态方式

           静态:

    #define DECLARE_TASKLET(name, func, data) 
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
    
    
    #define DECLARE_TASKLET_DISABLED(name, func, data) 
    struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

     

           动态:    

    复制代码
    void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
    {
           t->next = NULL;
           t->state = 0;
           atomic_set(&t->count, 0);
           t->func = func;
           t->data = data;
    }
    复制代码

     

     

     

      同时需要编写tasklet处理函数。

    调度tasklet:

           

    复制代码
    void tasklet_hi_schedule(struct tasklet_struct *t)
    {
           unsigned long flags;
           local_irq_save(flags);
           t->next = NULL;
           *__this_cpu_read(tasklet_vec.tail) = t;
           __this_cpu_write(tasklet_vec.tail, &(t->next));
           raise_softirq_irqoff(TASKLET_SOFTIRQ);
           local_irq_restore(flags);
    }
    复制代码

     

     

     

    执行tasklet处理程序:

           继续看上面调度tasklet程序执行:

          

    复制代码
     inline void raise_softirq_irqoff(unsigned int nr)
    {
           __raise_softirq_irqoff(nr);
           if (!in_interrupt())
                  wakeup_softirqd();
    }
    
    //使用ksoftirqd内核线程来处理
    static void wakeup_softirqd(void)
    {
           /* Interrupts are disabled: no need to stop preemption */
           struct task_struct *tsk = __this_cpu_read(ksoftirqd);
           if (tsk && tsk->state != TASK_RUNNING)
                  wake_up_process(tsk);
    }
    复制代码

     

     

     

    Ksoftirqd内核线程:

           软中断才被触发频率很高,在处理过程中还会重新触发软中断;执行会导致用户空间进程无法获得处理时间处于饥饿状态;

    对重新触发的软中断立即处理,会导致占据处理时间过长;不进行立即处理不合适;

    对此解决方法:

      l  只要还有被触发并等待处理和过程中重新触发的软中断的软中断,本次执行就要负责处理;软中断立即处理,用户空间得不到执行时间。

      l  不处理过程中触发的软中断,放到下一个中断执行时机时处理。软中断得不到立即处理,系统空闲时造成不合理;保证用户空间得到执行时间。

    两种方式有存在问题,只能在这其中采取这种的方式:

           内核使用线程处理软中断,线程优先级较低,可以被抢占;能够保证软中断被处理,也能保证用户空间程序得到执行时间。

           每个CPU上有存在这样一个线程:ksoftirqd/0或者ksoftirqd/1……

    复制代码
    static __init int spawn_ksoftirqd(void)
    {
           void *cpu = (void *)(long)smp_processor_id();
           int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
                  ……
           return 0;
    }
    
    early_initcall(spawn_ksoftirqd);
    static int __cpuinit cpu_callback(struct notifier_block *nfb,
                                  unsigned long action,
                                  void *hcpu)
    {
           int hotcpu = (unsigned long)hcpu;
           struct task_struct *p;
    
           switch (action) {
           case CPU_UP_PREPARE:
           case CPU_UP_PREPARE_FROZEN:
                  p = kthread_create_on_node(run_ksoftirqd,
                                          hcpu,
                                          cpu_to_node(hotcpu),
                                          "ksoftirqd/%d", hotcpu);
                  kthread_bind(p, hotcpu);
                per_cpu(ksoftirqd, hotcpu) = p;
                 break;
           ……
    }
    复制代码

     

     

    四 中断推后执行机制——工作队列

           工作队列(work queue)通过内核线程将中断下半部分程序推后执行到线程中执行,工作队列可以创建线程来处理相应任务。

           工作队列创建的线程为工作者线程:worker thread;系统提供默认的线程来处理工作者队列。

            

          

     

    工作者线程数据结构:

           

    复制代码
    struct workqueue_struct {
           unsigned int        flags;            /* W: WQ_* flags */
           union {
                  struct cpu_workqueue_struct __percpu       *pcpu;
                  struct cpu_workqueue_struct         *single;
                  unsigned long                           v;
           } cpu_wq;                          /* I: cwq's */
           struct list_head   list;        /* W: list of all workqueues */
           ……
    }
    复制代码

     

     

     

    CPU工作队列数据结构:

    复制代码
    struct cpu_workqueue_struct {
    //每个CPU工作队列信息 struct global_cwq *gcwq;
    //每个CPU工作队列 struct workqueue_struct *wq; …… };
    复制代码

     

    工作数据结构:

    struct work_struct {
           atomic_long_t data;
           struct list_head entry;
           work_func_t func;
    
    };

     

    声明工作队列:

           静态:

    #define DECLARE_WORK(n, f)                             
           struct work_struct n = __WORK_INITIALIZER(n, f) 

     

     

     

           动态:

    #define INIT_WORK(_work, _func)                                   
           do {                                           
                  __INIT_WORK((_work), (_func), 0);              
           } while (0)  

           需要编写工作队列处理函数:       

    typedef void (*work_func_t)(struct work_struct *work);

     

     

     

    调度工作队列:

    复制代码
    int schedule_work(struct work_struct *work)
    {
           return queue_work(system_wq, work);
    }
    int queue_work(struct workqueue_struct *wq, struct work_struct *work)
    {
          ret = queue_work_on(get_cpu(), wq, work);
    }
    复制代码

     

     

     

           唤醒工作者队列线程处理。

    执行工作者队列处理程序:

           

    复制代码
    static int worker_thread(void *__worker)
    {
           do {
                  struct work_struct *work =
                         list_first_entry(&gcwq->worklist,
                                       struct work_struct, entry);
                  process_one_work(worker, work);
           } while (keep_working(gcwq));
    
    }
    复制代码

     

     

     

    可以创建新的工作者队列和线程来处理。

    平衡是个很关键的问题!

  • 相关阅读:
    ZOJ 2158 Truck History
    Knight Moves (zoj 1091 poj2243)BFS
    poj 1270 Following Orders
    poj 2935 Basic Wall Maze (BFS)
    Holedox Moving (zoj 1361 poj 1324)bfs
    ZOJ 1083 Frame Stacking
    zoj 2193 Window Pains
    hdu1412{A} + {B}
    hdu2031进制转换
    openjudge最长单词
  • 原文地址:https://www.cnblogs.com/tdyizhen1314/p/5316854.html
Copyright © 2020-2023  润新知