/**
- struct irq_desc - interrupt descriptor
- @irq: interrupt number for this descriptor
- @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
- @chip: low level interrupt hardware access
- @msi_desc: MSI descriptor
- @handler_data: per-IRQ data for the irq_chip methods
- @chip_data: platform-specific per-chip private data for the chip
- methods, to allow shared chip implementations
- @action: the irq action chain
- @status: status information
- @depth: disable-depth, for nested irq_disable() calls
- @wake_depth: enable depth, for multiple set_irq_wake() callers
- @irq_count: stats field to detect stalled irqs
- @irqs_unhandled: stats field for spurious unhandled interrupts
- @last_unhandled: aging timer for unhandled count
- @lock: locking for SMP
- @affinity: IRQ affinity on SMP
- @cpu: cpu index useful for balancing
- @pending_mask: pending rebalanced interrupts
- @dir: /proc/irq/ procfs entry
- @name: flow handler name for /proc/interrupts output
*/
<include/linux/irq.h>
struct irq_desc {
unsigned int irq;
//当前中断处理函数入口,是一个函数指针。
irq_flow_handler_t handle_irq;
/底层硬件初始化
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
//用户提供的中断处理函数链表,我们写的中断处理
struct irqaction action; / IRQ action list /
unsigned int status; / IRQ status /
unsigned int depth; / nested irq disables /
unsigned int wake_depth; / nested wake enables /
unsigned int irq_count; / For detecting broken IRQs /
unsigned int irqs_unhandled;
unsigned long last_unhandled; / Aging timer for unhandled count */
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
extern struct irq_desc irq_desc[NR_IRQS];
///
尤entry_armv.s 里面的中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数。它在arch/arm/kernel/irq.c中定义
/**
-
我们暂且可以认为,绝大部分中断是从汇编跳到本函数处理的。当然,IPI和local_timer不是。
-
irq: 产生中断的外部中断号。
-
regs: 被中断打断的寄存器现场。
*/
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
/**
* 将当前正在处理的中断现场保存到每CPU变量__irq_regs中去。
* 这样做的目的,是为了在其他代码中,直接读取__irq_regs中的值,找到中断前的现场。
* 而不用将regs参数层层传递下去。
*/
struct pt_regs old_regs = set_irq_regs(regs);
irq_enter();
/
- Some hardware gives randomly wrong interrupts. Rather
- than crashing, do something sensible.
/
if (irq >= NR_IRQS) //中断号错误,超过最大中断号
handle_bad_irq(irq, &bad_irq_desc);
else
generic_handle_irq(irq);/// 这里进行正常的中断处理 /
/ AT91 specific workaround */
irq_finish(irq);
irq_exit();
//恢复__irq_regs每CPU变量的内容。
set_irq_regs(old_regs);
}
include/asm-generic/irq_regs.h
//这个函数就是记录中断现场
static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
{
struct pt_regs *old_regs, **pp_regs = &__get_cpu_var(__irq_regs);
old_regs = *pp_regs;
*pp_regs = new_regs;
return old_regs;
}
///
/**
* 中断退出过程,主要处理以下内容:
* 1、调试钩子,记录退出中断的事实。
* 2、在任务的抢占计数字段中,递减中断计数
* 3、处理软中断
* 4、调用rcu模块的函数,表示已经退出中断。
*/
<kernel/softirq.c>
/*
- Exit an interrupt context. Process softirqs if needed and possible:
*/
void irq_exit(void)
{
account_system_vtime(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
tick_nohz_stop_sched_tick(0);
rcu_irq_exit();
#endif
preempt_enable_no_resched();
}
/
/**
* 调用irq_enter表示进入中断处理过程。该函数进行如下处理:
* 1、rcu模块记录内部计数,表示当前退出了NOHZ状态。关于rcu,需要整整一本书来描述,请从http://xiebaoyou.download.csdn.net下载<深入理解并行编程>了解更多内容。
* 2、处理NOHZ事件。
* 3、将当前任务的抢占计数中的硬中断计数加1.该计数表示当前中断嵌套层次。
* 4、调试信息,表示当前已经进入中断的事实。
*/
<kernel/softirq.c>
/*
-
Enter an interrupt context.
*/
void irq_enter(void)
{
int cpu = smp_processor_id();if (idle_cpu(cpu) && !in_interrupt()) {
__irq_enter();
tick_check_idle(cpu);
} else
__irq_enter();
}
/
<arch/arm/plat-s3c64xx/irq.c>
static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc)
{
s3c_irq_demux_timer(irq, IRQ_TIMER0);
}
//
<arch/arm/plat-s3c64xx/irq.c>
/* Timer interrupt handling */
static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq)
{
generic_handle_irq(sub_irq);
}
//
<include/linux/irq.h>中定义
static inline void generic_handle_irq(unsigned int irq)
{
// * 根据中断描述符中的信息,得到该中断ISR,并处理中断。
generic_handle_irq_desc(irq, irq_to_desc(irq));
}
/*
- Architectures call this to let the generic IRQ layer
- handle an interrupt. If the descriptor is attached to an
- irqchip-style controller then we call the ->handle_irq() handler,
- and it calls __do_IRQ() if it’s attached to an irqtype-style controller.
*/
<include/linux/irq.h>中定义
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq, desc);
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
//此处调用s3c24xx_init_irq()中 set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8)的s3c_irq_demux_extint8()
else
__do_IRQ(irq);
#endif
}
///
handle.c
unsigned int __do_IRQ(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction action;
unsigned int status;
spin_lock(&desc->lock);
/
- REPLAY is when Linux resends an IRQ that was dropped earlier
- WAITING is used by probe to mark irqs that are being tested
/
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
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;
if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
action = desc->action;
status &= ~IRQ_PENDING; / we commit to handling /
status |= IRQ_INPROGRESS; / we are handling it /
}
desc->status = status;
/ - If there is no IRQ handler or it was disabled, exit early.
- Since we set PENDING, if another processor is handling
- a different instance of this same irq, the other processor
- will take care of it.
/
if (unlikely(!action))
goto out;
/ - Edge triggered interrupts need to remember
- pending events.
- This applies to any hw interrupts that allow a second
- instance of the same irq to arrive while we are in do_IRQ
- or in the handler. But the code here only handles the second
- instance of the irq, not the third or fourth. So it is mostly
- useful for irq hardware that does not mask cleanly in an
- SMP environment.
/
for (;😉 {
irqreturn_t action_ret;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);//通过此函数逐个执行action链表中用户注册的中断处理函数
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock);
if (likely(!(desc->status & IRQ_PENDING)))
break;
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
out:
/ - The ->end() handler has to deal with interrupts which got
- disabled while the handler was running.
/
desc->chip->end(irq);
spin_unlock(&desc->lock);
return 1;
}
/
handle.c
/* - handle_IRQ_event - irq action chain handler
- @irq: the interrupt number
- @action: the interrupt action chain for this irq
- Handles the action chain of an irq event
*/
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
do {
ret = action->handler(irq, action->dev_id);
if (ret == IRQ_HANDLED)
status |= action->flags;
retval |= ret;
action = action->next;
} while (action);
if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}
/
/init/main.c asmlinkage void __init start_kernel(void) —>调用 arch/arm/kernel/irq.c void __init init_IRQ(void)
void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
init_arch_irq();// 即为 s3c6410_init_irq
//以下为注释
//set_up.c 中有setup_arch()中有init_arch_irq=mdesc->init_irq;对于S3C2410,S3C2440开发板,<arch/arm/mach-s3c6410/mach_smdk6410.c>中有
MACHINE_START(SMDK6410, “SMDK6410”)
/* Maintainer: Ben Dooks ben@fluff.org */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C64XX_PA_SDRAM + 0x100,
.init_irq = s3c6410_init_irq,
.map_io = smdk6410_map_io,
.init_machine = smdk6410_machine_init,
.timer = &s3c64xx_timer,
MACHINE_END
//以上为注释
}
//
<arch/arm/mach-s3c6410/cpu.c>
void __init s3c6410_init_irq(void)
{
/* VIC0 is missing IRQ7, VIC1 is fully populated. */
s3c64xx_init_irq(~0 & ~(1 << 7), ~0);
}
//
<arch/arm/plat-s3c64xx/irq.c>
void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
{
int uart, irq;
printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);
/* initialise the pair of VICs */
vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid);
vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid);
/* add the timer sub-irqs */
set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0);
set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1);
set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2);
set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3);
set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4);
for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
set_irq_chip(irq, &s3c_irq_timer);
set_irq_handler(irq, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}
for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
s3c64xx_uart_irq(&uart_irqs[uart]);
}
///
<arch/arm/plat-s3c64xx/irq.c>
/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0…3]
-
are consecutive when looking up the interrupt in the demux routines.
*/
static struct uart_irq uart_irqs[] = {
[0] = {
.regs = S3C_VA_UART0,
.base_irq = IRQ_S3CUART_BASE0,
.parent_irq = IRQ_UART0,
},
[1] = {
.regs = S3C_VA_UART1,
.base_irq = IRQ_S3CUART_BASE1,
.parent_irq = IRQ_UART1,
},
[2] = {
.regs = S3C_VA_UART2,
.base_irq = IRQ_S3CUART_BASE2,
.parent_irq = IRQ_UART2,
},
[3] = {
.regs = S3C_VA_UART3,
.base_irq = IRQ_S3CUART_BASE3,
.parent_irq = IRQ_UART3,
},
};
//
<arch/arm/plat-s3c64xx/irq.c>
static void __init s3c64xx_uart_irq(struct uart_irq *uirq)
{
void *reg_base = uirq->regs;
unsigned int irq;
int offs;/* mask all interrupts at the start. */
__raw_writel(0xf, reg_base + S3C64XX_UINTM);for (offs = 0; offs < 3; offs++) {
irq = uirq->base_irq + offs;set_irq_chip(irq, &s3c_irq_uart); set_irq_chip_data(irq, uirq); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID);
}
set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);
}
include/linux/irq.h中
static inline void
set_irq_chained_handler(unsigned int irq,
irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 1, NULL);
}
//
kernel/irq/chip.c
void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
desc->handle_irq = handle;
desc->name = name;
if (handle != handle_bad_irq && is_chained) {
desc->status &= ~IRQ_DISABLED;
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
desc->depth = 0;
desc->chip->startup(irq);
}
spin_unlock_irqrestore(&desc->lock, flags);
}
//
static inline struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < nr_irqs) ? irq_desc + irq : NULL;
}
///
irq.h中
static inline struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < nr_irqs) ? irq_desc + irq : NULL;
}
//
///
linux3.08内核的struct irqaction解构体
/**
- struct irqaction - per interrupt action descriptor
- @handler: interrupt handler function
- @flags: flags (see IRQF_* above)
- @name: name of the device
- @dev_id: cookie to identify the device
- @next: pointer to the next irqaction for shared interrupts
- @irq: interrupt number
- @dir: pointer to the proc/irq/NN/name entry
- @thread_fn: interrupt handler function for threaded interrupts
- @thread: thread pointer for threaded interrupts
- @thread_flags: flags related to @thread
- @thread_mask: bitmask for keeping track of @thread activity
*/
//与request_irq函数对应的解构体
struct irqaction {
//用户注册的中断处理函数,由我们自己来写的
irq_handler_t handler;
//触发中断方式
unsigned long flags;
//中断处理函数和卸载中断处理函数是用到
void *dev_id;
struct irqaction *next;
//中断号
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
///
<include/linux/irqreturn.h>
中断处理函数的返回值
/**
- enum irqreturn
- @IRQ_NONE interrupt was not from this device 未处理,系统继续调用别的驱动函数去处理中断
- @IRQ_HANDLED interrupt was handled by this device 已经被处理好了。
- @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)
/
用户注册中断处理函数: manage.c
/**
- request_irq - allocate an interrupt line
- @irq: Interrupt line to allocate
- @handler: Function to be called when the IRQ occurs
- @irqflags: Interrupt type flags
- @devname: An ascii name for the claiming device 传递给request_irq的字符串,用来在/proc/interrupts中显示中断的拥有者
- @dev_id: A cookie passed back to the handler function 用于共享的中断信号线
- This call allocates interrupt resources and enables the
- interrupt line and IRQ handling. From the point this
- call is made your handler function may be invoked. Since
- your handler function must clear any interrupt the board
- raises, you must take care both to initialise your hardware
- and to set up the interrupt handler in the right order.
- Dev_id must be globally unique. Normally the address of the
- device data structure is used as the cookie. Since the handler
- receives this value it makes sense to use it.
- If your interrupt is shared you must pass a non NULL dev_id
- as this is required when freeing the interrupt.
- Flags:
- IRQF_SHARED Interrupt is shared
- IRQF_DISABLED Disable local interrupts while processing
- IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
- IRQF_TRIGGER_* Specify active edge(s) or level
*/在Linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,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在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
<kernel/irq/manage.c>
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc desc;
int retval;
#ifdef CONFIG_LOCKDEP
/
- Lockdep wants atomic interrupt handlers:
/
irqflags |= IRQF_DISABLED;
#endif
/ - Sanity-check: shared interrupts must pass in a real dev-ID,
- otherwise we’ll have trouble later trying to figure out
- which interrupt is which (messes up the interrupt freeing
- logic etc).
/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq); //根据中断号来获得关于中断资源的描述
if (desc->status & IRQ_NOREQUEST) //如果中断资源被设置为无法获得。
return -EINVAL;
if (!handler) // --中断必须要有中断处理函数
return -EINVAL;
action = 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;
__setup_irq函数,该函数才是真正的将中断注册
retval = __setup_irq(irq, desc, action);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
if (irqflags & IRQF_SHARED) {
/ - It’s a shared IRQ – the driver ought to be prepared for it
- to happen immediately, so let’s make sure…
- We disable the irq to make sure that a ‘real’ IRQ doesn’t
- run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id); //???为啥在这里立即调用了中断的handler函数(这是一个参数) 看看宏CONFIG_DEBUG_SHIRQ
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
/
manage.c中
__setup_irq函数,该函数才是真正的将中断注册 /*
- Internal function to register an irqaction - typically used to
- allocate special interrupts that are part of the architecture.
*/
static int
__setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new)
{
struct irqaction *old, **p;
const char old_name = NULL;
unsigned long flags;
int shared = 0;
int ret;
/ - The following block of code has to be executed atomically
*/
spin_lock_irqsave(&desc->lock, flags);
p = &desc->action;
old = p;
if (old) {
/ add new interrupt at end of irq queue */ ????????????????????????????、
do {
p = &old->next;
old = p;
} while (old);
shared = 1;
}
if (!shared) {
irq_chip_set_defaults(desc->chip);
/ Setup the type (level, edge polarity) if configured: /
if (new->flags & IRQF_TRIGGER_MASK) {
ret = __irq_set_trigger(desc, irq, new->flags);
if (ret) {
spin_unlock_irqrestore(&desc->lock, flags);
return ret;
}
} else
compat_irq_chip_set_default_handler(desc);
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = 0;
desc->status &= ~IRQ_DISABLED;
desc->chip->startup(irq);
} else
/ Undo nested disables: /
desc->depth = 1;
/ Exclude IRQ from balancing if requested /
if (new->flags & IRQF_NOBALANCING)
desc->status |= IRQ_NO_BALANCING;
/ Set default affinity mask once everything is setup /
do_irq_select_affinity(irq, desc);
} else if ((new->flags & IRQF_TRIGGER_MASK)
&& (new->flags & IRQF_TRIGGER_MASK)
!= (desc->status & IRQ_TYPE_SENSE_MASK)) {
/ hope the handler works with the actual trigger mode… */
pr_warning(“IRQ %d uses trigger mode %d; requested %d\n”,
irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),
(int)(new->flags & IRQF_TRIGGER_MASK));
}
p = new;
/ Reset broken irq detection when installing new handler /
desc->irq_count = 0;
desc->irqs_unhandled = 0;
/ - Check whether we disabled the irq via the spurious handler
- before. Reenable it and give it another chance.
*/
if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
desc->status &= ~IRQ_SPURIOUS_DISABLED;
__enable_irq(desc, irq);
}
spin_unlock_irqrestore(&desc->lock, flags);
new->irq = irq;
register_irq_proc(irq, desc);
new->dir = NULL;
register_handler_proc(irq, new);
return 0;
mismatch:
spin_unlock_irqrestore(&desc->lock, flags);
return -EBUSY;
}
/
arch/arm/kernel/setup.c void __init setup_arch(char **cmdline_p) 调用
early_trap_init(void)
arch/arm/kernel/traps.c
void __init early_trap_init(void)
{
// include/linux/autoconf.h #define CONFIG_VECTORS_BASE 0xffff0000
unsigned long vectors = CONFIG_VECTORS_BASE;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
//将_vectors_start到__vectors_start之间的代码拷贝到0xffff0000
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
sizeof(sigreturn_codes));
flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
中断共享
如果开发板上按键的中断已经被另外的驱动程序注册中断了,而我现在又想再注册一次这个中断,这就出现了一个中断号不止对应一个中断函数的情况。注意,这里与硬件上的共享中断不一样,这里是指,当一个中断信号来了,基于操作系统,一个中断的到来可以调用多个中断处理程序,与硬件无关。
SA_SHIRQ:这个标志表明多个中断处理程序可以共享一个中断号。
图片