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终端中进行网络连接和断开的步骤。
一、网络连接要在Linux终端中建立网络连接,可以使用以下命令:1. ifconfig:此命令用于显示和配置网络接口。
使用ifconfig命令可以查看系统中所有网络接口的状态。
例如,输入ifconfig可以显示当前网络接口的IP地址、MAC地址、子网掩码等信息。
2. ip命令:这是一个更高级的命令,用于在Linux系统中配置网络接口。
要查看网络接口的状态,请输入ip addr show命令。
要启用或禁用网络接口,请使用ip link set dev <interface> up或ip link set dev<interface> down命令。
其中,<interface>是网络接口的名称,如eth0或wlan0。
3. dhclient命令:此命令用于在Linux中获取动态主机配置协议(DHCP)的IP地址。
例如,输入sudo dhclient可以在现有网络接口上获取DHCP IP地址。
4. nmcli命令:这是NetworkManager的命令行接口,用于管理网络连接。
要查看可用的网络连接,请输入nmcli c show命令。
要启用或禁用网络连接,请使用nmcli c up或nmcli c down命令。
要连接到特定的网络连接,请输入nmcli c up <connection name>命令。
其中,<connection name>是网络连接的名称。
二、网络断开在Linux终端中断开网络连接的方法如下:1. ifconfig命令:要禁用(断开)网络接口,请输入ifconfig<interface> down命令。
其中,<interface>是网络接口的名称,如eth0或wlan0。
Linux内核中ARM中断实现详解request_irq()、free_irq()
Linux内核中ARM中断实现详解request_irq()、free_irq()Request_irq()调用的定义:int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);irq 是要申请的硬件中断号。
具体应是何值参见博文blog.csdn/songqqnew/article/details/6791602。
handler 是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id 参数将被传递给它。
即是中断服务子程序,用staticirqreturn_tadc_interrupt(intirq,void*dev_id);定义或声明。
Irqflags 是中断处理的一些属性。
若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon 已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。
(这几个flag 是可以通过或的方式同时使用的)devname 设置中断名称,在cat /proc/interrupts 中可以看到此名称。
为注册的驱动程序的设备名。
dev_id 在中断共享时会用到。
一般设置为这个设备的device 结构本身或者NULL。
中断处理程序可以用dev_id 找到相应的控制这个中断的设备,或者用irq2dev_map 找到中断对应的设备。
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中断功能
1).简介
GPIO应用是嵌入式设备最基本的应用之一,本文就基于EmbeddedLinux
系统演示开发GPIO中断以及输出相关的基本应用示例.
本文所采用的硬件平台来自与Toradex发布的基于NXPiMX7SoC的ColibriiMX7ARM计算机模块配合ColibriEvaBoard.
2).准备
a).ToradexColibriiMX7S(基于NXPiMX7SSoC)计算机模块配合ColibriEvaBoard开发载板.
b).EmbeddedLinux使用Toradex官方发布的LinuxreleaseV2.6.1,更新方法请见这里.
3).软硬件安装
a).本文所实现的GPIO应用原理为使用两个GPIO接口,一个作为按键输
入使用,另外一个作为输出驱动载板上面的LED.每次按键后,会将LED状态翻转,也就是点亮和熄灭交替.
b).硬件连接,将ColibriEva载板X3连接器C19和X21连接器SW6连
接,作为按键输入端;将X3连接器A19和X21连接器LED1连接,用于驱动
LED1.
c).在Ubuntu14.04开发主机配置开发环境,这里使用Eclipse作为开发IDE,具体配置可以参考这里的Linux开发上手指南.
4).GPIO应用示例
a).运行Eclipse,创建一个新项目,命名”gpiointtest”,配置为”EmptyProject”
和“CrossGCC”.。
x86 linux内核中断处理流程
x86 linux内核中断处理流程下载温馨提示:该文档是我店铺精心编制而成,希望大家下载以后,能够帮助大家解决实际的问题。
文档下载后可定制随意修改,请根据实际需要进行相应的调整和使用,谢谢!并且,本店铺为大家提供各种各样类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,如想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by theeditor.I hope that after you download them,they can help yousolve practical problems. The document can be customized andmodified after downloading,please adjust and use it according toactual needs, thank you!In addition, our shop provides you with various types ofpractical materials,such as educational essays, diaryappreciation,sentence excerpts,ancient poems,classic articles,topic composition,work summary,word parsing,copy excerpts,other materials and so on,want to know different data formats andwriting methods,please pay attention!深入解析x86 Linux内核中断处理机制在计算机系统中,中断扮演着至关重要的角色,它使得硬件事件能够及时通知操作系统进行相应的处理。
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会将当前任务的上下文保存到内存中,以便后续恢复任务的执行。
③ 中断处理:中断服务程序会根据中断类型进行相应的处理,如读取键盘输入、发送网络数据等。
在处理过程中,中断服务程序可以访问进程内存空间、内核内存空间等,并可以与其他设备进行交互。
linux gpio中断应用实例
linux gpio中断应用实例Linux GPIO(General Purpose Input/Output,通用输入输出)中断是一种用于处理外部设备事件的方式。
当外部设备产生一个事件时,它会向CPU发送一个信号,这个信号被称为中断。
CPU在接收到中断信号后,会暂停当前的任务,转而去处理这个中断事件。
处理完中断事件后,CPU会返回到被暂停的任务继续执行。
这种机制使得CPU 可以高效地处理多个任务,而不会被某个任务阻塞。
在Linux系统中,GPIO中断主要应用于以下场景:1. 按键事件处理:当用户按下或松开一个按键时,会产生一个中断事件。
通过配置GPIO中断,可以实现对按键事件的实时响应。
2. 传感器数据采集:许多传感器设备(如温度传感器、湿度传感器等)会周期性地产生数据。
通过配置GPIO中断,可以实现对这些数据的实时采集和处理。
3. 马达控制:通过配置GPIO中断,可以实现对马达的启动、停止和速度控制。
下面是一个使用Linux GPIO中断的简单实例:实现一个LED灯的闪烁控制。
首先,我们需要配置GPIO中断。
在这个例子中,我们将使用GPIO 4作为中断引脚,连接到一个按钮开关。
当按下按钮时,LED灯会闪烁。
1. 配置GPIO 4为输入模式:bashecho "4" > /sys/class/gpio/exportecho "in" > /sys/class/gpio/gpio4/direction2. 配置GPIO中断:bashecho "4" > /sys/class/gpio/gpio4/irqecho "1000" > /sys/class/gpio/gpio4/edgeecho "1" > /sys/class/gpio/gpio4/debounce这里,我们设置了GPIO 4的中断触发方式为上升沿触发(edge),并设置了去抖动时间(debounce)。
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中断处理之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()来防止软中断嵌套,和抢占硬中断环境。
计算机原理软硬中断实例
计算机原理软硬中断实例
软中断和硬中断是计算机系统中处理外部事件的两种不同方式。
软中断是由CPU执行的特殊指令,用于请求操作系统执行特定
的任务或服务。
例如,在Linux系统中,软中断可以由用户空间程
序通过系统调用触发,以请求操作系统执行特定的内核功能。
另一
个例子是在网络编程中,当数据包到达时,操作系统会生成软中断
来处理网络数据包。
硬中断是由计算机硬件设备(如网卡、键盘、定时器等)发送
给CPU的信号,用于通知CPU发生了特定的事件。
例如,当键盘被
按下时,键盘控制器会发送一个硬中断给CPU,以通知操作系统有
按键事件发生。
一个常见的软中断和硬中断的实例是计时器中断。
硬件定时器
可以定期发送硬中断给CPU,以便操作系统可以执行定时任务,比
如更新系统时间或执行周期性的操作。
另一方面,操作系统也可以
设置一个软定时器,当定时器时间到达时,CPU会执行相应的软中
断处理程序。
总的来说,软中断和硬中断都是计算机系统中用于处理外部事件的重要机制,它们在操作系统和硬件之间起着桥梁的作用,确保系统能够及时、有效地响应外部事件。
关于Linux0.00中的时钟中断代码的一点理解
关于Linux0.00中的时钟中断代码的⼀点理解在读Linux0.00 head.s的代码时,时钟中断这段代码折腾了我半天才弄懂,先贴上代码1 /*2 * head.s contains the 32-bit startup code.3 * Two L3 task multitasking. The code of tasks are in kernel area,4 * just like the Linux. The kernel code is located at 0x10000.5 */6 KRN_BASE = 0x100007 TSS0_SEL = 0x208 LDT0_SEL = 0x289 TSS1_SEL = 0X3010 LDT1_SEL = 0x381112 .text13startup_32:14 movl $0x10,%eax15mov %ax,%ds16mov %ax,%es17mov %ax,%fs18mov %ax,%gs19lss stack_ptr,%esp2021 # setup base fields of descriptors.22 movl $KRN_BASE, %ebx23 movl $gdt, %ecx24lea tss0, %eax25 movl $TSS0_SEL, %edi26call set_base27lea ldt0, %eax28 movl $LDT0_SEL, %edi29call set_base30lea tss1, %eax31 movl $TSS1_SEL, %edi32call set_base33lea ldt1, %eax34 movl $LDT1_SEL, %edi35call set_base3637call setup_idt38call setup_gdt39 movl $0x10,%eax # reload all the segment registers40mov %ax,%ds # after changing gdt.41mov %ax,%es42mov %ax,%fs43mov %ax,%gs44lss stack_ptr,%esp4546 # setup up timer 8253 chip.47 movb $0x36, %al48 movl $0x43, %edx49 outb %al, %dx50 movl $11930, %eax # timer frequency 100 HZ51 movl $0x40, %edx52 outb %al, %dx53 movb %ah, %al54 outb %al, %dx5556 # setup timer & system call interrupt descriptors.57 movl $0x00080000, %eax58 movw $timer_interrupt, %ax59 movw $0x8E00, %dx60 movl $0x20, %ecx61lea idt(,%ecx,8), %esi62 movl %eax,(%esi)63 movl %edx,4(%esi)64 movw $system_interrupt, %ax65 movw $0xef00, %dx66 movl $0x80, %ecx67lea idt(,%ecx,8), %esi68 movl %eax,(%esi)69 movl %edx,4(%esi)7071 # unmask the timer interrupt.72 movl $0x21, %edx73 inb %dx, %al74 andb $0xfe, %al75 outb %al, %dx77 # Move to user mode (task 0)78 pushfl79 andl $0xffffbfff, (%esp)80 popfl81 movl $TSS0_SEL, %eax82ltr %ax83 movl $LDT0_SEL, %eax84lldt %ax85 movl $0, current86sti87 pushl $0x1788 pushl $stack0_ptr89 pushfl /* ⼿动设置返回环境*/90 pushl $0x0f /**/91 pushl $task0 /**/92iret9394 /****************************************/95setup_gdt:96lgdt lgdt_opcode97ret9899setup_idt:100lea ignore_int,%edx101 movl $0x00080000,%eax102 movw %dx,%ax /* selector = 0x0008 = cs */103 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 104lea idt,%edi105mov $256,%ecx106rp_sidt:107 movl %eax,(%edi)108 movl %edx,4(%edi)109 addl $8,%edi110dec %ecx111jne rp_sidt112lidt lidt_opcode113ret114115 # in: %eax - logic addr; %ebx = base addr ;116 # %ecx - table addr; %edi - descriptors offset.117set_base:118 addl %ebx, %eax119 addl %ecx, %edi120 movw %ax, 2(%edi)121 rorl $16, %eax122 movb %al, 4(%edi)123 movb %ah, 7(%edi)124 rorl $16, %eax125ret126127write_char:128push %gs129 pushl %ebx130 pushl %eax131mov $0x18, %ebx132mov %bx, %gs133 movl scr_loc, %bx134shl $1, %ebx135 movb %al, %gs:(%ebx)136shr $1, %ebx137 incl %ebx138 cmpl $2000, %ebx139jb 1f140 movl $0, %ebx1411: movl %ebx, scr_loc142 popl %eax143 popl %ebx144pop %gs145ret146147 /***********************************************/148 /* This is the default interrupt "handler" :-) */149 .align 2150ignore_int:151push %ds152 pushl %eax153 movl $0x10, %eax154mov %ax, %ds155 movl $67, %eax /* print 'C' */156call write_char157 popl %eax158pop %ds159iret161 /* Timer interrupt handler */162 .align 2163timer_interrupt:164push %ds165 pushl %edx166 pushl %ecx167 pushl %ebx168 pushl %eax169 movl $0x10, %eax170mov %ax, %ds171 movb $0x20, %al172 outb %al, $0x20173 movl $1, %eax174 cmpl %eax, current175je 1f176 movl %eax, current177 ljmp $TSS1_SEL, $0178jmp 2f1791: movl $0, current180 ljmp $TSS0_SEL, $01812: popl %eax182 popl %ebx183 popl %ecx184 popl %edx185pop %ds186iret187188 /* system call handler */189 .align 2190system_interrupt:191push %ds192 pushl %edx193 pushl %ecx194 pushl %ebx195 pushl %eax196 movl $0x10, %edx197mov %dx, %ds198call write_char199 popl %eax200 popl %ebx201 popl %ecx202 popl %edx203pop %ds204iret205206 /*********************************************/207current:.long 0208scr_loc:.long 0209210 .align 2211 .word 0212lidt_opcode:213 .word 256*8-1 # idt contains 256 entries214 .long idt + KRN_BASE # This will be rewrite by code.215 .align 2216 .word 0217lgdt_opcode:218 .word (end_gdt-gdt)-1 # so does gdt219 .long gdt + KRN_BASE # This will be rewrite by code.220221 .align 3222idt: .fill 256,8,0 # idt is uninitialized223224gdt: .quad 0x0000000000000000 /* NULL descriptor */225 .quad 0x00c09a01000007ff /* 8Mb 0x08, base = 0x10000 */ 226 .quad 0x00c09201000007ff /* 8Mb 0x10 */227 .quad 0x00c0920b80000002 /* screen 0x18 - for display */ 228229 .quad 0x0000e90100000068 # TSS0 descr 0x20230 .quad 0x0000e20100000040 # LDT0 descr 0x28231 .quad 0x0000e90100000068 # TSS1 descr 0x30232 .quad 0x0000e20100000040 # LDT1 descr 0x38233end_gdt:234 .fill 128,4,0235stack_ptr:236 .long stack_ptr237 .word 0x10238239 /*************************************/240 .align 3241ldt0: .quad 0x0000000000000000242 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000243 .quad 0x00c0f201000003ff # 0x17245 .long 0 /* back link */246 .long stack0_krn_ptr, 0x10 /* esp0, ss0 */247 .long 0, 0 /* esp1, ss1 */248 .long 0, 0 /* esp2, ss2 */249 .long 0 /* cr3 */250 .long 0 /* eip */251 .long 0 /* eflags */252 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */253 .long 0, 0, 0, 0 /* esp, ebp, esi, edi */254 .long 0,0,0,0,0,0 /* es, cs, ss, ds, fs, gs */255 .long LDT0_SEL /* ldt */256 .long 0x8000000 /* trace bitmap */257258 .fill 128,4,0259stack0_krn_ptr:260 .long 0261262 /************************************/263 .align 3264ldt1: .quad 0x0000000000000000265 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000266 .quad 0x00c0f201000003ff # 0x17267tss1:268 .long 0 /* back link */269 .long stack1_krn_ptr, 0x10 /* esp0, ss0 */270 .long 0, 0 /* esp1, ss1 */271 .long 0, 0 /* esp2, ss2 */272 .long 0 /* cr3 */273 .long task1 /* eip */274 .long 0x200 /* eflags */275 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */276 .long stack1_ptr, 0, 0, 0 /* esp, ebp, esi, edi */277 .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */278 .long LDT1_SEL /* ldt */279 .long 0x8000000 /* trace bitmap */280281 .fill 128,4,0282stack1_krn_ptr:283 .long 0284285 /************************************/286task0:287 movl $0x17, %eax288 movw %ax, %ds289 movl $65, %al /* print 'A' */290int $0x80291 movl $0xfff, %ecx2921: loop 1b293jmp task0294295 .fill 128,4,0296stack0_ptr:297 .long 0298299task1:300 movl $0x17, %eax301 movw %ax, %ds302 movl $66, %al /* print 'B' */303int $0x80304 movl $0xfff, %ecx3051: loop 1b306jmp task1307308 .fill 128,4,0309stack1_ptr:310 .long 0311312 /*** end ***/question:在代码的第177⾏(177 ljmp $TSS1_SEL, $0)和第180⾏(180 ljmp $TSS0_SEL, $0)可以看到,中断程序转移到对应的任务去执⾏了,后边的代码怎么执⾏?iret永远也执⾏不到了么?answer:实际上,程序在开始运⾏时,从第79⾏(79 andl $0xffffbfff, (%esp))可以看到标志寄存器的中断嵌套位NT被设置成了0,也就是说中断返回了esp0,ss0,ldt,trace bitmap设置了外,其余是0.嵌套任务标志NT(Nested Task)嵌套任务标志NT⽤来控制中断返回指令IRET的执⾏。
linux共享中断处理程序,一种Linux下共享中断的处理方法
linux共享中断处理程序,⼀种Linux下共享中断的处理⽅法前段时间调试⼀款芯⽚的时候,碰到⼀个奇怪的问题:只要在板卡上插⼊⼀个PS2键盘,启动内核时系统就可能会进⼊串⼝中断函数去执⾏,过⼀会系统就panic不往下继续执⾏。
后来经过分析出现问题时的panic的堆栈,借助EJTAG⼯具,读到这个时候的串⼝的中断状态位,竟然发现串⼝并没有真正产⽣中断。
那么,串⼝本⾝没有中断,内核怎么⼜会跑到串⼝的中断服务函数去执⾏呢?我们知道Linux的中断可以分为I/O 中断 、时钟中断和处理器核间中断。
其中I/O中断是Linux 系统响应外部IO事件的重要⽅式。
尽管不同的平台和体系结构实现的⽅法不⼀样,但是都⽀持不同的设备共享同⼀个中断向量 。
例如在PCI的总线结构中,⼏个设备可以共享同⼀个IRQ线,也即它们共享⼀个中断号,各⾃的中断服务例程挂在这个中断号上。
当CPU响应这个IRQ时,会逐⼀检查挂在这个中断向量上的例程,并且执⾏那些真正有中断的例程。
但是串⼝明显不属于PCI,不可能⽤到PCI的中断线和中断引脚寄存器,那⼜是什么原因导致kernel panic呢?带着上⾯的这些问题,和硬件⼯程师和芯⽚设计的同事深⼊讨论了各种可能性,最后结合芯⽚板卡原理图,发现这个板⼦上的低速设备都集成在芯⽚内部,这些低速设备包括串⼝、LPC只是控制器、SPI控制、I2C控制器、NAND控制器等,其中CPU UART和LPC的中断信号都路由到同⼀个CPU的中断引脚。
具体的连接图如下所显:通过上图最左侧的部分可以看到,CPU UART和CPU LPC控制器的中断请求信号经过逻辑或后,输出到了CPU 芯⽚内Interrupt Pending Bit2。
⼀旦CPU检测到任何⼀个Interrupt Pending位被设置起来,它就会根据约定的顺序逐⼀查询各个Interrupt Pending Bit,并且执⾏中断路由到这个bit上的设备驱动的中断服务例程。
中断的概念及案例
中断的概念及案例一、概念中断是指在计算机运行过程中,当发生某种特定事件时,由硬件或软件强制暂停正在执行的程序,转而去执行另一个程序或服务的过程。
它是计算机系统中非常重要的一种机制,可以使得系统在处理多个任务时更加高效和灵活。
二、硬件中断硬件中断是由计算机硬件控制器发出的一种信号,用于通知CPU当前有某种事件需要处理。
比如,当用户按下键盘上的某个键时,键盘控制器就会向CPU发送一个中断信号,告诉它有一个键被按下了。
CPU 接收到信号后会立即停止正在执行的程序,并跳转到相应的中断处理程序中去执行。
三、软件中断软件中断也叫做系统调用(system call),是由操作系统内核提供给用户程序使用的一种接口。
通过调用这些接口函数,用户程序可以请求操作系统内核帮助它完成某些任务。
比如,在Linux系统中,用户可以通过调用open()函数来打开一个文件,在read()函数来读取文件内容,在write()函数来写入文件内容等等。
当用户程序调用这些函数时,操作系统内核会自动进行相应的处理,并返回结果给用户程序。
四、案例分析1. 硬件中断案例假设我们正在玩一款游戏,此时突然有人拔掉了电源线,导致电脑突然关机。
这时候,电脑的硬件会检测到电源被拔掉的事件,并向CPU发送一个中断信号。
CPU接收到信号后会立即停止正在执行的程序,并跳转到相应的中断处理程序中去执行。
在这个中断处理程序中,操作系统会对当前的状态进行保存,并关闭所有正在运行的进程。
然后,操作系统会将系统恢复到安全状态,并提示用户重新启动电脑。
2. 软件中断案例假设我们想要在Linux系统中创建一个新文件。
为了实现这个目标,我们可以使用open()函数来打开一个文件描述符,并使用write()函数来写入文件内容。
当我们调用open()函数时,操作系统内核会自动进行相应的处理,并返回一个文件描述符给我们。
当我们调用write()函数时,操作系统内核也会自动进行相应的处理,并将数据写入到指定的文件中。
【原创】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下串口编程中断
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 却会被丢掉。
不用说也知道,这几个肯定是特殊字符,被用作特殊控制了。
linux gpio key中断触发方式
linux gpio key中断触发方式题目:Linux GPIO Key中断触发方式解析与应用引言:在嵌入式系统和物联网应用中,我们经常需要与外部设备进行交互。
GPIO (General Purpose Input Output)被广泛使用以实现对外设的控制和监测。
其中,利用中断触发方式读取GPIO输入状态,可以大大降低资源占用和响应时间。
本文将详细介绍Linux中GPIO Key的中断触发方式,以及如何在应用中应用和配置。
一、GPIO Key的基本概念GPIO Key是指将GPIO引脚配置为按键输入,并通过中断方式来检测按键的状态变化。
与轮询方式相比,中断触发可以实现即时响应和降低系统资源占用。
在Linux系统中,GPIO Key由内核的input子系统和keyboard 子系统来处理。
二、中断触发模式在Linux中,GPIO Key的中断触发模式主要有下列几种:1. 无触发:GPIO按键输入不产生中断,只能通过轮询方式读取输入状态。
2. 下降沿触发:只有在GPIO由高电平变为低电平时才会产生中断。
3. 上升沿触发:只有在GPIO由低电平变为高电平时才会产生中断。
4. 边沿触发:GPIO在电平转变时都会产生中断,包括上升沿和下降沿。
5. 按键触发:GPIO按键按下和释放各自产生中断。
三、配置GPIO Key中断触发方式1. 准备工作:(1) 确认GPIO管脚号和对应的GPIO控制器。
(2) 确认使用的GPIO按键的中断触发方式。
(3) 确认所使用的开发板或单板计算机上是否已经加载了GPIO对应的驱动。
2. 创建GPIO Key设备:(1) 打开设备树文件,找到GPIO节点(一般位于/arch/arm/boot/dts/目录下)。
(2) 在GPIO节点中添加GPIO Key设备信息,包括GPIO管脚号、中断触发方式等。
(3) 编译设备树文件,生成设备树二进制文件(.dtb文件)。
(4) 将生成的设备树二进制文件拷贝到/boot目录下,并在引导过程中加载。
Linux0.01键盘中断的C语言实现
—
2 键 盘中断设 计与实现
由于 c语言程序无法直接实现诸 如 ps 、o 、 uh pp
it r 等汇 编语 言指令 , 以键 盘 中断 的主 框架仍 用 汇 e 所
编语 言 实现 , 将其 键 盘处理 功 能用 C语 言 编程实 现 .
2 1 A CI . S I 码映射 表
Vo . 2 № 8 13
Au . 2 1 g 01
Ln x0 O iu . 1键 盘 中 断的 C语 言 实现
于 江 涛 曲 波 ,
(. 1 通化 师范学 院 计算机科学系 , 吉林 通化 14 0 ;. 3 0 2 2 南京 晓庄学院 数学与信息技术 学院, 江苏 南京 2 17 ) 11 1
以适 当改 造 , 在 当前 Ln x平 台编 译 运行 , 常 可于按 键 的 扫 描码 , 标 变 量 下
对应 于 A CI . S I码 例如 按下 “ S ” , E C 键 该键 的扫描 码
为 “ ” 其对应 的 A CI 1, S I码为 “7 . 2” 由于 Lns iu 是芬 兰人 , 以 当初 Ln x .1的键 所 iu O 0 盘 映射是按 芬 兰键 盘 设计 的. 笔者 在 本程 序 中使 用 通用 的美 国键盘 映射 . Si 组 合键 及 At 合 键 的 映 射 表 与 此类 似 , hf t l组
摘
要 : 阐述了用 c语 言改写 L u . 1 文章 i x 0 键盘模块的技术要 点 , n 0 包括 A CI S I码映射表 、 盘处理子程 序跳转表 、 盘处理 键 键
程序、 键盘 中断程序 的编程 方法及 关键代码. 关键词 :iu . 1键 盘 中断 ; Ln x 0 ; 0 C语言编程 中图分类号 :P 1 文献标志码 : 文章编号 :0 8— 9 4 2 1 )8— 0 4—0 T 36 A 10 7 7 (0 10 0 1 3
Linux实现时钟中断的全过程
Linux实现时钟中断的全过程1.可编程定时/计数器的初始化IBM PC中使用的是8253或8254芯片。
有关该芯片的详细知识我们不再详述,只大体介绍以下它的组成和作用,如下表5.1所示:表5.1 8253/8254的组成及作用计数器0的输出就是图5.3中的Out0,它的频率由操作系统的设计者确定,Linux对8253的初始化程序段如下(在/arch/i386/kernel/i8259.c的init_IRQ ()函数中):set_intr_gate(ox20, interrupt[0]);/*在IDT的第0x20个表项中插入一个中断门。
这个门中的段选择符设置成内核代码段的选择符,偏移域设置成0号中断处理程序的入口地址。
*/outb_p(0x34,0x43); /* 写计数器0的控制字:工作方式2*/outb_p(LATCH & 0xff , 0x40); /* 写计数初值LSB 计数初值低位字节*/outb(LATCH >> 8 , 0x40); /* 写计数初值MSB 计数初值高位字节*/LATCH(英文意思为:锁存器,即其中锁存了计数器0的初值)为计数器0的计数初值,在/include/linux/timex.h中定义如下:#define CLOCK_TICK_RATE 1193180 /* 图5.3中的输入脉冲 */#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* 计数器0的计数初值 */CLOCK_TICK_RATE是整个8253的输入脉冲,如图5.3中所示为1.193180MHz,是近似为1MHz的方波信号,8253内部的三个计数器都对这个时钟进行计数,进而产生不同的输出信号,用于不同的用途。
HZ表示计数器0的频率,也就是时钟中断或系统时钟的频率,在/include/asm/param.h中定义如下:#define HZ 1002.与时钟中断相关的函数下面我们看时钟中断触发的服务程序,该程序代码比较复杂,分布在不同的源文件中,主要包括如下函数:时钟中断程序:timer_interrupt( );中断服务通用例程do_timer_interrupt();时钟函数:do_timer( );中断安装程序:setup_irq( );中断返回函数:ret_from_intr( );前三个函数的调用关系如下:timer_interrupt( )do_timer_interrupt()do_timer( )(1) timer_interrupt( )这个函数大约每10ms被调用一次,实际上, timer_interrupt( )函数是一个封装例程,它真正做的事情并不多,但是,作为一个中断程序,它必须在关中断的情况下执行。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
你的第一个中断程序:本文通过一个简单的中断程序来描述一般中断程序的基本框架。
完整代码在这里。
中断程序一般会包含在某个设备的驱动程序中,因此,接下来的程序本质上还是一个内核模块。
说到内核模块,你应该知道首先去看什么了吧?对了,就是内核模块加载函数。
view sourceprint?01 static int __init myirq_init()02 {03 printk("Module is working..\n");04 if(request_irq(irq,myirq_handler,IRQF_SHARED,devname, &mydev)!=0)05 {06 printk("%s request IRQ:%dfailed..\n",devname,irq);07 return -1;08 }09 printk("%s rquest IRQ:%d success..\n",devname,irq);10 return 0;11 }在内核加载函数中,我们除了显示一些信息外,最重要的工作就是申请一根中断请求线,也就是注册中断处理程序。
很明显,这一动作是通过 request_irq函数来完成的。
这个函数的原型如下:view sourceprint?1 static int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);第一个参数是中断号,这个中断号对应的就是中断控制器上IRQ线的编号。
第二个参数是一个irq_handler_t类型个函数指针:view sourceprint?1 typedef irqreturn_t (*irq_handler_t)(int, void *);handler所指向的函数即为中断处理程序,需要具体来实现。
第三个参数为标志位,可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。
在本实例程序中取 IRQF_SHARED,该标志表示多个设备共享一条IRQ线,因此相应的每个设备都需要各自的中断服务例程。
一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。
取IRQF_SAMPLE_RANDOM则表示设备可以被看做是事件随见的发生源。
第四个参数是请求中断的设备的名称。
可以在/proc/interface中查看到具体设备的名称,与此同时也可以查看到这个设备对应的中断号以及请求次数,甚至中断控制器的名称。
第五个参数为一个指针型变量。
注意此参数为void型,也就是说通过强制转换可以转换为任意类型。
这个变量在IRQF_SHARED标志时使用,目的是为即将要释放中断处理程序提供唯一标志。
因为多个设备共享一条中断线,因此要释放某个中断处理程序时,必须通过此标志来唯一指定这个中断处理程序。
习惯上,会给这个参数传递一个与设备驱动程序对应的设备结构体指针。
关于中断程序,可参考这里的文章。
以上就是request_irq函数各个参数的意义。
与中断处理程序的注册相对应的是free_irq函数,它会注销相应的中断处理程序,并释放中断线。
这个函数一般被在内核模块卸载函数中被调用。
view sourceprint?1 static void __exit myirq_exit()2 {3 printk("Module is leaving..\n");4 free_irq(irq,&mydev);5 printk("%s request IRQ:%d success..\n",devname,irq);6 }如果该中断线不是共享的,那么该函数在释放中断处理程序的同时也将禁用此条中断线。
如果是共享中断线,只是释放与mydev对应的中断处理程序。
除非该中断处理程序恰好为该中断线上的最后一员,此条中断线才会被禁用。
在此处,你也可以感受到mydev的重要性。
下面具体分析中断处理函数。
该函数的功能很简单,只是显示一些提示信息。
view sourceprint?01 static irqreturn_t myirq_handler(int irq,void* dev)02 {03 struct myirq mydev;04 static int count=1;05 mydev=*(struct myirq*)dev;06 printk("key: %d..\n",count);07 printk("devid:%d ISR is working..\n",mydev.devid);08 printk("ISR is leaving..\n");09 count++;10 return IRQ_HANDLED;11 }另外,本内核模块在插入时还需要附带参数,下面的语句首先定义两个参数,然后利用宏module_param宏来接受参数。
view sourceprint?1 static int irq;2 static char* devname;34 module_param(devname,charp,0644);5 module_param(irq,int,0644);使用方法:1.通过cat /proc/interrupts查看中断号,以确定一个即将要共享的中断号。
本程序因为是与键盘共享1号中断线,因此irq=1;2.使用如下命令就可以插入内核:sudo insmod filename.ko irq=1 devname=myirq3.再次查看/proc/interrupts文件,可以发现1号中断线对应的的设备名处多了myirq设备名;4.dmesg查看内核日志文件,可看到在中断处理程序中所显示的信息;5.卸载内核模块;可以看到,内核模块加载后,我们所写中断处理程序是被自动调用的,主要是因为该中断线上有键盘所发出的中断请求,因此内核会执行该中断线上的所有中断处理程序,当然就包括我们上述所写的那个中断处理程序。
关于中断处理程序的执行,可参考这里的文章。
这样,一个最基本的中断程序就编写完成了!try!后记:这个程序调试起来并不难,但是我们并不能仅仅局限在这个程序本身。
以它为入口点深入学习中断的基本原理再好不过。
下面给出几个学习的入口点。
1.为何我们的中断程序和其他设备共享了一个中断线后会被执行?或者说,共享中断线上的所有中断服务例程是怎么执行的?2.中断涉及到那些基本的数据结构?这些数据结构之间有什么关系?3.do_IRQ()函数的大体执行流程是什么?亲们,要学习的东西还很多,让我们一起加油吧!中断下半部-tasklet:tasklet的实现tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简单的。
正如在前文中你所知道的那样,一个使用tasklet的中断程序首先会通过执行中断处理程序来快速完成上半部分的工作,接着通过调用tasklet使得下半部分的工作得以完成。
可以看到,下半部分被上半部分所调用,至于下半部分何时执行则属于内核的工作。
对应到我们此刻所说的tasklet就是,在中断处理程序中,除了完成对中断的响应等工作,还要调用 tasklet,如下图示。
tasklet由tasklet_struct结构体来表示,每一个这样的结构体就表示一个tasklet。
在<linux/interrupt.h>中可以看到如下的定义:view sourceprint?1 tasklet_struct2 {3 struct tasklet_struct *next;4 unsigned long state;5 atomic_t count;6 void (*func)(unsigned long);7 unsigned long data;8 };在这个结构体中,第一个成员代表链表中的下一个tasklet。
第二个变量代表此刻tasklet的状态,一般为 TASKLET_STATE_SCHED,表示此tasklet已被调度且正准备运行;此变量还可取TASKLET_STATE_RUN,表示正在运行,但只用在多处理器的情况下。
count成员是一个引用计数器,只有当其值为0时候,tasklet 才会被激活;否则被禁止,不能被执行。
而接下来的 func变量很明显是一个函数指针,它指向tasklet处理函数,这个处理函数的唯一参数为data。
使用tasklet在使用tasklet前,必须首先创建一个tasklet_struct类型的变量。
通常有两种方法:静态创建和动态创建。
这样官方的说法仍然使我们不能理解这两种创建到底是怎么一回事。
不够透过源码来分析倒是可以搞明白。
在<linux/interrupt.h>中的两个宏:view sourceprint?1 464#define DECLARE_TASKLET(name, func, data) \2 465struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }3 4664 467#define DECLARE_TASKLET_DISABLED(name, func, data) \5 468struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }就是我们进行静态创建tasklet的两种方法。
通过第一个宏创建的tasklet处于激活状态,再通过调度函数被挂起尽而被内核执行;而通过第二个宏创建的tasklet处于禁止状态。
从两个宏的定义可以看到,所谓的静态创建就是直接定义个一个名为name的tasklet_struct类型的变量,并将宏中各个参数相应的赋值给这个name变量的各个成员。
注意,两个宏在功能上差异就在于对name变量count成员的赋值上,具体原因在第一部分已经说明。
也许你对ATOMIC_INIT这样的初始化方式感到疑惑,那么看完定义后,你就会一目了然:view sourceprint?1 //在arch/x86/include/asm/atomic.h中2 15#define ATOMIC_INIT(i) { (i) }3 //在linux/types.h中4 190typedef struct {5 191 int counter;6 192} atomic_t;与静态创建相对的是动态创建,通过给tasklet_init函数传递一个事先定义的指针,来动态创建一个tasklet。