门户网站作用,Centos建网站必须域名,手机网站开发 html5,网站开发合同 下载一、基本概念
1、中断及中断上下文 中断是一种由硬件设备产生的信号#xff0c;不同设备产生的中断通过中断号来区分。CPU在接收到中断信号后#xff0c;根据中断号执行对应的中断处理程序#xff08;Interrupt Service Routine#xff09; 内核对异常和中断的处理类似不同设备产生的中断通过中断号来区分。CPU在接收到中断信号后根据中断号执行对应的中断处理程序Interrupt Service Routine 内核对异常和中断的处理类似差别只在于中断是由硬件引起的 异常举例软中断实现系统调用缺页异常除0异常 中断上下文执行一个中断处理程序时内核处于中断上下文中 注在中断上下文中不允许睡眠这是因为中断上下文没有后备进程即无法被调度唤醒
2、上半部与下半部 一般将中断处理分为上半部和下半部 上半部在接收到中断后只完成有严格时限要求的工作如对中断应答或者复位硬件此时中断是被禁止的 下半部指中断处理流程中推后执行的那一部分在合适的时机执行此时允许相应所有的中断 3、下半部和推后执行的工作 除了对时间敏感且保证不被其他中断打断的部分剩余的任务考虑在下半部执行下半部的实现机制有以下几种
下半部机制功能状态BH(bottom half)一个静态创建、由32个bottom halves组成的链表上半部通过32整数中的一位来标识出哪个bottom half可以执行虽然分属不同处理器也不允许任何两个bottom half同时执行。不够灵活简单有性能瓶颈Linux 2.5移除任务队列引入任务队列机制来实现工作的推后执行替代BH机制。驱动程序会将下半部注册到相应的等待队列等待调用执行。不够灵活不能满足性能要求较高的子系统Linux 2.5移除软中断(Softirq)一组静态定义的下半部接口有32个可以在所有处理器上同时运行同类型的接口也可以同时执行。tasklet是需要在编译阶段进行静态注册。针对性能要求较高的子系统Linux 2.3引入tasklet一组基于软中断实现的灵活性强、动态创建的下半部实现机制 不同类型的tasklet可以在不同的处理器上执行, 但类型相同tasklet不能同时执行。tasklet可以通过代码进行动态注册。大部分的场景Linux 2.3引入工作队列Work queues工作队列取代了任务队列Linux 2.3引入
二、中断处理程序
1、注册中断处理程序
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name,void *dev);
irq要分配的中断号
handler指向这个中断的处理程序
flag可以为0也可以是一个或多个标志的掩码IRQF_DISABLED --- 内核在处理中断处理程序期间禁止所有的其他中断IRQF_SAMPLE_RANDOM --- 该设备产生的中断对内核熵池有贡献内核熵池负责从各个随机事件导出真正的随机数IRQF_TIMER --- 为系统定时器的中断处理而准备IRQF_SHARED --- 多个中断处理程序之间共享中断线同一个中断线的所有的中断处理程序都要指定该标志
name设备ASCLL文本会被proc/interrupts文件使用以便与用户通信
dev主要用于共享中断线dev提供唯一标志信息(cookie)用来区分共享一个中断的多个设备当一个中断处理程序需要释放时以便从共享中断线的诸多处理程序中删除指定的一个如果没有共享中断设置为NULL即可
注此函数会睡眠所以不要在中断上下文中调用
2、释放中断处理程序
void free_irq(unsigned int irq, void *dev) 卸载驱动处理程序时需要注销相应的中断处理程序并释放中断线 如果指定的中断线不是共享的那么函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的则删除dev所对应的处理程序共享中断线只有在删除了最后一个中断处理程序时才会被禁用。
3、中断处理主程序
static irqreturn_t intr_handler(int irq, void *dev);
irq处理程序要响应中断的中断号
dev与传递给request_irq()的dev保持一致dev将提供唯一的标志信息cookie用来区分共享同一个中断处理程序的多个设备 Linux中断处理程序是不允许嵌套其他中断处理程序中断上下文的代码应当迅速简洁中断处理程序没有独立的栈与所中断进程共享内核栈
4、中断控制
函数说明local_irq_disable()禁止本地中断传递local_irq_enable()激活本地中断传递local_irq_save(unsigned long flags)保存本地中断传递的当前状态然后禁止本地中断传递local_irq_restore(unsigned long flags)恢复本地中断传递到给定的状态disable_irq(unsigned int irq)禁止给定中断线并确保该函数返回之前在该中断线上没有处理程序在运行disable_irq_nosync(unsigned int irq)禁止给定中断线不会等待当前中断处理程序执行完毕enable_irq(unsigned int irq)激活给定中断线synchronize_irq(unsigned int irq)等待一个特定的中断处理程序退出才会返回irqs_disabled()如果本地处理器上的中断系统被禁止则返回非0否则返回0in_interrupt()如果在中断上下文中包括执行中断处理程序和正在执行下半部处理程序 则返回非0 如果在进程上下文中则返回0in_irq()如果当前正在执行中断处理程序 则返回非0否则返回0
三、中断绑定
1、查看中断号
cat proc/interruptsCPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 11: 17280 12860 7073 8441 7254 10057 11085 10031 GICv3 27 Level arch_timer13: 0 0 0 0 0 0 0 0 GICv3 38 Level arch_mem_timer15: 0 0 0 0 0 0 0 0 GICv3 23 Level arm-pmu16: 0 0 0 0 0 0 0 0 GICv3 960 Edge gh_msgq_tx17: 9 0 0 0 0 0 0 0 GICv3 961 Edge gh_msgq_rx18: 13799 1329 0 0 0 0 0 0 GICv3 261 Level ipcc_019: 62 0 0 0 0 0 0 0 GICv3 94 Level qcom_cpucp1、irq逻辑中断号
2、中断在各CPU发生的次数
3、中断所属设备类名称
4、硬件中断号
5、中断触发方式
6、中断处理函数2、将中断绑定到CPU
smp_affinity通过 bitmask 算法绑定CPU echo 0xf /proc/irq/45/smp_affinitysmp_affinity_list通过数字指定CPU编号 echo 0-3 /proc/irq/45/smp_affinity_list //作用是将中断号45的设备中断处理包括软中断和硬中断均摊绑定到0、1、2、3各个CPU
四、下半部机制
1、软中断 软中断是在编译期间静态分配的软中断由softirq_action结构表示。软中断保留给系统中对时间要求最严格以及最重要的下半部使用
struct softirq_action
{void (*action)(struct softirq_action *);
}; 注册的软中断都在这个数组里面每个被注册的软中断都占据该数组的一项
static struct softirq_action softirq_vec[NR_SOFTIRQS]
1软中断处理程序 同一个处理器一个软中断不会抢占另外一个软中断但中断处理程序可以抢占软中断。其他软中断包括相同类型的软中断可以在其他处理器上同时执行
void softirq_handler(struct softirq_action *);
2软中断执行程序 每个处理器都有一个ksoftirqd/n线程他们会通过softirq_pending()发现是否有待处理的软中断如果发现就会调用do_softirq() 循环遍历调用相应的处理程序
3软中断类型 下面是按照优先级排列的
/* PLEASE, avoid to allocate new softirqs, if you need not _really_ highfrequency threaded job scheduling. For almost all the purposestasklets are more than enough. F.e. all serial device BHs etal. should be converted to tasklets, not to softirqs.*/enum
{HI_SOFTIRQ0, /*优先级较高的tasklet*/TIMER_SOFTIRQ, /*定时器的下半部*/NET_TX_SOFTIRQ, /*发送网络数据包*/NET_RX_SOFTIRQ, /*接收网络数据包*/BLOCK_SOFTIRQ, /*block装置*/IRQ_POLL_SOFTIRQ, TASKLET_SOFTIRQ, /*正常优先权的tasklet*/SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};
4使用软中断
a.注册软中断
open_softirq(NET_TX_SOFTIRQ,net_tx_action);
open_softirq(NET_RX_SOFTIRQ,net_rx_action);b.触发软中断
//将软中断设置成挂起状态下次调用do_softirq()函数时投入运行。
raise_softirq(NET_TX_SOFTIRQ);
//中断已经被禁止,调用下面指令
raise_softirq_irqoff(NET_TX_SOFIRQ);
2、tasklet tasklet是一种利用软中断实现的一种下半部机制, 但是它的接口更简单锁保护也要求较低
struct tasklet_struct
{struct tasklet_struct *next;//链表指向下一个taskletunsigned long state;//tasklet状态 0/准备执行/正在运行atomic_t count;//引用计数,为0才被激活void (*func)(unsigned long);//tasklet处理函数unsigned long data;//给tasklet处理函数的参数
};
1调度tasklet 已经通过tasklet_schedule()完成调度等待执行的tasklet会被存放在两个链表tasklet_vec和tasklet_hi_vec将软中断TASKLET_SOFTIRQ或HI_SOFTIRQ设置成挂起状态等待下一次调用do_softirq()就会执行 tasklet_vec:存放普通的tasklet由tasklet_schedule()进行调度使用的是TASKLET_SOFTIRQ软中断。 tasklet_hi_vec:存放高优先级的tasklet由tasklet_hi_schedule()进行调度使用的是HI_SOFTIRQ软中断 //将软中断TASKLET_SOFTIRQ设置成挂起状态等待do_softirq()调用raise_softirq_irqoff(softirq_nr);
2使用tasklet
静态创建一个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 }
通过传入一个利用tasklet_init()函数动态创建tasklet结构
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;
}
EXPORT_SYMBOL(tasklet_init);
调度你创建的tasklet
tasklet_schedule(my_tasklet)
3、工作队列 在中断处理中经常用到工作队列这样便能缩短中断处理时的时间。工作队列可以睡眠比如需要获取大量内存或者需要获取信号量 Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程方便了用户的编程
1常用函数
a.INIT_WORK() 通过此函数将work_struct与函数建立联系
#ifdef CONFIG_LOCKDEP
#define __INIT_WORK_KEY(_work, _func, _onstack, _key) \do { \__init_work((_work), _onstack); \(_work)-data (atomic_long_t) WORK_DATA_INIT(); \lockdep_init_map((_work)-lockdep_map, (work_completion)#_work, (_key), 0); \INIT_LIST_HEAD((_work)-entry); \(_work)-func (_func); \} while (0)
#else
#define __INIT_WORK_KEY(_work, _func, _onstack, _key) \do { \__init_work((_work), _onstack); \(_work)-data (atomic_long_t) WORK_DATA_INIT(); \INIT_LIST_HEAD((_work)-entry); \(_work)-func (_func); \} while (0)
#endif#define __INIT_WORK(_work, _func, _onstack) \do { \static __maybe_unused struct lock_class_key __key; \\__INIT_WORK_KEY(_work, _func, _onstack, __key); \} while (0)#define INIT_WORK(_work, _func) \__INIT_WORK((_work), (_func), 0)workwork_struct结构体
func函数名
struct work_struct {atomic_long_t data; /*工作处理函数func的参数*/ struct list_head entry; /*连接工作的指针*/ work_func_t func; /*函数指针,指向func函数*/
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};
b.schedule_work(work)
static inline bool schedule_work(struct work_struct *work)
{return queue_work(system_wq, work);
} 当中断来了立马调用schedule_work(work)然后退出此函数一般在中断上半部。 中断结束后内核线程会自动调用work结构体对应的func函数
static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
{return queue_delayed_work(system_wq, dwork, delay);
} 有时并不希望work立即执行而是希望过一段delay时间后再执行。工作队列是没有优先级的基本按照FIFO的方式进行处理。
c.cancel_work_sync(work);
/*取消work结构体对应的func函数但会等待其执行完一般在exit中使用*/
bool cancel_work_sync(struct work_struct *work)
{return __cancel_work_timer(work, false);
}
EXPORT_SYMBOL_GPL(cancel_work_sync);
d.queue_work(workqueue work)
/*调度执行一个指定workqueue中的任务*/
static inline bool queue_work(struct workqueue_struct *wq,struct work_struct *work)
{return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
e.flush_work(work)
/*等待工作队列完成执行工作*/
bool flush_work(struct work_struct *work)
{return __flush_work(work, false);
}
EXPORT_SYMBOL_GPL(flush_work);
【参考博客】
[1] Linux 内核设计与实现
[2] Linux内核 | 中断机制 - 世至其美
[3] INIT_WORK()工作队列使用-CSDN博客
[4] https://www.cnblogs.com/oceanding/p/7595738.html