在Linux内核中,各个设备驱动可以简单地调用request_irq()、enable_irq()、disable_irq()、
local_irq_disable()、local_irq_enable()等通用API来完成中断申请、使能、禁止等功能。
local_irq_disable()、local_irq_enable()的实现与具体中断控制器无关,对于ARM v6以上的体系结
构而言,是直接调用CPSID/CPSIE指令进行,而对于ARM v6以前的体系结构,则是通过MRS、MSR指令
来读取和设置ARM的CPSR寄存器。由此可见,local_irq_disable()、local_irq_enable()针对的并不是
外部的中断控制器,而是直接让CPU本身不响应中断请求。相关的实现位于arch/arm/include/asm/irqflags.h
中
1#if __LINUX_ARM_ARCH__ >= 6
2
3static inline unsigned long arch_local_irq_save(void)
4{
5 unsigned long flags;
6
7 asm volatile(
8 " mrs %0, cpsr @ arch_local_irq_save\n"
9 " cpsid i"
10 : "=r" (flags) : : "memory", "cc");
11 return flags;
12}
13
14static inline void arch_local_irq_enable(void)
15{
16 asm volatile(
17 " cpsie i @ arch_local_irq_enable"
18 :
19 :
20 : "memory", "cc");
21}
22
23static inline void arch_local_irq_disable(void)
24{
25 asm volatile(
26 " cpsid i @ arch_local_irq_disable"
27 :
28 :
29 : "memory", "cc");
30}
31#else
32
33/*
34 * Save the current interrupt enable state & disable IRQs
35 */
36static inline unsigned long arch_local_irq_save(void)
37{
38 unsigned long flags, temp;
39
40 asm volatile(
41 " mrs %0, cpsr @ arch_local_irq_save\n"
42 " orr %1, %0, #128\n"
43 " msr cpsr_c, %1"
44 : "=r" (flags), "=r" (temp)
45 :
46 : "memory", "cc");
47 return flags;
48}
49
50/*
51 * Enable IRQs
52 */
53static inline void arch_local_irq_enable(void)
54{
55 unsigned long temp;
56 asm volatile(
57 " mrs %0, cpsr @ arch_local_irq_enable\n"
58 " bic %0, %0, #128\n"
59 " msr cpsr_c, %0"
60 : "=r" (temp)
61 :
62 : "memory", "cc");
63}
64
65/*
66 * Disable IRQs
67 */
68static inline void arch_local_irq_disable(void)
69{
70 unsigned long temp;
71 asm volatile(
72 " mrs %0, cpsr @ arch_local_irq_disable\n"
73 " orr %0, %0, #128\n"
74 " msr cpsr_c, %0"
75 : "=r" (temp)
76 :
77 : "memory", "cc");
78}
79 #endif
与local_irq_disable()和local_irq_enable()不同,disable_irq()、enable_irq()针对的则是中断
控制器,因此它们适用的对象是某个中断。disable_irq()的字面意思是暂时屏蔽掉某中断(其实在内核
的实现层面上做了延后屏蔽),直到enable_irq()后再执行ISR。实际上,屏蔽中断可以发生在外设、中
断控制器、CPU三个位置,如图1所示。对于外设端,是从源头上就不产生中断信号给中断控制器,由
于它高度依赖于外设于本身,所以Linux不提供标准的API而是由外设的驱动直接读写自身的寄存器。

