linux驱动编程--中断

本文深入探讨了Linux内核中的中断处理原理及优化技术,包括基本处理、扩展处理及中断处理的分层机制(顶半部和底半部)。详细介绍了tasklet、工作队列和软中断的实现方式及其在中断处理中的应用,旨在提高系统响应速度和资源利用效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.中断的基本处理:

        简单的中断处理,主要是使用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;
	}



 




 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值