Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1488105
  • 博文数量: 338
  • 博客积分: 2695
  • 博客等级: 少校
  • 技术积分: 3556
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-05 11:37
个人简介

小鱼儿游啊游啊。。。。

文章分类

全部博文(338)

文章存档

2019年(4)

2018年(8)

2017年(6)

2016年(10)

2015年(49)

2014年(48)

2013年(98)

2012年(115)

分类: LINUX

2012-11-09 21:52:08

一 中断

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

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

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

  硬件中断延后再进行处理

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

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

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

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

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

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

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

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

下面看一下这个过程:

加入软中断类型:

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)); }
复制代码

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

平衡是个很关键的问题!

阅读(1175) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~