在内核中,通过irq_chip结构体来描述中断控制器。该结构体内部封装了中断mask、unmask、ack等成
员函数,其定义于include/linux/irq.h中,如代码清单4所示。
1struct irq_chip {
2 const char *name;
3 unsigned int (*irq_startup)(struct irq_data *data);
4 void (*irq_shutdown)(struct irq_data *data);
5 void (*irq_enable)(struct irq_data *data);
6 void (*irq_disable)(struct irq_data *data);
7
8 void (*irq_ack)(struct irq_data *data);
9 void (*irq_mask)(struct irq_data *data);
10 void (*irq_mask_ack)(struct irq_data *data);
11 void (*irq_unmask)(struct irq_data *data);
12 void (*irq_eoi)(struct irq_data *data);
13
14 int (*irq_set_affinity)(struct irq_data *data, const struct
cpumask *dest, bool force);
15 int (*irq_retrigger)(struct irq_data *data);
16 int (*irq_set_type)(struct irq_data *data, unsigned int
flow_type);
17 int (*irq_set_wake)(struct irq_data *data, unsigned int on);
18};
各个芯片公司会将芯片内部的中断控制器实现为irq_chip驱动的形式。受限于中断控制器硬件的能
力,这些成员函数并不一定需要全部实现,有时候只需要实现其中的部分函数即可。譬如
drivers/pinctrl/sirf/pinctrl-sirf.c驱动中的下面代码部分:
static struct irq_chip sirfsoc_irq_chip = {
.name = "sirf-gpio-irq",
.irq_ack = sirfsoc_gpio_irq_ack,
.irq_mask = sirfsoc_gpio_irq_mask,
.irq_unmask = sirfsoc_gpio_irq_unmask,
.irq_set_type = sirfsoc_gpio_irq_type,
};
我们只实现了其中的ack、mask、unmask和set_type成员函数,ack函数用于清中断,mask、unmask用
于中断屏蔽和取消中断屏蔽、set_type则用于配置中断的触发方式,如高电平、低电平、上升沿、下降沿
等。至于到enable_irq()的时候,虽然没有实现irq_enable()成员函数,但是内核会间接调用
irq_unmask()成员函数,这点从kernel/irq/chip.c中可以看出:
void irq_enable(struct irq_desc *desc)
{
irq_state_clr_disabled(desc);
if (desc->irq_data.chip->irq_enable)
desc->irq_data.chip->irq_enable(&desc->irq_data);
else
desc->irq_data.chip->irq_unmask(&desc->irq_data);
irq_state_clr_masked(desc);
}
在芯片内部,中断控制器可能不止1个,多个中断控制器之间还很可能是级联的。举个例子,假设芯
片内部有一个中断控制器,支持32个中断源,其中有4个来源于GPIO控制器外围的4组GPIO,每组GPIO
上又有32个中断(许多芯片的GPIO控制器也同时是一个中断控制器),其关系如图4所示。

图4 中断控制器典型分布图
那么,一般来讲,在实际操作中,gpio0_0~gpio0_31这些引脚本身在第1级会使用中断号28,而这些
引脚本身的中断号在实现与GPIO控制器对应的irq_chip驱动时,我们又会把它映射到Linux系统的32~63号
中断。同理,gpio1_0~gpio1_31这些引脚本身在第1级会使用中断号29,而这些引脚本身的中断号在实现
与GPIO控制器对应的irq_chip驱动时,我们又会把它映射到Linux系统的64~95号中断,以此类推。对于中
断号的使用者而言,无须看到这种2级映射关系。如果某设备想申请与gpio1_0这个引脚对应的中断,它只
需要申请64号中断即可。这个关系图看起来如图5所示。

