博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(笔记)Linux内核学习(五)之中断推后处理机制
阅读量:5989 次
发布时间:2019-06-20

本文共 6963 字,大约阅读时间需要 23 分钟。

一 中断

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

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

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

  硬件中断延后再进行处理

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

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

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

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

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

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

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

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

下面看一下这个过程:

加入软中断类型:

       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));}

 

 

 

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

平衡是个很关键的问题!

转载地址:http://bejlx.baihongyu.com/

你可能感兴趣的文章
Active Directory 活动目录之强制占有操作主机
查看>>
python中List添加元素的几种方法
查看>>
搭建基于虚拟用户的电子邮件服务器
查看>>
VS2015中C#连接Oracle数据库
查看>>
C#学习速记_面向对象编程简介
查看>>
WSS3.0 和 MOSS 的版本号小结
查看>>
我的友情链接
查看>>
对Class.getResourceAsStream和ClassLoader.getResourceAsStream方法所使用的资源路径的解释...
查看>>
Lync Server 2013服务器准备林架构报错
查看>>
ES6基础
查看>>
yum groupinstall "Development Tools" 批量安装软件 linux
查看>>
hibernate批量导入性能问题
查看>>
vim 单行或者多行复制粘贴
查看>>
我的友情链接
查看>>
windows下一个tomcat版本创建多个实例
查看>>
VMware Vcenter 部署
查看>>
Spring Java 安全管理器--SecurityManager
查看>>
我的友情链接
查看>>
mysql数据库备份脚本
查看>>
DB2 Foreign Key
查看>>