linux驱动编程--中断
2014-03-22 15:47
579 查看
一.中断的基本处理:
简单的中断处理,主要是使用request_irq()和free_irq()。
In fact, the kernel create a global array( irq_desc[] ) for manage all information about irq.Included
slot-function, irq_chip and so on. By the way.Each irq is corresponding to a member of
irq_desc. 在kernel建立了一个全局变量数组irq_desc[],用来记录每个中断的信息。
slot-function is used to record what this irq need to do when a new interrupy arrived.(many user function)
irq_chip is a structure, record many callback-function about how to control this irq.( i.e how to enable/disable irq, how to set handware status)
the following is some useful function:
看一下free_irq的源码,发现对irq资源的回收就是从irq_desc[]中找到该irq对应的信息记录变量desc。然后从desc->action的注册函数链表中取出需要注销的函数。
二.中断处理扩展:
为了改善中断处理效率,中断处理函数被分成了两部分:顶半部和底半部。底半部有三种实现方式:tasklet,工作队列和软中断。
2.1 Tasklet
tasklet运行于软中断上下文,所以在其处理函数中不允许睡眠。在内核有一个全局链表用于记录需要完成的任务,对应的处理进程为守护进程 ksoftirqd/0。一般在执 行中断上半部后会执行下半部,到下半部时如果有新的中断到达,则唤醒ksoftirqd/0来执行当前部分,而自己去处理新中断。其函数解析如下:
2.2 工作队列
工作队列是一个直接将一个任务封包挂到全局链表上交给一个worker process处理,所以是工作在进程上下文,允许睡眠以及进程调度操作。
2.3 软中断
处理器的所有状态中有一种是软中断状态,这是一种可被打断的中断状态(其他中断是不允许被打断的)。 one of most important feasure, for softirq, is interruptible.this is very important for ensure the system quickly answer a new interrupt ,because the rest of all interrupt
can't be interrupt.
为了方便事物分类处理又在内部进行了逻辑分类,
for example, tasklet is corresponding to TASKLET_SOFTIRQ. we often raise the softirq( the
TASKLET_SOFTIRQ type) when a new tasklet arrived.
the type of softirq is
In the next is some common function:
一个非常棒的关于软中断的说明在这里,:
a49e
简单的中断处理,主要是使用request_irq()和free_irq()。
In fact, the kernel create a global array( irq_desc[] ) for manage all information about irq.Included
slot-function, irq_chip and so on. By the way.Each irq is corresponding to a member of
irq_desc. 在kernel建立了一个全局变量数组irq_desc[],用来记录每个中断的信息。
slot-function is used to record what this irq need to do when a new interrupy arrived.(many user function)
irq_chip is a structure, record many callback-function about how to control this irq.( i.e how to enable/disable irq, how to set handware status)
the following is some useful function:
/** 中断处理函数*/ can_request_irq(unsigned int irq,unsigned long irqflags); //查询是否可以申请 request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char * devname,void * dev_id); //申请 free_irq(unsigned int irq,void * dev_id); //释放中断 /** 扩展处理函数*/ disable_irq(unsigned int irq); //禁止中断,如果有当前中断,会等待该中断执行完 disable_irq_nosync(unsigned int irq); //禁止中断,会直接返回 enable_irq(unsigned int irq); local_irq_disable(); local_irq_save(flags); local_irq_enable(); local_irq_restore(x); set_fiq_handler(void * start,unsigned int length);
看一下free_irq的源码,发现对irq资源的回收就是从irq_desc[]中找到该irq对应的信息记录变量desc。然后从desc->action的注册函数链表中取出需要注销的函数。
/* * Internal function to unregister an irqaction - used to free * regular and special interrupts that are part of the architecture. */ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) { struct irq_desc *desc = irq_to_desc(irq); //kernel建立了一个全局变量数组irq_desc[],用来存放所有的irq中断信息。 struct irqaction *action, **action_ptr; // struct task_struct *irqthread; unsigned long flags; WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq); if (!desc) return NULL; spin_lock_irqsave(&desc->lock, flags); /* * There can be multiple actions per IRQ descriptor, find the right * one based on the dev_id: */ action_ptr = &desc->action; for (;;) {//根据dev_id找到该irq对应的链表中的部分 action = *action_ptr; if (!action) { WARN(1, "Trying to free already-free IRQ %d\n", irq); spin_unlock_irqrestore(&desc->lock, flags); return NULL; } if (action->dev_id == dev_id) break; action_ptr = &action->next; } /* Found it - now remove it from the list of entries: */ *action_ptr = action->next; /* Currently used only by UML, might disappear one day: */ #ifdef CONFIG_IRQ_RELEASE_METHOD if (desc->chip->release) desc->chip->release(irq, dev_id); #endif /* If this was the last handler, shut down the IRQ line: */ if (!desc->action) { desc->status |= IRQ_DISABLED; if (desc->chip->shutdown) desc->chip->shutdown(irq); else desc->chip->disable(irq); } irqthread = action->thread; action->thread = NULL; spin_unlock_irqrestore(&desc->lock, flags); unregister_handler_proc(irq, action); /* Make sure it's not being used on another CPU: */ synchronize_irq(irq); if (irqthread) { if (!test_bit(IRQTF_DIED, &action->thread_flags)) kthread_stop(irqthread); put_task_struct(irqthread); } #ifdef CONFIG_DEBUG_SHIRQ /* * It's a shared IRQ -- the driver ought to be prepared for an IRQ * event to happen even now it's being freed, so let's make sure that * is so by doing an extra call to the handler .... * * ( We do this after actually deregistering it, to make sure that a * 'real' IRQ doesn't run in * parallel with our fake. ) */ if (action->flags & IRQF_SHARED) { local_irq_save(flags); action->handler(irq, dev_id); local_irq_restore(flags); } #endif return action; }
二.中断处理扩展:
为了改善中断处理效率,中断处理函数被分成了两部分:顶半部和底半部。底半部有三种实现方式:tasklet,工作队列和软中断。
2.1 Tasklet
tasklet运行于软中断上下文,所以在其处理函数中不允许睡眠。在内核有一个全局链表用于记录需要完成的任务,对应的处理进程为守护进程 ksoftirqd/0。一般在执 行中断上半部后会执行下半部,到下半部时如果有新的中断到达,则唤醒ksoftirqd/0来执行当前部分,而自己去处理新中断。其函数解析如下:
/** 信息结构体*/ struct tasklet_struct { struct tasklet_struct *next; //信息结构体被做成链表 unsigned long state; // atomic_t count; // void (*func)(unsigned long); //回调函数 unsigned long data; //传给回调函数的参数 }; /** 初始化*/ tasklet_init(struct tasklet_struct * t,void(* func)(unsigned long),unsigned long data); DECLARE_TASKLET(name,func,data); #define DECLARE_TASKLET(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } /** schedule*/ tasklet_schedule(a); static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_schedule(t); } void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); t->next = NULL; *__get_cpu_var(tasklet_vec).tail = t; //??将新task添加到全局task链表中 __get_cpu_var(tasklet_vec).tail = &(t->next); // raise_softirq_irqoff(TASKLET_SOFTIRQ); //挂起TASKLET_SOFTIRQ类型的软中断,并根据需要唤醒守护进程 local_irq_restore(flags); } /* * This function must run with irqs disabled! */ inline void raise_softirq_irqoff(unsigned int nr) { __raise_softirq_irqoff(nr); //挂起软中断nr /* * If we're in an interrupt or softirq, we're done * (this also catches softirq-disabled code). We will * actually run the softirq once we return from * the irq or softirq.(假如在中断中,该函数一定会被执行) * * Otherwise we wake up ksoftirqd to make sure we * schedule the softirq soon.(否则就叫醒守护进程) */ if (!in_interrupt()) //假如不在中断上下文 wakeup_softirqd(); //叫醒softirq守护进程,它会保证该函数执行一次 }
2.2 工作队列
工作队列是一个直接将一个任务封包挂到全局链表上交给一个worker process处理,所以是工作在进程上下文,允许睡眠以及进程调度操作。
//重要结构体 struct work_struct; struct workqueue_struct; struct cpu_workqueue_struct; //初始化 INIT_WORK(_work,_func); DECLARE_WORK(n,f); //常用函数 schedule_work(a); --将新工作a添加到一个全局的工作队列中,一般由守护进程events复制处理;但如果需要可以创建自定义 的worker process。
2.3 软中断
处理器的所有状态中有一种是软中断状态,这是一种可被打断的中断状态(其他中断是不允许被打断的)。 one of most important feasure, for softirq, is interruptible.this is very important for ensure the system quickly answer a new interrupt ,because the rest of all interrupt
can't be interrupt.
为了方便事物分类处理又在内部进行了逻辑分类,
for example, tasklet is corresponding to TASKLET_SOFTIRQ. we often raise the softirq( the
TASKLET_SOFTIRQ type) when a new tasklet arrived.
the type of softirq is
enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, TASKLET_SOFTIRQ, // this is a tasklet thing SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };
In the next is some common function:
//重要结构体 struct softirq_action; struct softirq_action { void (*action)(struct softirq_action *); }; //常用函数 open_softirq(int nr,void(* action)(struct softirq_action *)); //reigister a function for a softirq( 注册软中断对应的处理函数) raise_softirq(unsigned int nr); //raise a softirq(触发一个软中断) local_bh_disable(void); //禁止软中断,tasklet底半部机制 local_bh_enable(void); //
一个非常棒的关于软中断的说明在这里,:
void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }
a49e
相关文章推荐
- Linux设备驱动编程之中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之二--Linux中断的组织形式
- Linux设备驱动编程之中断处理
- Linux学习二, 中断编程和字符设备驱动
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- Linux设备驱动编程中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之四--中断API
- Linux驱动编程day6-外部中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之七姑八姨
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之一--中断概述
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之老大-软中断softirq
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之工作队列详解
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之tasklet详解
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之二--Linux中断的组织形式
- Linux设备驱动编程之中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之四--中断API
- linux驱动开发之输入子系统编程(一)使用工作队列实现中断下半部
- Linux设备驱动编程之中断处理
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之目录