图5 中断级联与映射
要特别注意的是,上述图4和5中所涉及的中断号的数值,无论是base还是具体某个GPIO对应的
中断号是多少,都不一定是如图20.4和图20.5所描述的简单线性映射。Linux使用IRQ Domain来描述一个
中断控制器所管理的中断源。换句话说,每个中断控制器都有自己的Domain。我们可以将IRQ Domain看
作是IRQ控制器的软件抽象。在添加IRQ Domain的时候,内核中存在的映射方法有:
irq_domain_add_legacy()、irq_domain_add_linear()、irq_domain_add_tree()等。
irq_domain_add_legacy()实际上是一种过时的方法,它一般是由IRQ控制器驱动直接指定中断源硬
件意义上的偏移(一般称为hwirq)和Linux逻辑上的中断号的映射关系。类似图20.5的指定映射可以被这
种方法弄出来。irq_domain_add_linear()则在中断源和irq_desc之间建立线性映射,内核针对这个IRQ
Domain维护了一个hwirq和Linux逻辑IRQ之间关系的一个表,这个时候我们其实也完全不关心逻辑中断号
了;irq_domain_add_tree()则更加灵活,逻辑中断号和hwirq之间的映射关系是用一棵radix树来描述的,
我们需要通过查找的方法来寻找hwirq和Linux逻辑IRQ之间的关系,一般适合某中断控制器支持非常多中
断源的情况。
实际上,在当前的内核中,中断号更多的是一个逻辑概念,具体数值是多少不是很关键。人们更多的
是关心在设备树中设置正确的interrupt_parrent和相对该interrupt_parent的偏移。
以drivers/pinctrl/sirf/pinctrl-sirf.c的irq_chip部分为例,在sirfsoc_gpio_probe()函数中,每组GPIO的中
断都通过gpiochip_set_chained_irqchip()级联到上一级中断控制器的中断。
代码清单5 二级GPIO中断级联到一级中断控制器
1static int sirfsoc_gpio_probe(struct device_node *np)
2{
3...
4for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) {
5 bank = &sgpio->sgpio_bank[i];
6 spin_lock_init(&bank->lock);
7 bank->parent_irq = platform_get_irq(pdev, i);
8 if (bank->parent_irq < 0) {
9 err = bank->parent_irq;
10 goto out_banks;
11 }
12
13 gpiochip_set_chained_irqchip(&sgpio->chip.gc,
14 &sirfsoc_irq_chip,
15 bank->parent_irq,
16 sirfsoc_gpio_handle_irq);
17}
18
19...
20}
下面用一个实例来呈现这个过程,假设GPIO0_0~31对应上级中断号28,而外设A使用了GPIO0_5(即
第0组GPIO的第5个),并假定外设A的中断号为37,即32+5,中断服务程序为deva_isr()。那么,当
GPIO0_5中断发生的时候,内核的调用顺序是:sirfsoc_gpio_handle_irq()->generic_handle_irq()-
deva_isr()。如果硬件的中断系统有更深的层次,这种软件上的中断服务程序级联实际上可以有更深的
级别。
在上述实例中,GPIO0_0~31的interrupt_parrent实际是上级中断控制器,而外设A的interrupt_parrent就
是GPIO0,这些都会在设备树中进行呈现。
特别值得一提的是,目前多数主流ARM芯片内部的一级中断控制器都使用了ARM公司的GIC,我们
几乎不需要实现任何代码,只需要在设备树中添加相关的节点。
如在arch/arm/boot/dts/exynos5250.dtsi中即含有:
gic:interrupt-controller@10481000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x10481000 0x1000>, <0x10482000 0x2000>;
};
打开drivers/irqchip/irq-gic.c,发现GIC驱动的入口声明如下:
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
这说明drivers/irqchip/irq-gic.c这个驱动可以兼容arm,gic-400、arm,cortex-a15-gic、arm,cortex-a7-gic
等,但是初始化函数都是统一的gic_of_init。
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl
在我的Character模型中,我添加了:字符.rbbefore_savedoself.profile_picture_url=asset_path('icon.png')end但是,对于数据库中已存在的所有角色,它们的profile_picture_url为nil。因此,我想进入控制台并遍历所有这些并进行设置。在我试过的控制台中:Character.find_eachdo|c|c.profile_picture_url=asset_path('icon.png')end但这给出了错误:NoMethodError:undefinedmethod`asset_path'formain:O
require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie
当我进入Rails控制台时,我已将pry设置为加载代替irb。我找不到该页面或不记得如何将其恢复为默认行为,因为它似乎干扰了我的Rubymine调试器。有什么建议吗? 最佳答案 我刚发现问题,pry-railsgem。忘记了它的目的是让“railsconsole”打开pry。 关于ruby-on-rails-带有Pry的Rails控制台,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/question
我正在尝试将$stdout设置为临时写入一个文件,然后返回到一个文件。test.rb:old_stdout=$stdout$stdout.reopen("mytestfile.out",'w+')puts"thisgoesinmytestfile"$stdout=old_stdoutputs"thisshouldbeontheconsole"$stdout.reopen("mytestfile1.out",'w+')puts"thisgoesinmytestfile1:"$stdout=old_stdoutputs"thisshouldbebackontheconsole"这是输出。r
我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id