linux中断总结
Linux 内核软中断(softirq)执行分析
Linux 内核软中断(softirq)执行分析Author: sinisterEmail: sinister@Homepage:Date: 2007-01-11本文对 Linux 内核软中断的执行流程进行了分析,并尽可能的结合当前运行环境详细地写出我的理解,但这并不表明我的理解一定正确。
这本是论坛里的一篇帖子,发出来是为了抛砖引玉,如果您在阅读本文时发现了我的错误,还望得到您的指正。
今天无意中看了眼 2.6 内核的软中断实现,发现和以前我看到的大不相同(以前也是走马观花,不大仔细),可以说改动很大。
连 softirq 的调用点都不一样了,以前是三个调用点,今天搜索了一下源代码,发现在多出了ksoftirqd 后,softirq 在系统中的调用点仅是在 ISR 返回时和使用了local_bh_enable() 函数后被调用了。
网卡部分的显示调用,我觉得应该不算是系统中的调用点。
ksoftirqd 返回去调用 do_softirq() 函数应该也只能算是其中的一个分支,因为其本身从源头上来讲也还是在 ISR 返回时irq_exit() 调用的。
这样一来就和前些日子写的那份笔记(Windows/Linux /Solaris 软中断机制)里介绍的 Linux 内核部分的软中断有出处了,看来以后讨论 Linux kernel 代码一定要以内核版本为前题,要不非乱了不可。
得买本 Linux 方面的书了,每次上来直接看相关代码也不是回事,时间也不允许。
//// do_IRQ 函数执行完硬件 ISR 后退出时调用此函数。
//void irq_exit(void){account_system_vtime(current);trace_hardirq_exit();sub_preempt_count(IRQ_EXIT_OFFSET);//// 判断当前是否有硬件中断嵌套,并且是否有软中断在// pending 状态,注意:这里只有两个条件同时满足// 时,才有可能调用 do_softirq() 进入软中断。
Linux网络疑难杂症解决断网问题的几个窍门
Linux网络疑难杂症解决断网问题的几个窍门在使用Linux操作系统时,有时会遇到网络连接断开的问题,这对于依赖网络工作的人来说是非常头疼的。
本文将分享几个解决Linux网络断网问题的窍门,希望能帮助读者尽快解决这一疑难杂症。
一、检查网络硬件连接网络连接出现问题的第一步是检查网络硬件连接,包括网线、网卡和路由器等设备。
确保网线插紧,网卡连接正常,路由器无故障,从而排除硬件连接问题对网络故障的影响。
二、使用ping命令检查网络连通性使用ping命令是一种简单有效的方法,可以检查Linux主机与其他设备的网络连通性。
在终端中执行ping命令,加上目标主机的IP地址或域名,如果能够连通,则表示无问题。
如果无法连通,请检查本地网络配置或目标主机是否正常。
三、检查网络配置文件Linux系统的网络配置文件位于/etc/network/interfaces文件中。
可以通过编辑此文件来检查和修改网络配置。
确保配置文件中指定的IP地址、子网掩码、网关等参数是正确的。
如果有任何更改,请使用sudo service networking restart命令重启网络服务以使更改生效。
四、重启网络管理服务有时,网络管理服务可能出现问题,导致网络连接断开。
可以尝试重启网络管理服务以解决这个问题。
通过执行sudo service network-manager restart命令,可以重新启动网络管理服务。
请注意,此命令可能因Linux发行版的不同而有所区别,请根据您所使用的发行版进行调整。
五、禁用IPv6IPv6是一种新的网络协议,用于替代IPv4。
虽然IPv6在未来是必要的,但当前仍有些网络环境对其支持存在问题。
尝试禁用IPv6可能会解决某些网络断开的问题。
可以通过编辑/etc/sysctl.conf文件,在末尾添加以下行来禁用IPv6:```net.ipv6.conf.all.disable_ipv6 = 1net.ipv6.conf.default.disable_ipv6 = 1```保存文件后,通过执行sudo sysctl -p命令使更改生效。
Linux中request_irq()中断申请与处理说明
Linux中request_irq()中断申请与处理说明1、中断的理解中断你可以理解为就是⼀种电信号,是由硬件设备产⽣的然后发送给处理器,处理器接收到中断后,就会马上向操作系统反映此信号,之后就是系统的⼯作了。
这⾥有两个注意的地⽅,第⼀中断是随时都可以产⽣,意味着中断的处理程序随时都可以执⾏,所以得保证中断处理程序能够快速执⾏,才可能尽快的恢复中断代码执⾏,所以中断代码尽量简短。
第⼆每⼀个中断都有⾃⼰唯⼀的数字标记,这样操作系统才能对症下药2、注册中断中断处理程序中断处理程序是管理硬件的驱动程序的组成部分,每⼀设备都有相关的驱动程序,驱动程序可以通过request_irq()函数注册⼀个中断处理程序,并且激活给定的中断线,来处理指定的中断,原型如下:int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags, const char *devname, void *dev_id)第⼀个参数irq表⽰要分配的中断号,就我⽬前所接触的都是预先已经预定好的,还没试着通过探测或者动态来确定中断号第⼆个参数handler是⼀个指针,指向处理这个中断的实际中断处理程序,只要操作系统⼀接收到中断,该函数就被调⽤,这个函数稍后讨论第三个参数flags中断处理程序的标志,这个标志可以是⼀个也可以是多个,列举⼏个最重要的标志:IRQF_DISABLED: 该标志被设置后,意味内核在处理中断处理程序本⾝的时候,禁⽌了其他所有的中断。
如果不设置,中断处理程序可以与除本⾝之外的其他任何中断同时运⾏。
显⽽易见我们⼀般不去这么野蛮的设置这个标志IRQF_TIMER:为系统定时器的中断处理⽽准备的IRQF_SHARED:这个中断标志经常能遇见,这个标志意思就是多个中断处理程序之间可以共享中断线,概括起来就是没有这个标志就只能独⾃⼀个⼈占⽤,标志了,就是很多⼈可以占⽤这个中断号来第四个才参数就是⾃定义与中断设备相关的⽂本了第五个参数dev,看到第三个参数中IRQF_SHARED时候,你会不会有这样的疑问,假如现在我要释放当前共享的指定这个中断程序时候,我如何释放?会不会把其他占⽤也会删除掉,这就是第五个参数的意义,如果中断线是共享的,那么就必须传递能够代表当前设备的唯⼀信息request_irq()成功返回0,如果返回⾮0,就表⽰有错误发⽣,这个时候你可以考虑当前中断是否被占⽤了,所以可以加上IRQF_SHARED标志3、中断处理程序这⾥延续上⾯的handler指针,原型如下:Static irqreturn_t intr_handler(int irq, void *dev)这⾥唠叨⼀下,不知道⼤家⾯试时候有没有遇到像这样的题⽬__interrupt double compute_area (double radius){double area = PI * radius * radius;printf(" Area = %f", area);return area;},指出上⾯中断函数出现的错误,不知道的就认真看下⾯的 O(∩_∩)O第⼀个参数irq就是这个处理程序要响应的中断号,这个我认为现在没有多⼤意义了,因为上⾯有讲述到第五个参数的意义第⼆个参数dev是⼀个通⽤的指针,同样的,还是将上⾯讲述到的第五个参数拿过来理解。
Linux中断-简单中断,以GPIO中断为例
Linux中断-简单中断,以GPIO中断为例Linux中断基础概念中断上下⽂Linux内核的中断回调可以有两部分,即上下⽂。
当中断⽐较简单时,可以只有上⽂。
⼀般中断上⽂是指由中断产⽣的回调函数直接执⾏的部分;中断下⽂在上⽂中启⽤调度,再由内核调度。
中断上⽂:处理尽可能少的任务,特点是响应速度快中断下⽂:处理耗时任务,可以被新的中断打断中断嵌套Linux中断现在不能嵌套,之前可以中断相关的函数及命令获取中断号如果是有设备树的内核,⼀般通过节点的interrupt-parent和interrupt属性来描述中断对GPIO来说,GPIO的节点可以作为中断控制器,⼀般由BSP⼚家编写<linux/of_irq.h>//从设备树的设备节点中获取中断号unsigned int irq_of_parse_and_map(struct device_node *dev, int index);//参数:dev设备节点,index索引(节点中interrupts属性可能包含多条中断信息,通过index确认)//返回值:中断号//如果是GPIO的话,可以不从设备树中获取int gpio_to_irq(unsigned int gpio);//参数:gpio的编号//返回值:gpio对应的中断号申请中断申请中断的函数int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev);//参数://irq:要申请中断的中断号//handler:中断处理函数//flags:中断标志//name:中断名字,可在/proc/interrupts⽂件中看到对应的名字//dev:flags为IRQF_SHARED时,dev⽤来区分不同的中断。
⼀般将dev设置为设备结构体,传递给irq_handler_t的第⼆个参数//返回值:0申请成功,其他负值申请失败;如果返回-EBUSY标识已经被申请中断标志(申请中断函数的flags参数)定义在 include/linux/interrupt.h中常见的中断标志:标志功能IRQF_SHARED多个设备共享⼀个中断线,申请中断函数的dev参数是区分它们的唯⼀标志IRQF_ONESHOT单次中断,中断执⾏⼀次就结束IRQF_TRIGGER_NONE⽆触发IRQF_TRIGGER_RISING上升沿触发IRQF_TRIGGER_FALLING下降沿触发IRQF_TRIGGER_HIGH⾼电平触发IRQF_TRIGGER_LOW低电平触发中断处理函数使⽤request_irq申请中断的时候需要中断处理函数irq_handler_t来做参数,这⾥的irq_handler_t函数可以理解为中断上⽂的回调函数,发⽣中断时内核会调⽤处理函数。
论述linux操作系统处理中断的过程。
论述linux操作系统处理中断的过程。
Linux操作系统是一种开源的、自由的、类Unix操作系统,它的内核是由Linus Torvalds和全球志愿者团队开发的。
Linux内核的一个重要功能是处理中断,它可以使操作系统在执行某个任务时,直接响应外部的事件,如键盘输入、网络数据传输等。
本文将详细介绍Linux操作系统处理中断的过程。
1. 中断的概念中断是指计算机在执行某个任务时,被外部事件所打断,暂停当前任务的执行,转而去处理其他任务的一种机制。
中断可以分为硬件中断和软件中断两种。
硬件中断是指计算机硬件设备发出的中断信号,如键盘、鼠标、网络接口卡等。
当硬件设备发出中断信号时,CPU会暂停当前任务的执行,跳转到中断服务程序中去执行处理,处理完中断后再返回原来的任务。
软件中断是指操作系统内核发出的中断信号,可以通过系统调用的方式触发,如定时器中断、系统调用等。
软件中断和硬件中断的处理方式是相同的。
2. 中断的分类根据中断的优先级,中断可以分为以下几类:① 外部中断:由硬件设备发出,如键盘输入、鼠标移动、网络数据传输等,优先级最高。
② 内部中断:由软件程序触发,如定时器中断、系统调用等,优先级次之。
③ 异常中断:由于程序执行错误或硬件故障等原因而发生的中断,优先级最低。
3. 中断的处理过程在Linux操作系统中,中断处理的过程可以分为以下几个步骤:① 中断请求:当硬件设备发出中断请求信号时,会将中断请求信号发送给中断控制器,中断控制器会将中断请求信号发送给CPU。
② 中断响应:CPU接收到中断请求信号后,会暂停当前任务的执行,跳转到中断服务程序中去执行处理。
在跳转之前,CPU会将当前任务的上下文保存到内存中,以便后续恢复任务的执行。
③ 中断处理:中断服务程序会根据中断类型进行相应的处理,如读取键盘输入、发送网络数据等。
在处理过程中,中断服务程序可以访问进程内存空间、内核内存空间等,并可以与其他设备进行交互。
10-5 Linux操作系统 - 中断、异常及系统调用
10.5.4 中断上半部分的处理 一、 中断控制器 •每个硬件设备控制器都能通过中断请求线 发出中断请求(简称IRQ) •所有设备的中断请求线又连到中断控制器 的输入端。 •在x86单CPU的机器上采用两个8259A芯片作 为中断控制器,一主一从。
•当8259A有中断信号输入同时中断信号不被 屏蔽时,主8259A向CPU发出 INT信号,请求 中断。这时如果CPU是处于允许中断状况, CPU就会发信号给8259A进入中断响应周期。 •在对8259A芯片的初始化过程中,第n号中 断在IDT表中的向量号为 n+32
•IDT中向量号的使用情况如下: 0-31 异常与非屏蔽中断使用。 32-47 可屏蔽中断使用32至47 128(0x80)实现系统调用。 其余 未使用 •保存现场 发生异常时在核心栈的程序计数器eip的 值取决于具体情况。一般情况下eip保存的 下一条指令的地址,但对于页面异常,保存 的产生异常的这条指令的地址而不是下一条 指令的地址
中断向量表IDT •IDT是中断/异常处理在内核的入口。IDT表 项还记录了一些其它信息用以安全检查。 •IDT在系统初始化时创建。 •每个中断/异常都有一个向量号,该号的值 在0-255之间,该值是中断/异常在IDT中的 索引。 •每个中断/异常均有其相应的处理函数,中 断/异常在使用前必须在IDT中注册信息以保 证发生中断/异常时能找到相应的处理函数。
struct hw_interrupt_type { const char * typename; unsigned int (*startup)(unsigned int irq); void (*shutdown)(unsigned int irq); void (*enable)(unsigned int irq); void (*disable)(unsigned int irq); void (*ack)(unsigned int irq); void (*end)(unsigned int irq); void (*set_affinity)(unsigned int irq, unsigned long mask); };
linux中断处理流程
linux中断处理流程Linux中断处理流程Linux中断处理是操作系统中的一个重要组成部分,用于响应硬件设备的事件。
在Linux中,中断可以是外部中断,如硬件设备发送的中断信号,也可以是内部中断,如软件产生的异常或系统调用。
中断处理的目的是及时响应硬件设备的事件,并采取相应的措施来处理这些事件。
一、中断的触发中断是由硬件设备发送的一个信号,用于通知操作系统某个事件的发生。
这个信号可以是一个电平的变化,一个特定的数据包,或者一个指定的硬件寄存器的变化。
当硬件设备检测到某个事件发生时,它会向处理器发送一个中断信号,处理器会立即停止当前正在执行的任务,保存当前的上下文,并跳转到中断处理程序的入口点。
二、中断处理程序的执行中断处理程序是一个特殊的函数,负责处理中断事件。
当中断发生时,处理器会跳转到中断处理程序的入口点,并执行相应的代码。
中断处理程序的执行过程可以分为以下几个步骤:1. 保存上下文:在执行中断处理程序之前,处理器需要保存当前任务的上下文,包括程序计数器、寄存器和堆栈指针等。
这样可以确保在中断处理程序执行完成后,能够正确地返回到原来的任务。
2. 中断处理程序的执行:一旦保存了上下文,处理器就会执行中断处理程序的代码。
中断处理程序根据中断的类型,执行相应的操作。
例如,对于外部中断,中断处理程序可能需要读取硬件设备的状态,处理数据包或执行特定的操作。
对于内部中断,中断处理程序可能需要处理异常或系统调用。
3. 中断处理程序的结束:当中断处理程序执行完成后,处理器会恢复之前保存的上下文,并将控制权返回给原来的任务。
这样原来的任务就可以继续执行,而不会受到中断的影响。
三、中断处理的优先级在Linux中,中断处理有不同的优先级。
这是为了确保对于紧急事件的及时处理。
中断的优先级由硬件设备决定,通常是通过一个优先级编码器来实现的。
当多个中断同时发生时,处理器会按照优先级的顺序来处理中断。
高优先级的中断会立即被处理,而低优先级的中断则会被推迟到稍后处理。
linux中断处理之IRQ中断
linux中断处理之IRQ中断本文系本站原创,欢迎转载!转载请注明出处:/------------------------------------------一:前言在前一个专题里曾分析过所有IRQ中断处理流程,经过SAVE_ALL保存硬件环境后,都会进入do_IRQ()进行处理,今天接着分析do_IRQ()处理的相关东西.分为两部中断处理程序与软中断两个大的部份进行介绍.二:中断处理程序在驱动程序中,通常使用request_irq()来注册中断处理程序.我们先从注册中断处理程序的实现说起./*irq:可断号handler:中断处理程序irqflags:中断处理标志.SA_SHIRQ:共享中断线SA_INTERRUPT:快速处理中断必须在关中断的情况下运行.SA_SAMPLE_RANDOM:该中断可能用于产生一个随机数devname dev_id:设备名称与ID*/int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *),unsigned long irqflags,const char * devname,void *dev_id){int retval;struct irqaction * action;#if 1if (irqflags & SA_SHIRQ) {if (!dev_id)printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);}#endif//参数有效性判断if (irq >= NR_IRQS)return -EINVAL;if (!handler)return -EINVAL;// 分配一个irqactionaction = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_ATOMIC);if (!action)return -ENOMEM;action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;//将创建并初始化完在的action加入irq_desc[NR_IRQS]retval = setup_irq(irq, action);if (retval)kfree(action);return retval;}上面涉及到的irqaction结构与irq_desc[]的关系我们在上一节我们已经详细分析过了,这里不再赘述. 转进setup_irq():int setup_irq(unsigned int irq, struct irqaction * new){int shared = 0;unsigned long flags;struct irqaction *old, **p;irq_desc_t *desc = irq_desc + irq;//如果hander == no_irq_type:说明中断控制器不支持该IRQ线if (desc->handler == &no_irq_type)return -ENOSYS;sif (new->flags & SA_SAMPLE_RANDOM) {rand_initialize_irq(irq);}/** The following block of code has to be executed atomically*/spin_lock_irqsave(&desc->lock,flags);p = &desc->action;if ((old = *p) != NULL) {//判断这条中断线上的中断处理程序是否允许SHARE/* Can't share interrupts unless both agree to */if (!(old->flags & new->flags & SA_SHIRQ)) {spin_unlock_irqrestore(&desc->lock,flags);return -EBUSY;}/* add new interrupt at end of irq queue */do {p = &old->next;old = *p;} while (old);shared = 1;}//将其添加到中断处理函数链的末尾*p = new;//如果这一条线还没有被占用,初始化这条中断线//包含清标志,在8259A上启用这条中断线if (!shared) {desc->depth = 0;desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);desc->handler->startup(irq);}spin_unlock_irqrestore(&desc->lock,flags);//在proc下建立相关的文件register_irq_proc(irq);return 0;}现在知道怎么打一个中断处理程序挂到irq_desc[NR_IRQS]数组上了,继续分析中断处理中如何调用中断处理函数.从我们开篇时说到的do_IRQ()说起.asmlinkage unsigned int do_IRQ(struct pt_regs regs){//屏蔽高位,取得中断号int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code *///取得中断号对应的desc结构irq_desc_t *desc = irq_desc + irq;struct irqaction * action;unsigned int status;irq_enter();// 调试用,忽略#ifdef CONFIG_DEBUG_STACKOVERFLOW/* Debugging check for stack overflow: is there less than 1KB free? */{long esp;__asm__ __volatile__("andl %%esp,%0" :"=r" (esp) : "0" (THREAD_SIZE - 1));if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {printk("do_IRQ: stack overflow: %ld\n",esp - sizeof(struct thread_info));dump_stack();}}#endif//更新统计计数kstat_this_cpu.irqs[irq]++;spin_lock(&desc->lock);//给8259 回一个ack.回ack之后,通常中断控制会屏蔽掉此条IRQ线desc->handler->ack(irq);//清除IRQ_REPLAY IRQ_WAITING标志status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);//设置IRQ_PENDING:表示中断被应答,但没有真正被处理status |= IRQ_PENDING; /* we _want_ to handle it *//** If the IRQ is disabled for whatever reason, we cannot* use the action we have.*/action = NULL;//中断被屏蔽或者正在处理//IRQ_DIASBLED:中断被禁用//IRQ_INPROGRESS:这个类型的中断已经在被另一个CPU处理了if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action;status &= ~IRQ_PENDING; /* we commit to handling *///置位,表示正在处理中...status |= c; /* we are handling it */}desc->status = status;//没有挂上相应的中断处理例程或者不满足条件if (unlikely(!action))goto out;for (;;) {irqreturn_t action_ret;u32 *isp;union irq_ctx * curctx;union irq_ctx * irqctx;curctx = (union irq_ctx *) current_thread_info();irqctx = hardirq_ctx[smp_processor_id()];spin_unlock(&desc->lock);//通常curctx == irqctx.除非中断程序使用独立的4K堆栈.if (curctx == irqctx)action_ret = handle_IRQ_event(irq, ®s, action);else {/* build the stack frame on the IRQ stack */isp = (u32*) ((char*)irqctx + sizeof(*irqctx));irqctx->tinfo.task = curctx->tinfo.task;irqctx->tinfo.real_stack = curctx->tinfo.real_stack;irqctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack;irqctx->tinfo.previous_esp = current_stack_pointer();*--isp = (u32) action;*--isp = (u32) ®s;*--isp = (u32) irq;asm volatile(" xchgl %%ebx,%%esp \n"" call handle_IRQ_event \n"" xchgl %%ebx,%%esp \n": "=a"(action_ret): "b"(isp): "memory", "cc", "edx", "ecx");}spin_lock(&desc->lock);//调试用,忽略if (!noirqdebug)note_interrupt(irq, desc, action_ret, ®s);if (curctx != irqctx)irqctx->tinfo.task = NULL;//如果没有要处理的中断了,退出if (likely(!(desc->status & IRQ_c)))break;//又有中断到来了,继续处理desc->status &= ~c;}//处理完了,清除IRQ_INPROGRESS标志desc->status &= ~IRQ_INPROGRESS;out:/** The ->end() handler has to deal with interrupts which got* disabled while the handler was running.*///处理完了,调用中断控制器的end.通常此函数会使中断控制器恢复IRQ线中断desc->handler->end(irq);spin_unlock(&desc->lock);//irq_exit():理论上中断处理完了,可以处理它的下半部了irq_exit();return 1;}这段代码比较简单,但里面几个标志让人觉的很迷糊,列举如下:IRQ_DISABLED:相应的IRQ被禁用.既然中断线被禁用了,也就不会产生中断,进入do_IRQ()了?因为电子器件的各种原因可能会产生“伪中断”上报给CPU.IRQ_PENDING:CPU收到这个中断信号了,已经给出了应答,但并末对其进行处理.回顾上面的代码,进入do_IRQ后,发送ack,再设置此标志.IRQ_ INPROGRESS:表示这条IRQ线的中断正在被处理.为了不弄脏CPU的高速缓存.把相同IRQ线的中断放在一起处理可以提高效率,且使中断处理程序不必重入举例说明:如果CPU A接收到一个中断信号.回一个ACK,设置c,假设此时末有这个中断线的中断处理程序在处理,继而会将标志位设为IRQ_ INPROGRESS.转去中断处理函数执行.如果此时,CPU B检测到了这条IRQ线的中断信号.它会回一个ACK.设置IRQ_PENDING.但时此时这条IRQ线的标志为IRQ_ INPROGRESS.所以,它会进经过goto out退出.如果cpu A执行完了中断处理程序,判断它的标志线是否为IRQ_PENDING.因为CPU B已将其设为了IRQ_PENDING.所以继续循环一次.直到循环完后,清除IRQ_INPROGRESS标志注意上述读写标志都是加锁的.linux采用的这个方法,不能不赞一个*^_^*继续看代码:asmlinkage int handle_IRQ_event(unsigned int irq,struct pt_regs *regs, struct irqaction *action){int status = 1; /* Force the "do bottom halves" bit */int ret, retval = 0;//如果没有设置SA_INTERRUPT.将CPU 中断打开//应该尽量的避免CPU关中断的情况,因为CPU屏弊本地中断,会使//中断丢失if (!(action->flags & SA_INTERRUPT))local_irq_enable();//遍历运行中断处理程序do {ret = action->handler(irq, action->dev_id, regs);if (ret == IRQ_HANDLED)status |= action->flags;retval |= ret;action = action->next;} while (action);if (status & SA_SAMPLE_RANDOM)add_interrupt_randomness(irq);//关中断local_irq_disable();return retval;}可能会有这样的疑问.如果在一根中断线上挂上了很多个中断处理程序,会不会使这一段程序的效率变得很低下呢?事实上,我们在写驱动程序的过程中,都会首先在中断处理程序里判断设备名字与设备ID,只有条件符合的设备中断才会变处理.三:软中断为了提高中断的响应速度,很多操作系统都把中断分成了两个部份,上半部份与下半部份.上半部份通常是响应中断,并把中断所得到的数据保存进下半部.耗时的操作一般都会留到下半部去处理.接下来,我们看一下软中断的处理模型:Start_kernel() à softirq_init();在softirq_init()中会注册两个常用类型的软中断,看具体代码:void __init softirq_init(void){open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);}//参数含义:nr:软中断类型action:软中断处理函数data:软中断处理函数参数void open_softirq(int nr, void (*action)(struct softirq_action*), void *data){softirq_vec[nr].data = data;softirq_vec[nr].action = action;}static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;struct softirq_action{void (*action)(struct softirq_action *);void *data;};在上面的代码中,我们可以看到:open_softirq()中.其实就是对softirq_vec数组的nr项赋值.softirq_vec是一个32元素的数组,实际上linux内核只使用了六项. 如下示:enum{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,SCSI_SOFTIRQ,TASKLET_SOFTIRQ}另外.如果使软中断能被CPU调度,还得要让它激活才可以.激活所使用的函数为__raise_softirq_irqoff()代码如下:#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL << (nr); } while (0)这个宏使local_softirq_pending的nr位置1好了,经过open_softirq()à local_softirq_pending()后,我们来看下软中断怎么被CPU调度.继续上面中断处理的代码.在处理完硬件中断后,会调用irq_exit().这就是软中断的入口点了,我们来看下#define irq_exit() \do { \preempt_count() -= IRQ_EXIT_OFFSET; \//注意了,软中断不可以在硬件中断上下文或者是在软中断环境中使用哦^_^//softirq_pending()的判断,注意我们上面分析过的_raise_softirqoff().它判断当前cpu有没有激活软中断if (!in_interrupt() && softirq_pending(smp_processor_id())) \do_softirq(); \preempt_enable_no_resched(); \} while (0)跟踪进do_softirq()asmlinkage void do_softirq(void){__u32 pending;unsigned long flags;//在硬件中断环境中,退出if (in_interrupt())return;//禁止本地中断,不要让其受中断的影响local_irq_save(flags);pending = local_softirq_pending();//是否有软中断要处理?if (pending)__do_softirq();//恢复CPU中断local_irq_restore(flags);}转入__do_softirq()asmlinkage void __do_softirq(void){struct softirq_action *h;__u32 pending;int max_restart = MAX_SOFTIRQ_RESTART;int cpu;pending = local_softirq_pending();//禁止软中断,不允许软中断嵌套local_bh_disable();cpu = smp_processor_id();restart:/* Reset the pending bitmask before enabling irqs *///把挂上去的软中断清除掉,因为我们在这里会全部处理完local_softirq_pending() = 0;//开CPU中断local_irq_enable();//softirq_vec:32元素数组h = softirq_vec;//依次处理挂上去的软中断do {if (pending & 1) {//调用软中断函数h->action(h);rcu_bh_qsctr_inc(cpu);}h++;pending >>= 1;} while (pending);//关CPU 中断local_irq_disable();pending = local_softirq_pending();//在规定次数内,如果有新的软中断了,可以继续在这里处理完if (pending && --max_restart)goto restart;//依然有没有处理完的软中断,为了提高系统响应效率,唤醒softirqd进行处理if (pending)wakeup_softirqd();//恢复软中断__local_bh_enable();}从上面的处理流程可以看到,软中断处理就是调用open_ softirq()的action参数.这个函数对应的参数是软中断本身(h->action(h)),采用这样的形式,可以在改变softirq_action结构的时候,不会重写软中断处理函数在进入了软中断的时候,使用了in_interrupt()来防止软中断嵌套,和抢占硬中断环境。
linux系统中断详解
linux系统中断详解最近为了解决风控问题,⼀直在研究linux的系统内核,经过⼀段时间的学习,先整理出⼀份关于linux中断的⼩记。
1.什么是中断?计算机cpu在执⾏task时,不可能每次都将任务执⾏完毕,会因为各种不同的场景⽽暂停执⾏,所谓中断就是这个暂停执⾏的过程。
2.中断算是⼀种错误吗?严格来说,中断当然算是运⾏错误的⼀种,但是,由于其不可避免,程序开发者⾃然可以将其视为⼀种机制,加以运⽤,反⽽更容易帮助我们完成现实功能的实现。
3.中断的分类从产⽣原因上看,中断可以分为软件中断和硬件中断,⽽从类别划分上看,也可以氛围有出错码中断和⽆出错码中断。
⼆者并不排斥,⽐如说,中断分类存在int0~int255中,其中int0 ~ int31 表⽰软件中断,int32 ~ int255: 可由⽤户⾃⼰设置。
其中int32 ~ int47 对应8259A的IRQ0 ~ IRQ15中断。
需要注意的是,int128 为系统调⽤中断(system_call)。
如果⼤家有看过内核源码或者其他汇编代码,会发现汇编语⾔在调⽤c函数时,就是使⽤system_call,由此可见,调⽤其他函数,也是⼀种中断。
这⾥不⽌局限于linux系统,其中中断逻辑囊括所有计算机逻辑执⾏逻辑,其最基础的实现逻辑就是计算机0/1与或逻辑,属于机器语⾔中最低级也是最⾼效的展现形式。
4.中断时堆栈变化情况堆栈相关知识此处不做介绍,不了解的同学可以⾃⾏查找⼀下相关资料。
上图可以⾮常清楚的展⽰中断发⽣时堆栈的变化情况。
即中断发⽣前,需要将图中的信息按照先后顺序,压⼊中断处理程序的堆栈中。
下⾯进⾏具体的分析:SS(stack segment): 堆栈段ESP(extended stack pointer): 堆栈指针EFLAGS : 状态标志寄存器CS(code segment): 代码段EIP(extended instruction pointer) : 中断后要执⾏的下⼀条指令的地址1)有/⽆出错码:我们只需知道,对于某些中断,⽐如:int0(⽆错误码)是不需要保存出错码的,⽽像int8等中断是需要保存出错码的。
linux内核中断处理的irq_thread机制 -回复
linux内核中断处理的irq_thread机制-回复Linux内核中断处理的irq_thread机制是一种处理中断请求的机制,旨在提高中断处理的效率和可扩展性。
在本文中,我们将详细介绍irq_thread 的原理和实现细节,并分析其对系统性能的影响。
一、引言随着计算机系统的发展,硬件设备的种类和数量不断增加,从而引发了对中断处理的需求。
在传统中断处理模式下,Linux内核需要通过一个叫做中断处理程序(interrupt handler)的功能模块来处理硬件设备发出的中断请求。
然而,这种模式在多核系统和高速设备的环境下往往存在性能瓶颈和可扩展性问题。
为了解决这些问题,Linux内核引入了irq_thread机制。
二、irq_thread机制的原理irq_thread机制的核心思想是将中断处理程序从内核上下文中移出,转而在用户态创建一个专门的线程来处理中断请求。
这个线程被称为irq_thread。
irq_thread在内核注册的中断向量表中占据一个特殊的槽位,当对应的硬件设备发生中断时,硬件会触发相应的中断请求,并将中断处理程序的控制权交给irq_thread。
在irq_thread的实现中,内核通过分配一个独立的线程栈和一组寄存器保存irq_thread的上下文。
同时,irq_thread中还有一个运行队列,用于保存需要处理的中断任务。
当一个中断请求到达时,irq_thread会从硬件设备读取中断状况,并将相应的中断任务放入运行队列。
三、irq_thread机制的实现细节1. 中断控制器:irq_thread机制需要硬件支持,其中关键的组件是中断控制器(interrupt controller)。
中断控制器是一种硬件设备,用于管理和分发中断请求。
在Linux内核中,主流的中断控制器有APIC(Advanced Programmable Interrupt Controller)和IOAPIC(Input/Output Advanced Programmable Interrupt Controller)等。
Linux子系统之中断机制研究
Linux子系统之中断机制研究本文为速成教材,非精细研究,适合想了解linux中断机制,和从事linux驱动开发或者调式IRQ相关的驱动的工程师使用。
1 Linux中断机制1.1 认识三个重要的数据结构1.1.1 中断描述符结构irq_descirq_desc:中断全局描述符struct irq_desc {unsigned intirq;注释:表示此中断描述符的中断号struct timer_rand_state *timer_rand_state;注释:timer伪随机数状态结构指针unsigned int*kstat_irqs;#ifdef CONFIG_INTR_REMAPstruct irq_2_iommu*irq_2_iommu;#endifirq_flow_handler_t handle_irq;注释:当前中断的内核中断处理函数入口(非用户定义)struct irq_chip *chip;注释:中断的硬件chip级描述符,提供底层的硬件访问。
具体见下面结构描述struct msi_desc*msi_desc;void*handler_data;voidstruct irqaction*action;/* IRQ action list */注释:标识当出现IRQ时要调用的中断服务例程。
该字段指向IRQ的irqaction链表的第一个元素。
unsigned intstatus;/* IRQ status */注释:描述IRQ线状态的一组标志unsigned intdepth;/* nested irq disables */注释:如果IRQ线被激活,则显示0,如果IRQ线被禁止了不止一次,则显示一个正数。
unsigned intwake_depth;/* nested wake enables */unsigned intirq_count;/* For detecting broken IRQs */unsigned longlast_unhandled;/* Aging timer for unhandled count */unsigned intirqs_unhandled;raw_spinlock_tlock;#ifdef CONFIG_SMPcpumask_var_taffinity;const struct cpumask*affinity_hint;node;#ifdef CONFIG_GENERIC_PENDING_IRQcpumask_var_tpending_mask;#endif#endifatomic_tthreads_active;wait_queue_head_twait_for_threads;#ifdef CONFIG_PROC_FSstruct proc_dir_entry*dir;#endifconst char*name;} ____cacheline_internodealigned_in_smp;Linux内核将所有的中断统一编号,使用一个irq_desc结构数组来描述这些中断;每个数组项对应一个中断,也可能是一组中断,它们共用相同的中断号,里面记录了中断的名称、中断状态、中断标记(比如中断类型、是否共享中断等),并提供了中断的低层硬件访问函数(清除、屏蔽、使能中断),提供了这个中断的处理函数入口,通过它可以调用用户注册的中断处理函数。
linux 软中断原理
linux 软中断原理Linux软中断是操作系统中一种用于处理紧急任务的机制。
它采用的是一种特殊的中断方式,可以在用户态和内核态之间切换,以提供高效的中断处理能力。
在Linux内核中,软中断是一种特殊的中断处理机制。
相比硬中断,软中断的处理过程更加高效,可以在短时间内完成中断处理,并且不会占用过多的系统资源。
软中断的原理是通过将中断处理函数放入一个队列中,然后由内核线程负责依次执行队列中的中断处理函数。
在Linux内核中,每个软中断都有一个唯一的标识符,并且有一个对应的中断处理函数。
当某个软中断被触发时,中断处理函数会被调用。
这样,操作系统就可以根据不同的软中断来执行相应的中断处理操作。
软中断的使用可以提高系统的响应速度和处理能力。
在Linux内核中,有一些常用的软中断,如定时器中断、网络中断等。
这些软中断可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。
软中断的原理是基于内核线程的机制实现的。
在Linux内核中,有一个特殊的内核线程,称为软中断处理线程。
这个线程会不断地检查软中断队列,如果队列中有待处理的中断,就会调用相应的中断处理函数。
软中断的实现需要考虑到多核处理器的情况。
在多核处理器中,每个CPU核心都有自己的软中断处理队列和软中断处理线程。
这样,每个CPU核心都可以独立地处理软中断,提高系统的并发处理能力。
软中断的使用可以提高系统的可靠性和稳定性。
在Linux内核中,软中断可以用于处理系统中的紧急任务,如定时器中断、网络中断等。
这些紧急任务需要及时地处理,以保证系统的正常运行。
Linux软中断是一种高效的中断处理机制,可以提高系统的响应速度和处理能力。
它通过将中断处理函数放入一个队列中,然后由专门的内核线程负责执行,可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。
通过软中断的使用,可以提高系统的可靠性和稳定性,保证系统的正常运行。
linux中断源的分类
linux中断源的分类Linux中断源的分类Linux是一种开源操作系统,它的设计目标是为了在各种硬件平台上运行。
在Linux中,中断是一种重要的机制,它用于处理硬件设备的异步事件。
中断源是触发中断的事件或条件,根据中断源的不同特性,我们可以将Linux中断源分为以下几类。
1. 硬件中断源:硬件中断源是指由硬件设备产生的中断。
例如,当键盘按下某个键时,键盘控制器将产生一个硬件中断,通知操作系统有键盘事件发生。
其他常见的硬件中断源包括鼠标、硬盘、网卡等设备。
硬件中断源的触发通常是由硬件设备本身引起的,操作系统需要及时响应并处理这些中断。
2. 软件中断源:软件中断源是指由软件产生的中断。
在Linux中,软件中断源主要有两种:一种是由操作系统内核产生的中断,例如时钟中断、定时器中断等;另一种是由用户程序产生的中断,例如系统调用、信号等。
软件中断源的触发是由软件主动发起的,它可以用于实现各种功能和服务,如进程调度、定时任务等。
3. 异常中断源:异常中断源是指由异常事件引起的中断。
异常是指在程序执行过程中发生的一些非正常事件,例如除零错误、缺页错误等。
当这些异常事件发生时,操作系统会产生一个异常中断,通常会导致程序终止或进行相应的错误处理。
异常中断源的触发是由程序执行过程中的错误引起的。
4. 外部中断源:外部中断源是指由外部事件引起的中断。
在Linux中,外部中断源通常是指由外部设备或外部信号引起的中断。
例如,当有新的网络数据包到达时,网卡会产生一个外部中断,通知操作系统有数据可读。
外部中断源的触发是由外部事件的发生引起的,操作系统需要及时响应并处理这些中断。
5. 虚拟中断源:虚拟中断源是指由虚拟化技术引起的中断。
在虚拟化环境中,虚拟机监视器(VMM)负责管理和分配物理资源,并模拟硬件设备给虚拟机使用。
当虚拟机运行时,VMM会将一部分中断源模拟给虚拟机,例如时钟中断、网络中断等。
虚拟中断源的触发是由虚拟化环境中的事件引起的,VMM需要及时将这些中断传递给相应的虚拟机。
嵌入式Linux系统中断设计要点详解
类型 、 体系结构的设计及机器本 身。Ln x中断移植 不仅 需要 i u
充 分 了解 使 用 片 的 中 断 设 计 , 时还 需要 对 Ln x内 核 中 断 同 iu 系 统 的 结 构 与 运 作 有 较 深 入 的 理 解 。 i x内核 代 码 极 为 庞 人 Ln u 和 复 杂 , 提 供 的 A I 多 , 给 Lnx的 中 断 移 植 造 成 了 内核 P众 这 iu
211 中断 控 制 器 描 述 符 i c p .. r hi q
_
图 1 Ln x内核 中断 函 数 抽 象 图 iu
很大 的困难 。因此, 本文 以三星公司 的 ¥ C 4 0芯片为例, 3 24 对
嵌 入 式 Ln x版 本 263 1 中 断 系 统 进 行 分 析 , 绍 嵌 入 式 iu ( .. 的 2 介 中断设计的要点与优化细节。
便中断 自己的当前工作转 而处理中断。此后 , 处理器会通 知操 作系统 已经产生 中断, 这样 , 操作系统就可 以对 这个 中断进行
作者 简 介 :罗智 杰 , , 东广 州人 , 学士 , 理 工 程 师 。主 要 研 究方 向 : n x内核 开发 , 入 式 系统 设 计 男 广 双 助 lu i 嵌
21 中断 服 务 例 程 描 述 符 i at n .. 2 r ci q o 在 I Q 描 述 符 中 我 们 看 到 指 针 at n 的 结 构 为 i a— R ci o r c q t n 它 是 为 多 个 设 备 能 共 享 条 中 断 线 而 设 鬣 的 一 数 据 结 i , o 个
21 中 断方 式 设置 符 i o ade .. 3 r f w hn lr ql i o a de 要 明 确 了 内核 中 各 种 类 型 的 中 断 处 r f w hnl ql r主 理 方 式 ,用 户 可 以根 据 自己 需 要 的 形 式 制 定 不 同 的 中 断 处 理
【原创】Linux中断子系统(四)-Workqueue
【原创】Linux中断子系统(四)-Workqueue背景•Read the fucking source code! --By 鲁迅•A picture is worth a thousand words. --By 高尔基说明:1.Kernel版本:4.142.ARM64处理器,Contex-A53,双核3.使用工具:Source Insight 3.5, Visio1. 概述•Workqueue工作队列是利用内核线程来异步执行工作任务的通用机制;•Workqueue工作队列可以用作中断处理的Bottom-half机制,利用进程上下文来执行中断处理中耗时的任务,因此它允许睡眠,而Softirq和Tasklet在处理任务时不能睡眠;来一张概述图:•在中断处理过程中,或者其他子系统中,调用workqueue的调度或入队接口后,通过建立好的链接关系图逐级找到合适的worker,最终完成工作任务的执行;2. 数据结构2.1 总览此处应有图:•先看看关键的数据结构:1.<>e>work_struct:工作队列调度的最小单位,work item;2.workqueue_struct:工作队列,work item都挂入到工作队列中;3.worker:work item的处理者,每个worker对应一个内核线程;4.worker_pool:worker池(内核线程池),是一个共享资源池,提供不同的worker来对work item进行处理;5.pool_workqueue:充当桥梁纽带的作用,用于连接workqueue和worker_pool,建立链接关系;下边看看细节吧:2.2 workstruct work_struct用来描述work,初始化一个work并添加到工作队列后,将会将其传递到合适的内核线程来进行处理,它是用于调度的最小单位。
关键字段描述如下:struct work_struct {atomic_long_t data; //低比特存放状态位,高比特存放worker_pool的ID或者pool_workqueue的指针struct list_head entry; //用于添加到其他队列上work_func_t func; //工作任务的处理函数,在内核线程中回调#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;#endif};图片说明下data字段:2.3 workqueue•内核中工作队列分为两种:1.bound:绑定处理器的工作队列,每个worker创建的内核线程绑定到特定的CPU上运行;2.unbound:不绑定处理器的工作队列,创建的时候需要指定WQ_UNBOUND标志,内核线程可以在处理器间迁移;•内核默认创建了一些工作队列(用户也可以创建):1.system_mq:如果work item执行时间较短,使用本队列,调用schedule[_delayed]_work[_on]()接口就是添加到本队列中;2.system_highpri_mq:高优先级工作队列,以nice值-20来运行;3.system_long_wq:如果work item执行时间较长,使用本队列;4.system_unbound_wq:该工作队列的内核线程不绑定到特定的处理器上;5.system_freezable_wq:该工作队列用于在Suspend时可冻结的work item;6.system_power_efficient_wq:该工作队列用于节能目的而选择牺牲性能的work item;7.system_freezable_power_efficient_wq:该工作队列用于节能或Suspend时可冻结目的的work item;struct workqueue_struct关键字段介绍如下:struct workqueue_struct {struct list_head pwqs; /* WR: all pwqs of this wq */ //所有的pool_workqueue都添加到本链表中struct list_head list; /* PR: list of all workqueues */ //用于将工作队列添加到全局链表workqueues中struct list_head maydays; /* MD: pwqs requesting rescue */ //rescue状态下的pool_workqueue添加到本链表中struct worker *rescuer; /* I: rescue worker */ //rescuer内核线程,用于处理内存紧张时创建工作线程失败的情况struct pool_workqueue *dfl_pwq; /* PW: only for unbound wqs */char name[WQ_NAME_LEN]; /* I:workqueue name *//* hot fields used during command issue, aligned to cacheline */unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */ //Per-CPU都创建pool_workqueuestruct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */ //Per-Node创建pool_workqueue...};2.4 worker•每个worker对应一个内核线程,用于对work item的处理;•worker根据工作状态,可以添加到worker_pool的空闲链表或忙碌列表中;•worker处于空闲状态时并接收到工作处理请求,将唤醒内核线程来处理;•内核线程是在每个worker_pool中由一个初始的空闲工作线程创建的,并根据需要动态创建和销毁;关键字段描述如下:struct worker {/* on idle list while idle, on busy hash table while busy */union {struct list_head entry; /* L: while idle */ //用于添加到worker_pool的空闲链表中struct hlist_node hentry; /* L: while busy */ //用于添加到worker_pool的忙碌列表中};struct work_struct *current_work; /* L: work being processed */ //当前正在处理的workwork_func_t current_func; /* L: current_work's fn */ //当前正在执行的work回调函数struct pool_workqueue *current_pwq; /* L: current_work's pwq */ //指向当前work所属的pool_workqueuestruct list_head scheduled; /* L: scheduled works */ //所有被调度执行的work都将添加到该链表中/* 64 bytes boundary on 64bit, 32 on 32bit */struct task_struct *task; /* I: worker task */ //指向内核线程struct worker_pool *pool; /* I: the associated pool */ //该worker所属的worker_pool/* L: for rescuers */struct list_head node; /* A: anchored at pool->workers */ //添加到worker_pool->workers链表中/* A: runs through worker->node */...};2.5 worker_pool•worker_pool是一个资源池,管理多个worker,也就是管理多个内核线程;•针对绑定类型的工作队列,worker_pool是Per-CPU创建,每个CPU都有两个worker_pool,对应不同的优先级,nice值分别为0和-20;•针对非绑定类型的工作队列,worker_pool创建后会添加到unbound_pool_hash哈希表中;•worker_pool管理一个空闲链表和一个忙碌列表,其中忙碌列表由哈希管理;关键字段描述如下:struct worker_pool {spinlock_t lock; /* the pool lock */int cpu; /* I: the associated cpu */ //绑定到CPU的workqueue,代表CPU IDint node; /* I: the associated node ID */ //非绑定类型的workqueue,代表内存Node IDint id; /* I: pool ID */unsigned int flags; /* X: flags */unsigned long watchdog_ts; /* L: watchdog timestamp */struct list_head worklist; /* L: list of pending works */ //pending状态的work添加到本链表int nr_workers; /* L: total number of workers */ //worker的数量/* nr_idle includes the ones off idle_list for rebinding */int nr_idle; /* L: currently idle ones */struct list_head idle_list; /* X: list of idle workers */ //处于IDLE状态的worker添加到本链表struct timer_list idle_timer; /* L: worker idle timeout */struct timer_list mayday_timer; /* L: SOS timer for workers *//* a workers is either on busy_hash or idle_list, or the manager */DECLARE_HASHTABLE(busy_hash,BUSY_WORKER_HASH_ORDER); //工作状态的worker添加到本哈希表中/* L: hash of busy workers *//* see manage_workers() for details on the two manager mutexes */struct worker *manager; /* L: purely informational */struct mutex attach_mutex; /* attach/detach exclusion */struct list_head workers; /* A: attached workers */ //worker_pool管理的worker添加到本链表中struct completion *detach_completion; /* all workers detached */struct ida worker_ida; /* worker IDs for task name */struct workqueue_attrs *attrs; /* I: worker attributes */struct hlist_node hash_node; /* PL: unbound_pool_hash node */ //用于添加到unbound_pool_hash中...} ____cacheline_aligned_in_smp;2.6 pool_workqueue•pool_workqueue充当纽带的作用,用于将workqueue和worker_pool关联起来;关键字段描述如下:struct pool_workqueue {struct worker_pool *pool; /* I: the associated pool */ //指向worker_poolstruct workqueue_struct *wq; /* I: the owning workqueue */ //指向所属的workqueueint nr_active; /* L: nr of active works */ //活跃的work数量int max_active; /* L: max active works */ //活跃的最大work数量struct list_head delayed_works; /* L: delayed works */ //延迟执行的work挂入本链表struct list_head pwqs_node; /* WR: node on wq->pwqs */ //用于添加到workqueue链表中struct list_head mayday_node; /* MD: node on wq->maydays */ //用于添加到workqueue链表中...} __aligned(1 << WORK_STRUCT_FLAG_BITS);2.7 小结再来张图,首尾呼应一下:3. 流程分析3.1 workqueue子系统初始化•workqueue子系统的初始化分成两步来完成的:workqueue_init_early和workqueue_init。
Linux之时钟中断详解
Linux之时钟中断详解⽬录时钟中断的产⽣Linux实现时钟中断的全过程1.可编程定时/计数器的初始化2.与时钟中断相关的函数3.系统调⽤返回函数:总结在Linux的0号中断是⼀个定时器中断。
在固定的时间间隔都发⽣⼀次中断,也是说每秒发⽣该中断的频率都是固定的。
该频率是常量HZ,该值⼀般是在100 ~ 1000之间。
该中断的作⽤是为了定时更新系统⽇期和时间,使系统时间不断地得到跳转。
另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。
指的是调⽤scheduler_tick递减进程的时间⽚,若进程的时间⽚递减到0,进程则被调度出去⽽放弃CPU使⽤权。
时钟中断的产⽣Linux的OS时钟的物理产⽣原因是可编程定时/计数器产⽣的输出脉冲,这个脉冲送⼊CPU,就可以引发⼀个中断请求信号,我们就把它叫做时钟中断。
“时钟中断”是特别重要的⼀个中断,因为整个操作系统的活动都受到它的激励。
系统利⽤时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利⽤时钟中断进⾏记帐、监督系统⼯作以及确定未来的调度优先级等⼯作。
可以说,“时钟中断”是整个操作系统的脉搏。
时钟中断的物理产⽣如图所⽰:操作系统对可编程定时/计数器进⾏有关初始化,然后定时/计数器就对输⼊脉冲进⾏计数(分频),产⽣的三个输出脉冲Out0、Out1、Out2各有⽤途,很多接⼝书都介绍了这个问题,我们只看Out0上的输出脉冲,这个脉冲信号接到中断控制器8259A_1的0号管脚,触发⼀个周期性的中断,我们就把这个中断叫做时钟中断,时钟中断的周期,也就是脉冲信号的周期,我们叫做“滴答”或“时标”(tick)。
从本质上说,时钟中断只是⼀个周期性的信号,完全是硬件⾏为,该信号触发CPU去执⾏⼀个中断服务程序,但是为了⽅便,我们就把这个服务程序叫做时钟中断。
Linux实现时钟中断的全过程1.可编程定时/计数器的初始化IBM PC中使⽤的是8253或8254芯⽚。
Linux下串口编程中断
L i n u x下串口编程中断 The pony was revised in January 2021Linux 下串口编程的文章网上是满天飞,但大都是出自一篇文章,而且写的都是些基本的操作,像控制 RTS/CTS 等串口引脚状态,接收发送二进制数据等,都没有很好的说明,我在使用中遇到了些问题,写出来,希望能对大家有所帮助,少走弯路,呵呵!我使用的操作系统是 Redhat9 , gcc 版本是 3.2.2其实在 linux 下对串口的设置主要是通过 termios 这个结构体实现的,但是这个结构体却没有提供控制 RTS 或获得 CTS 等串口引脚状态的接口,可以通过 ioctl 系统调用来获得 / 控制。
获得:ioctl(fd, TIOCMGET, &controlbits);if (controlbits & TIOCM_CTS)printf(“有信号 \n”);elseprintf(“无信号 \n”);设置:ioctl(fd, TIOCMGET, &ctrlbits) ;if (flag)ctrlbits |= TIOCM_RTS;elsectrlbits &= ~TIOCM_RTS;ioctl(fd, TIOCMSET, &ctrlbits);其实 TIOCM_RTS 有效后是把串口的 RTS 设置为有信号,但串口的电平为低时是有信号,为高时为无信号,和用 TIOCMGET 获得的状态正好相反,也就是说 TIOCMGET/TIOCMSET 只是获得 / 控制串口的相应引脚是否有信号,并不反应当前串口的真实电平高低。
网上许多流行的 linux 串口编程的版本中都没对 c_iflag ( termios 成员变量)这个变量进行有效的设置,这样传送 ASCII 码时没什么问题,但传送二进制数据时遇到0x0d,0x11 和 0x13 却会被丢掉。
不用说也知道,这几个肯定是特殊字符,被用作特殊控制了。
IA32上Linux内核中断机制分析
427callsmp_/**/name;\
428jmpret_from_intr;
其中,SAVE_ALL宏就是用来保存寄存器的。
内核书籍中经常提到的中断上下文,指的是内核正在运行中断服务程序或softirq,无法代表当前进程的情形。中断上下文没有自己专有的堆栈,相反,它借用被中断进程的内核堆栈──IA-32上的Linux默认这个堆栈只有8k大小,而且很可能在处理中断的过程中又被另一个中断源中断。因此如果你自己编写中断处理程序,递归层次太深或者函数局部变量太大,都有可能导致栈溢出。(i386有一个4KStacks补丁,如果编译时打开该选项,则中断上下文使用独立的栈,而不占用被中断进程的。)
2)陷阱(Trap)。陷阱处理返回时,不重新执行引发陷阱的那条指令。
3)中止(Abort)。中止是严重的异常,将导致任务的中止而不会返回。
还有一种是程序产生的异常,如INT3指令、BOUND指令等。CPU把这种异常当作是陷阱来处理。
5,中断描述表IDT
异常与中断发生时,都需要到IDT中查找相关信息,以找到对应的处理程序以及其他动作。需要注意的是,保护模式下发生权限提升时,中断穿越的是中断门,而异常穿越的是陷阱门。二者的区别是:当CPU穿越中断门时,是自动关中断的;而穿越异常门则不会。
return1;
}
spin_lock(&desc->lock);
desc->handler->ack(irq);/*给i8259A或APIC应答信号*/
/*
*REPLAYiswhenLinuxresendsanIRQthatwasdroppedearlier
*WAITINGisusedbyprobetomarkirqsthatarebeingtested
linux的irq中断子系统分析
本文以Linux中断子系统架构为视角,旨在提供一个对Linux中断系统的全局认识,不涉及具体实现细节。
一、Linux中断子系统架构在Linux中断子系统(generic irq)出现之前,内核使用__do_IRQ处理所有的中断,这意味着__do_IRQ中要处理各种类型的中断,这会导致软件的复杂性增加,层次不分明,而且代码的可重用性也不好。
通用中断子系统的原型最初出现于ARM体系中,一开始内核的开发者们把3种中断类型区分出来(电平中断、边缘中断、简易中断),后来又针对某些需要回应eoi(end of interrupt)的中断控制器,加入了fast eoi type,针对smp加入了per cpu type。
把这些不同的中断类型抽象出来后,成为了中断子系统的流控层。
要使所有的体系架构都可以重用这部分的代码,中断控制器也被进一步地封装起来,形成了中断子系统中的芯片级硬件封装层。
二、芯片级硬件封装层中断系统与CPU硬件关系密切,linux系统为了兼容各种型号的CPU,提供了对于各种CPU的特性及其中断控制器的底层封装,这样就可以把底层的硬件实现尽可能地隐藏起来,使得驱动程序的开发人员不用关注底层的实现。
该部分主要工作是:●实现不同CPU的中断入口,初始化中断向量表,该部分通常由汇编实现。
●对中断控制器实现软件抽象(struct irq_chip),源码路径如:”arch/arm/plat-s3c24xx/irq.c”该部分初始化过程中,系统根据设备使用的中断控制器的类型,实现irq_chip结构中的接口,并把该irq_chip实例注册到irq_desc.irq_data.chip字段中,这样各个irq和中断控制器就进行了关联,只要知道irq编号,即可得到对应到irq_desc结构,进而可以通过chip指针访问中断控制器。
其初始化流程如下图所示:三、中断流控层由linux内核提供,所谓中断流控是指合理并正确地处理连续发生的中断,比如一个中断在处理中,同一个中断再次到达时如何处理,何时应该屏蔽中断,何时打开中断,何时回应中断控制器等一系列的操作。
linux内核中断处理流程
Linux内核中断处理流程一、中断概述在计算机系统中,中断是一种重要的机制,它允许处理器暂停当前的程序执行,转而处理更为紧急的事件。
Linux内核作为操作系统的核心,负责管理和处理系统中的各种中断。
中断可以分为外中断(硬件中断)和内中断(软件中断或陷阱),它们在Linux内核中有不同的处理流程。
二、中断处理流程1. 中断请求(IRQ)的接收当中断控制器接收到一个外部设备的中断请求时,它会根据优先级等信息决定是否向CPU发送中断信号。
如果决定发送,CPU会暂停当前的执行流程,开始中断处理。
2. 中断向量的确定每个中断都有一个唯一的中断向量与之对应。
中断向量是中断处理程序的入口点。
在x86架构中,中断向量表(Interrupt Descriptor Table,IDT)存储了系统中所有中断处理程序的地址信息。
CPU根据中断向量从IDT中找到对应的中断处理程序入口。
3. 中断处理程序的执行一旦确定了中断处理程序的入口点,CPU会跳转到该程序并执行。
中断处理程序通常被称为中断服务例程(Interrupt Service Routine,ISR)。
ISR负责处理中断事件,如读取设备数据、更新系统状态等。
4. 中断上下文的保存与恢复在执行ISR之前,CPU需要保存当前执行上下文(包括寄存器状态、程序计数器等),以便在中断处理完成后能够恢复到原来的执行状态。
同样,当中断处理完成时,CPU需要恢复被中断程序的执行上下文。
5. 嵌套中断的处理在某些情况下,一个中断处理程序可能会被另一个更高优先级的中断打断。
这种情况称为嵌套中断。
Linux内核通过中断优先级管理和中断屏蔽等技术来处理嵌套中断,确保系统的稳定性和实时性。
6. 中断结束与返回当中断处理程序执行完毕时,它会通过一条特殊的指令(如iret在x86架构中)来通知CPU中断处理已完成。
CPU随后会恢复被中断程序的执行上下文,并继续执行被中断的程序。
三、软件中断(软中断)和任务调度除了硬件中断外,Linux内核还支持软件中断(Softirqs)和任务调度(Tasklets)机制。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.Linux中断的注册与释放:
在<linux/interrupt.h>, , 实现中断注册接口:
int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long flags,
const char *dev_name,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
函数参数说明
unsigned int irq:所要注册的中断号
irqreturn_t (*handler)(int, void *, struct pt_regs *):中断服务程序的入口地址。
unsigned long flags:与中断管理有关的位掩码选项,有三组值:
1. SA_INTERRUPT :快速中断处理程序,当使用它的是后处理器上所有的其他中断都被禁用。
2. SA_SHIRQ :该中断是在设备之间可共享的
3. SA_SAMPLE_RANDOM :这个位表示产生的中断能够有贡献给 /dev/random
和 /dev/urandom 使用的加密池.(此处不理解)
const char *dev_name:设备描述,表示那一个设备在使用这个中断。
void *dev_id:用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有数据区(来标识哪个设备在中断) 。
这个参数在真正的驱动程序中一般是指向设备数据结构的指针.在调用中断处理程序的时候它就会传递给中断处理程序的void *dev_id。
(这是我的理解)如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向设备结构不管如何是个好主意. 我们将在"实现一个处理"一节中看到dev_id 的一个实际应用。
中断号的查看可以使用下面的命令:“cat /proc/interrupts”。
/proc/stat 记录了几个关于系统活动的低级统计量, 包括(但是不限于)自系统启动以来收到的中断数. stat 的每一行以一个文本字串开始, 是该行的关键词; intr 标志是我们在找的.
第一个数是所有中断的总数, 而其他每一个代表一个单个 IRQ 线, 从中断 0 开始. 所有的计数跨系统中所有处理器而汇总的. 这个快照显示, 中断号 4 已使用 1 次, 尽管当前没有安装处理. 如果你在测试的驱动请求并释放中断在每个打开和关闭循环, 你可能发现/proc/stat 比 /proc/interrupts 更加有用.
以下是一个统计中断时间间隔的中断服务程序。
irqreturn_t short_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
static long mytime=0;
static int i=0;
struct net_device *dev=(struct net_device *)dev_id;
if(i==0){
mytime=jiffies;
}else
if(i<20){
mytime =jiffies- mytime;
printk("Request on IRQ %d time %d\n",irq , mytime);
mytime=jiffies;
printk("Interrupt on %s -----%d \n",dev->name,dev->irq);
}
i ;
return IRQ_HANDLED;
}
这个函数实现的只是对两次发生中断的时间间隔的统计,时间单位是毫秒
前言
在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入来了解内核中断的执行过程
一.内核中断程序:
我们还是来看一看成程序:
在看程序之前,要熟悉如何进行模块编程,和了解module_pararm()的用法。
如果不熟悉的话请大家看,module_param()的学习和Linux内核模块编程,在此不作解释。
1.程序interrupt.c
1 /*
2 *file name :interrupt.c
3 *atuthor : john
4 */
5 #include<linux/init.h>
6 #include<linux/module.h>
7 #include<linux/kernel.h>
8 #include<linux/interrupt.h>
9
10 MODULE_LICENSE("GPL");
11 static int irq;
12 char *interface;
13 static irqreturn_t myirq_handler(int irq,void *dev);
14
15 static int __init myirq_init(void)
16 {
17 printk("the module is working!\n");
18 printk("the irq is ready for working!\n");
19 if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){
20 printk(KERN_ERR "%s interrrupt can't register %d IRQ \n",interface,irq);
21 return -EIO;
22 }
23 printk("%s request %d IRQ\n",interface,irq);
24 return 0;
25 }
26 static irqreturn_t myirq_handler(int irq,void *dev)
27 {
28 printk("%d IRQ is working\n",irq);
29 return IRQ_NONE;
30 }
31 static void __exit myirq_exit(void)
32 {
33 printk("the module is leaving!\n");
34 printk("the irq is bye bye!\n");
35 free_irq(irq,&irq);
36 printk("%s interrupt free %d IRQ\n",interface,irq);
37
38 }
39 module_init(myirq_init);
0 module_exit(myirq_exit);
41 module_param(interface,charp,0644);
42 module_param(irq,int,0644);
43
1 /*
2 *file name :interrupt.c
3 *atuthor : john
4 */
5 #include<linux/init.h>
6 #include<linux/module.h>
7 #include<linux/kernel.h>
8 #include<linux/interrupt.h>
9
10 MODULE_LICENSE("GPL");
11 static int irq;
12 char *interface;
13 static irqreturn_t myirq_handler(int irq,void *dev);
14
15 static int __init myirq_init(void)
16 {
17 printk("the module is working!\n");
18 printk("the irq is ready for working!\n");
19 if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){
20 printk(KERN_ERR "%s interrrupt can't register %d IRQ \n",interface,irq);
21 return -EIO;
22 }
23 printk("%s request %d IRQ\n",interface,irq);
24 return 0;
25 }
26 static irqreturn_t myirq_handler(int irq,void *dev)
27 {
28 printk("%d IRQ is working\n",irq);
29 return IRQ_NONE;。