精华内容
下载资源
问答
  • 汇编语言 --实现软中断机制

    千次阅读 2018-09-19 22:42:21
    CPU会自动识别产生的异常,并将当前的PC值定位到...中断也是异常的一种,中断有硬中断(由硬件产生的中断)和软中断(软件产生的中断)之分。ARM有七种不同的中断源,在中断向量表中对应的地址范围是0X00 ~ 0X1C,...

    CPU会自动识别产生的异常,并将当前的PC值定位到异常向量表的某一个位置,但是由于异常的产生是随机的,我们并不知道什么时间会出现在什么类型的异常,所以需要一个异常向量表(也有说是中断向量表),来完成对不同异常处理函数入口地址的映射。

    中断也是异常的一种,中断有硬中断(由硬件产生的中断)和软中断(软件产生的中断)之分。ARM有七种不同的中断源,在中断向量表中对应的地址范围是0X00 ~ 0X1C,本章只介绍软中断。软中断执行由SWI指令产生,用于用户模式下的程序调用特权操作指令。

     

    简单实现软中断

    我们可以通过软件仿真来查看程序的执行过程,首先硬件复位后,PC指向0X00(异常向量表中的第一个位置),执行跳转rest函数,通过swi触发软中断,CPU帮我们将PC指向0X08这个地址,这里有是一个跳转指令,跳转到swi_hander函数。

    所以,只要是异常产生,CPU就会帮我们把PC指针定位到异常向量表中某一个位置,这里实现地址的映射,指向什么函数入口,需要我们自己来实现。

    	.text
    	.global _start       
    _start:
    @异常向量表 0X00 - 0X1C
    b rest			@0x00	系统复位后的函数入口 rest
    nop 			@0x04
    b swi_handler 	        @0x08 软中断函数地址入口
    nop 			@0x012
    nop 			@0x014
    nop 			@0x018
    nop 			@0x01c
    		
    
    swi_handler:
    	stmfd sp!,{r0,lr}	@将r0 lr的值进行压栈保存       sp!表示将 sp栈顶指针自动偏移
    	mov r0,#5
    	ldmfd sp!,{r0,pc}	@栈中的值将分别出栈赋值给 r0,pc
    
    rest:
    	ldr sp,=stack_base    @将地址栈底地址装入sp
    	mov r0,#3
    	swi 2	@swi 触发软中断
    	b rest
    
    .data
    	.space 32	@栈空间分配
    stack_base:		@栈底指针
    
    
    	.end
    	
    
    .global _start    声明全局变量_start,整个工程可见
    @ 表示一个函数
    swi_handler:
        @...
        @...
        
    
    rest:     
        @...
        @...      
    @我们使用栈来做现场的保存与恢复
    stmfd sp!,{r0,lr}	@将r0 lr的值进行压栈保存    
    @...
    @...
    ldmfd sp!,{r0,pc}	@栈中的值将分别出栈赋值给 r0,pc

     

     

    软中断的进阶使用

    做了一些改进

    1. 出栈时恢复现场,同时恢复工作模式
    2. 利用软中断号来实现不同的功能
    3. 解决 b指令的缺陷 -- 无法操作地址长度较大的函数地址

     

    由于产生软中断会将工作模式成为SUV模式,如果想要中断结束后恢复之前的模式,需要在出栈ldm后面加上 ^ ; swi 2产生软中断,2是软中断号,存放在这条指令空间中。由于异常发生时,处理器会把当前PC的值保存到LR,所以我们通过LR中的值来拿到软中断号; 一个b指令空间只有4个字节,其中一部分还有存放操作码,一些地址长度比较大的函数就无法实现了,我们用LDR指令来实现跳转。

    	.text
    	.global _start
    _start:
    
    b rest					@0x00	系统复位后的函数入口 rest
    nop 					@0x04
    ldr pc,_swi_handler 	@0x08 软中断函数地址入口
    nop 					@0x012
    nop 					@0x014
    nop 					@0x018
    nop 					@0x01c
    		
    _swi_handler:
    	.word swi_handler  @一个字的空间存放地址
    
    do_swi:
    	mov r3, #3
    	mov r4,	#4
    	mov pc,lr
    
    swi_handler:
    	stmfd sp!,{r0,lr}	@将r0 lr的值进行压栈保存
    	
    	sub r0,lr,#4			@取出软中断号,并且比较软中断号
    	ldr r0,[r0]
    	bic r0,#0xff000000		@清除高八位数据
    	cmp r0,#2
    	bleq do_swi				 @处理软中断
    	
    	ldmfd sp!,{r0,pc}^	@栈中的值将分别出栈赋值给 r0,pc   ^代表出栈恢复入栈时的模式(User)
    
    rest:
    	ldr sp,=stack_base		@Suv模式下
    	
    	mrs   r0,cpsr
    	and   r0,r0,#0xFFFFFFE0
    	orr   r0,r0,#0x10
    	msr   cpsr,r0			@进入User模式
    	
    	mov r0,#3
    	swi 2	@swi 触发软中断 中断号为 2
    	b rest
    
    .data
    	.space 200	@栈空间分配
    stack_base:		@栈底指针
    
    
    	.end

     

     

     

     

     

     

     

     

    展开全文
  • 目录1. 前言2....本专题记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qemu 5

    1. 前言

    本专题我们开始学习进程管理部分。本文主要参考了《奔跑吧, Linux内核》、ULA、ULK的相关内容。
    本专题记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qemu 5.0.0内嵌平台为例,中断控制器采用GIC-400控制器,支持GIC version2技术规范。本文开始介绍ARM64的中断处理过程,上一节介绍了中断的高层处理过程gic_handle_irq,在gic_handle_irq的最后会调用irq_exit退出中断,irq_exit的执行过程中会处理软中断。

    kernel版本:5.10
    平台:arm64

    注:
    为方便阅读,正文标题采用分级结构标识,每一级用一个"-“表示,如:两级为”|- -", 三级为”|- - -“

    2. 软中断概述

    软中断再Linux2.3引入,是预留给系统中对时间要求最为严格和最重要的下半部使用的,目前驱动中只有块设备和网络子系统使用了软中断。

    软中断类型

    系统静态定义了若干种软中断类型,内核开发者不希望用户再扩充新的软中断类型,如有需要,建议使用tasklet机制。每一种软中断都使用索引来表示一种相对的优先级,索引号越小,软中断优先级高,并在一轮软中断处理中得到优先执行。
    已经定义好的软中断类型如下:

    /* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
       frequency threaded job scheduling. For almost all the purposes
       tasklets are more than enough. F.e. all serial device BHs et
       al. should be converted to tasklets, not to softirqs.
     */
    enum
    {
            HI_SOFTIRQ=0,
            TIMER_SOFTIRQ,
            NET_TX_SOFTIRQ,
            NET_RX_SOFTIRQ,
            BLOCK_SOFTIRQ,
            IRQ_POLL_SOFTIRQ,
            TASKLET_SOFTIRQ,
            SCHED_SOFTIRQ,
            HRTIMER_SOFTIRQ,
            RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
    
            NR_SOFTIRQS
    };
    

    软中断数据结构

    • struct softirq_action
    /* softirq mask and active fields moved to irq_cpustat_t in
     * asm/hardirq.h to get better cache usage.  KAO
     */
    struct softirq_action
    {
            void    (*action)(struct softirq_action *); 
    };
    

    用于描述软中断所要执行的回调

    static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
    

    软中断描述符数组softirq_vec[],类似于硬件中断描述符数据结构irq_desc[],每个软中断类型对应一个描述符,其中软中断的索引号就是该数组的索引.NR_SOFTIRQS为支持的最大软中断类型个数,__cacheline_aligned_in_smp用于将softirq_vec数据结构和L1缓存行对齐

    • irq_cpustat_t
    typedef struct {
    	unsigned int __softirq_pending;
    } ____cacheline_aligned irq_cpustat_t;
    

    描述软中断状态信息,可以理解为软中断状态寄存器,其成员__softirq_pending的每个bit位表示一个软中断类型的状态,置0表示此类型软中断没有触发,置1表示已经触发,需要执行对应的软中断处理函数。

    #ifndef __ARCH_IRQ_STAT
    DEFINE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
    EXPORT_PER_CPU_SYMBOL(irq_stat);
    #endif
    

    每一个CPU有一个软中断状态信息变量irq_stat(即__softirq_pending),__softirq_pending的每一个bit代表本CPU的一个软中断类型

    3. 注册软中断

    void open_softirq(int nr, void (*action)(struct softirq_action *))
        |--softirq_vec[nr].action = action;
    

    softirq_vec[]是一个多CPU共享的数组,软中断的初始化通常是在系统启动时init函数中完成(如:subsys_initcall(blk_softirq_init);),系统启动时是串行执行的,因此没有额外保护机制。

    4. 触发软中断

    raise_softirq和raise_softirq_irqoff是触发软中断的两个主要接口,注意触发软中断只是将per cpu的irq_stat.__softirq_pending的相应bit位置位,中断处理退出函数irq_exit执行软中断才会真正的执行软中断对应的回调action

    raise_softirq

    void raise_softirq(unsigned int nr)
        |  //关闭本地中断,实际屏蔽本地CPU的PSTATE的irq bit
        |--local_irq_save(flags);
        |--raise_softirq_irqoff(nr);
        |      |--__raise_softirq_irqoff(nr);
        |      |      |--lockdep_assert_irqs_disabled();
        |      |      |  //实际是置位本地cpu的irq_stat.__softirq_pending的第nr bit
        |      |      |--or_softirq_pending(1UL << nr);
        |      |  //如果不在中断上下文,wakeup_softirqd唤醒本cpu的ksoftirqd线程执行软中断处理函数
        |      |--if (!in_interrupt()) 
        |             |--wakeup_softirqd();
        |--local_irq_restore(flags);
    

    主动触发一个软中断的API接口函数,它会主动关闭本地中断,它实际只会记录

    or_softirq_pending:通过置位本地cpu的irq_stat.__softirq_pending的第nr bit,来使得软中断处于pending状态,这样irq_exit->invoke_softirq来执行软中断的softirq_action

    wakeup_softirqd:如果不在中断上下文,wakeup_softirqd唤醒本cpu的ksoftirqd线程执行软中断处理函数。 每个cpu都会有一个ksoftirqd线程,专门处理自己CPU的软中断

    小结:从这里可以看出每个cpu都通过一个变量irq_stat(per_cpu类型)维护本CPU的所有类型软中断状态,如果变量中某个bit位置位,表示发生了对应类型的软中断,需要退出中断上下文时,执行软中断的处理函数(后面会总结软中断执行的三个时机)

    raise_softirq_irqoff

    与raise_softirq的区别是会不会主动关闭本地中断

    5. 执行软中断

    此处主要承接中断管理基础学习笔记 - 5.2 ARM64高层中断处理,在高层处理完毕后会调用irq_exit退出中断,在irq_exit中会检查并执行软中断的处理函数。这里要注意的是软中断处理函数的执行时机,包含三部分:

    1. 中断返回,在中断上下文,irq_exit会执行各个CPU的__softirq_pending中置位的软中断,运行在中断上下文;
    2. 如果软中断太多,或耗时太久将会唤醒 本cpu的ksoftirqd线程来执行软中断,运行在进程上下文;
    3. local_bh_enable中会调用do_softirq()执行软中断处理,运行在进程上下文;

    中断返回

    static inline void invoke_softirq(void)
    {
    		//如果当前有软中断线程正在执行软中断处理,则退出?保证软中断执行的串行化?
            if (ksoftirqd_running(local_softirq_pending()))
                    return;
            if (!force_irqthreads) {
    #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
                    /*
                     * We can safely execute softirq on the current stack if
                     * it is the irq stack, because it should be near empty
                     * at this stage.
                     */
                    __do_softirq();
    #else
                    /*
                     * Otherwise, irq_exit() is called on the task stack that can
                     * be potentially deep already. So call softirq in its own stack
                     * to prevent from any overrun.
                     */
                    //使用被中断进程的内核栈执行?
                    do_softirq_own_stack();
    #endif
            } else {
                    wakeup_softirqd();
            }
    }              
    

    本例中force_irqthreads为false,也就是没有强制软中断线程化,因此不会执行wakeup_softirqd;
    本例中没有定义CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK宏,因此会执行do_softirq_own_stack,它表示会使用被中断进程的内核栈?

    do_softirq_own_stack(void)
        |--__do_softirq(void)
        		|  //初始化软中断处理的最长时间,如果超过这个时间将唤醒软中断线程进行处理
        		|--unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
        		|  //初始化软中断处理允许的最大次数
        		|--int max_restart = MAX_SOFTIRQ_RESTART;
    			|--current->flags &= ~PF_MEMALLOC;
    			|  //获取pending的软中断
    			|--pending = local_softirq_pending();
    			|--account_irq_enter_time(current);
    			|  //禁用软中断
    			|--__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
    			|--in_hardirq = lockdep_softirq_start();
    		restart:                      
    			|  //Reset the pending bitmask before enabling irqs
    			|--set_softirq_pending(0);
    			|  //使能cpu中断,实际是清空PSTATE的I位
    			|--local_irq_enable();
    			|  //softirq_vec为软中断描述符数组,保存了每个软中断的action
    			|--h = softirq_vec;
    			|--while ((softirq_bit = ffs(pending))
    			|		|  //从softirq_vec数组拿到pending的软中断描述符(里面保存了action)
    			|		|--h += softirq_bit - 1;
    			|		|  //执行软中断处理函数
    			|		|--h->action(h);
    			|		|--h++;
    			|		|--pending >>= softirq_bit//从低位到高位,每处理完一个软中断,需右移
    			|--local_irq_disable();
    			|--pending = local_softirq_pending();
    			|--if (pending)
    			|		//如果没有超过软中断处理的最后时间点或没有超过允许的最大次数,可以继续在软中断上下文处理
    			|		if (time_before(jiffies, end) && !need_resched() && --max_restart)
    			|			goto restart;
    			|		//唤醒软中断线程进行处理
    			|		wakeup_softirqd();
    			|  //重新使能软中断
    			|--__local_bh_enable(SOFTIRQ_OFFSET);
    

    __do_softirq主要是处理pending的软中断,执行软中断的处理函数

    1. __local_bh_disable_ip:主要是通过递增current_thread_info()->preempt.count变量的softirq域。说明在软中断的处理过程中是屏蔽软中断的,因此软中断回调中不需要再关软中断;

    2. local_irq_enable:此处说明在软中断处理过程中,硬中断是开启的

    3. while ((softirq_bit = ffs(pending)):从pending的最低位开始遍历,由于最低位为最高优先级的软中断,因此此处体现了从软中断优先级最高到最低进行处理,对每一个pending 的软中断会执行其action回调函数

    4. wakeup_softirqd:如果没有超过软中断处理的最后时间点或没有超过允许的最大次数,可以继续在软中断上下文处理,否则将唤醒软中断线程进行处理

    ksoftirqd

    <kernel/softirq.c>
    static struct smp_hotplug_thread softirq_threads = {
            .store                  = &ksoftirqd,
            .thread_should_run      = ksoftirqd_should_run,
            .thread_fn              = run_ksoftirqd,
            .thread_comm            = "ksoftirqd/%u",
    };
    
    static __init int spawn_ksoftirqd(void)
    {
            cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, "softirq:dead", NULL,
                                      takeover_tasklets);
            BUG_ON(smpboot_register_percpu_thread(&softirq_threads));
    
            return 0;
    }
    early_initcall(spawn_ksoftirqd);
    

    ksoftirqd内核线程是在start_kernel初始化时通过spawn_ksoftirqd创建的per cpu线程,通过ps或top命令可以看到。其中的线程处理函数为run_ksoftirqd

    static void run_ksoftirqd(unsigned int cpu)
    {
            local_irq_disable();
            if (local_softirq_pending()) {
                    /*
                     * We can safely run softirq on inline stack, as we are not deep
                     * in the task stack here.
                     */
                    __do_softirq();
                    local_irq_enable();
                    cond_resched();
                    return;
            }
            local_irq_enable();
    }
    

    这里我们可以看到ksoftirqd线程在执行时是关闭了本地cpu中断的,再一次验证了软中断执行的串行化。这里同样也会判断是否有软中断pending, 如果有pending则会调用__do_softirq对软中断进行处理。注意这里与invoke_softirq的区别,invoke_softirq是运行在中断上下文,而ksoftirqd运行在进程上下文。

    local_bh_enable

    static inline void local_bh_enable(void)
    	|-- __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
    			|--if (softirq_count() == SOFTIRQ_DISABLE_OFFSET)
    			|		lockdep_softirqs_on(ip);
    			|-- preempt_count_sub(cnt - 1);
    			|--if (unlikely(!in_interrupt() && local_softirq_pending()))
    			|		do_softirq(); 
    			|--preempt_count_dec();
    			|-- preempt_check_resched();	
    

    另一个发生软中断处理的场景是local_bh_enable中。

    local_bh_disable()和local_bh_enable()是内核中提供的关闭软中断的锁机制,它们组成的临界区禁止本地CPU在软中断返回前夕执行软中断。local_bh_disable()和local_bh_enable()运行在进程上下文中。因为在软中断执行时即使被硬中断打断返回时仍然会继续执行完打断的软中断,软中断都是串行执行,因此从这个角度上讲,在软中断中没有必要通过local_bh_disable()和local_bh_enable()开关软中断。当进程中调用spin_lock_bh或local_bh_disable时,中断返回时本来要调用软中断,此时就不会调用软中断,软中断被延时执行,一致延迟到调用spin_unlock_bh或local_bh_enable

    6. tasklet软中断

    tasklet是利用软中断实现的一种下半部机制,本质上是软中断的一个变种,运行在软中断上下文。本节主要以tasklet软中断为例说明软中断的初始化、注册、以及执行的过程。

    定义一个tasklet

    (1)静态声明

    #define DECLARE_TASKLET(name, _callback)                \
    struct tasklet_struct name = {                          \
            .count = ATOMIC_INIT(0),                        \
            .callback = _callback,                          \
            .use_callback = true,                           \
    }
    

    tasklet->count初始化为0,表示tasklet处于激活状态

    #define DECLARE_TASKLET_DISABLED(name, _callback)       \
    struct tasklet_struct name = {                          \
            .count = ATOMIC_INIT(1),                        \
            .callback = _callback,                          \
            .use_callback = true,                           \
    }
    

    tasklet->count初始化为1,表示tasklet处于关闭状态

    (2)动态声明:

    void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
    

    tasklet软中断注册

    void __init softirq_init(void)
    {
            int cpu;
    
            for_each_possible_cpu(cpu) {
                    per_cpu(tasklet_vec, cpu).tail =
                            &per_cpu(tasklet_vec, cpu).head;
                    per_cpu(tasklet_hi_vec, cpu).tail =
                            &per_cpu(tasklet_hi_vec, cpu).head;
            }
    
            open_softirq(TASKLET_SOFTIRQ, tasklet_action);
            open_softirq(HI_SOFTIRQ, tasklet_hi_action);
    }
    

    kernel启动流程-start_kernel的执行_1.概述一节中介绍start_kernel执行时会执行 softirq_init();它会初始化各个cpu core的tasklet和tasklet_hi链表,另外还会注册TASKLET_SOFTIRQ和HI_SOFTIRQ这两个软中断,他们的软中断处理函数分别是tasklet_action和tasklet_hi_action,在这两个软中断处理函数里分别会遍历tasklet_vec链表和tasklet_hi_vec链表

    tasklet软中断执行

    static __latent_entropy void tasklet_action(struct softirq_action *a)
    	|--tasklet_action_common(a, this_cpu_ptr(&tasklet_vec), TASKLET_SOFTIRQ);
    			|--struct tasklet_struct *list;
    			|  //关中断的前提下获取tasklet_vec链表头保存在list
    			|--local_irq_disable();
    			|  list = tl_head->head;
    			|  tl_head->head = NULL;
    			|  tl_head->tail = &tl_head->head; 
    			|  local_irq_enable();
    			|--while (list)
    					struct tasklet_struct *t = list;
    					list = list->next;
    					//保证在同一个cpu上运行此tasklet
    					if (tasklet_trylock(t))
    						//t->count为0表示该tasklet处于可执行状态
    						if (!atomic_read(&t->count))
    							if (!test_and_clear_bit(TASKLET_STATE_SCHED,&t->state))
    								BUG();
    							t->func(t->data);
    							//清除tasklet->state的TASKLET_STATE_RUN标记
    							tasklet_unlock(t);
    							continue;
    					local_irq_disable();
    					t->next = NULL;
    					*tl_head->tail = t;
    					tl_head->tail = &t->next;
    					__raise_softirq_irqoff(softirq_nr);
    					local_irq_enable();
    

    tasklet_action主要是遍历执行tasklet_vec链表中的action,它主要区分两种情况,一种是tasklet已经执行过,但是被中断打断,对于此种情况,为保证tasklet只能在同个cpu上执行完毕,会将此tasklet标记到当前cpu的pending,在下次中断退出时处理,之前已经执行的tasklet在中断返回后继续执行完毕;另一种情况是,tasklet没有被执行过,则此处得到执行。

    1. list = tl_head->head: 关中断的前提下获取tasklet_vec链表头保存在list,while循环中将遍历此链表

    2. tasklet_trylock:此处主要为了确保tasklet只能在一个cpu上执行完。主要通过判断tasklet->state的TASKLET_STATE_RUN位是否已经置位,如果没有置位,说明此tasklet没有执行过,则置位并返回,表示可以锁定,则执行此tasklet的回调;否则表示此tasklet已经在一个cpu被运行,在运行的过程中可能被中断打断,为保证tasklet只能在同个cpu运行完,因此对这种情况忽略对此tasklet的处理,而是将其重新挂入到当前cpu的tasklet_vec链表,表示下次在当前cpu处理。而之前被中断的tasklet在中断返回后继续执行完毕

    3. t->func(t->data): 执行tasklet的回调;.

    4. test_and_clear_bit(TASKLET_STATE_SCHED,&t->state):测试tasklet->state的TASKLET_STATE_SCHED是否置位,它是在tasklet_schedule中置位的,如果没有置位就是一个BUG;如果已经置位,则清除,以保证在t->func(t->data)执行期间其它的tasklet可以执行tasklet_schedule进行调度;

    tasklet_schedule

    static inline void tasklet_schedule(struct tasklet_struct *t)
    	|--if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
    	 		__tasklet_schedule(t);
    				|--__tasklet_schedule_common(t, &tasklet_vec,TASKLET_SOFTIRQ);
    						|--local_irq_save(flags);
    						|--head = this_cpu_ptr(headp);
    						|--t->next = NULL;
    						|--*head->tail = t;
    						|--head->tail = &(t->next); 
    						|--raise_softirq_irqoff(softirq_nr);
    						|--local_irq_restore(flags);
    
    1. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
      taslet_schedule首先会检测tasklet_struct->state有没有置位TASKLET_STATE_SCHED标志位,如果已经置位TASKLET_STATE_SCHED,则会退出,因此在tasklet_action执行时要先清除TASKLET_STATE_SCHED标志,以让其它的tasklet执行tasklet_schedule

    2. __tasklet_schedule:如果没有设置表示还没有执行,置位的同时调用__tasklet_schedule只是触发软中断,即将tasklet挂入到tasklet_vec链表,由于tasklet_vec链表是per cpu的,因此会加入到当前的CPU的tasklet_vec,执行时也会有对应的CPU执行。如果已经置位TASKLET_STATE_SCHED标志位,直接退出,所以如果一个tasklet没有执行,多次执行tasklet_schedule,也不会将这个tasklet挂载到其它的cpu的tasklet_vec链表,除非在本cpu的tasklet_vec链表的这个tasklet执行完了,清空了tasklet->state的TASKLET_STATE_SCHED标志位,下次执行tasklet_schedule时才有机会链入其它的cpu上的tasklet_vec链表

    3. raise_softirq_irqoff:置位 per cpu的__softirq_pending的TASKLET_SOFTIRQ 位

    tasklet小结

    执行软中断需要等到中断处理时在irq_exit中遍历本cpu的__softirq_pending的每个bit位,如果TASKLET_SOFTIRQ置位则执行对应的tasklet_action,tasklet_action再遍历tasklet_vec链表,执行每个tasklet设定的function,执行每个tasklet完毕后会清空tasklet->state的TASKLET_STATE_SCHED,清空之后,下次执行tasklet_schedule时才有机会链入其它的cpu上的tasklet_vec链表,在其它CPU上执行这个tasklet,也就是一旦一个tasklet挂载到一个cpu上,只有在此cpu上执行完毕才能再挂载到其它CPU上执行

    参考文档

    奔跑吧,Linux内核

    展开全文
  • 目录1. 前言2. gic_handle_irq|- -irq_...本专题记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qe

    1. 前言

    本专题我们开始学习进程管理部分。本文主要参考了《奔跑吧, Linux内核》、ULA、ULK的相关内容。
    本专题记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qemu 5.0.0内嵌平台为例,中断控制器采用GIC-400控制器,支持GIC version2技术规范。本文开始介绍ARM64的中断处理过程,上一节介绍了底层硬件中断的处理过程,其中会调用到handle_arch_irq,在中断管理基础学习笔记 - 2.中断控制器初始化一节的gic_of_init->set_handle_irq中介绍过,会将handle_arch_irq初始化为gic_handle_irq,这个就作为中断的顶层处理函数,本节会详细说明gic_handle_irq函数的处理流程。

    kernel版本:5.10
    平台:arm64

    注:
    为方便阅读,正文标题采用分级结构标识,每一级用一个"-“表示,如:两级为”|- -", 三级为”|- - -“

    2. gic_handle_irq

    <drivers/irqchip/irq-gic.c>
    static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
        |--struct gic_chip_data *gic = &gic_data[0];
        |--void __iomem *cpu_base = gic_data_cpu_base(gic);
        |--do {
               //读取GIC的GICC_IAR寄存器,读取行为本身是对中断的ack
               irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
               //获取发生中断的硬中断号
               irqnr = irqstat & GICC_IAR_INT_ID_MASK;
               if (unlikely(irqnr >= 1020))
                   break;
               //写入GICC_EOIR寄存器,通知CPU interface中断处理完成
               if (static_branch_likely(&supports_deactivate_key))
                   writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
               isb();
               //对SGI私有中断的处理
               if (irqnr <= 15)
                   smp_rmb();
                   this_cpu_write(sgi_intid, irqstat);
               handle_domain_irq(gic->domain, irqnr, regs);
           } while (1);
    

    中断高层处理,这里有个疑问while循环是如何退出的?难道没有中断的时候GICC_IAR的bit0~bit9全1?

    1. readl_relaxed(cpu_base + GIC_CPU_INTACK);:读取GIC的GICC_IAR寄存器,读取行为本身是对中断的ack,根据中断管理基础学习笔记 - 1.概述一节中介绍的gic状态机,读取GICC_IAR寄存器就是对中断的ACK,会让中断从pending状态进入到active状态;

    2. writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI):写入GICC_EOIR寄存器,通知CPU interface中断处理完成,此时就是deactive 中断,让中断从active状态转到inactive状态;

    3. handle_domain_irq:前面介绍中断控制器初始化一文,对于gic中断控制器来讲会执行gic_of_init初始化,他会创建并注册irq_domain,此处的gic->domain就是gic初始化时创建的,它代表了中断控制器;在创建软硬中断号映射一文中,通过遍历dts中中断节点,为每个硬中断号创建了映射,并将映射关系保存在irq_domain->linear_revmap数组或revmap_tree树中,其中以硬中断号为索引。此处参数irqnr就是硬中断号,通过它可以知道软中断号,以软中断号为索引可以获取到irq_desc,进一步获取到irq_data,并获取到irqaction进行处理。

    handle_domain_irq(struct irq_domain *domain,unsigned int hwirq, struct pt_regs *regs)
        |--__handle_domain_irq(domain, hwirq, true, regs);
               |--unsigned int irq = hwirq;
               |--irq_enter();
               |  //以硬中断号为索引软中断号返回给irq
               |--irq = irq_find_mapping(domain, hwirq);
               |--generic_handle_irq(irq);
               |--irq_exit();
    
    1. irq_enter:显示的告诉Linux内核现在进入中断上下文,主要通过增加preempt.count的HARDIRQ域计数值来实现;

    2. irq_find_mapping:以硬中断号为索引软中断号返回给irq,这里rq_domian会维护软硬中断号的映射关系,通过irq_domain可以获取到软中断号,其中如果hwirq < domain->revmap_size,则会直接通过domain->linear_revmap[hwirq]返回,否则以hwirq为索引通过domain->revmap_tree查找获得irq_data,irq_data->irq中就保存了对应的软中断号;

    3. generic_handle_irq:为通用中断处理的主函数

    4. irq_exit:与irq_enter相匹配进行递减,并处理软中断

    |- -irq_enter

    void irq_enter(void)//Enter an interrupt context including RCU update
        |--rcu_irq_enter();
        |--irq_enter_rcu();
               |--if (is_idle_task(current) && !in_interrupt())
               |     local_bh_disable();
               |      tick_irq_enter();
               |      _local_bh_enable();
               |--__irq_enter();
                     |--account_irq_enter_time(current);
                     |--preempt_count_add(HARDIRQ_OFFSET);
                     |      |-- __preempt_count_add(val);
                     |              |--u32 pc = READ_ONCE(current_thread_info()->preempt.count);
                     |              |--pc += val;
                     |              |--WRITE_ONCE(current_thread_info()->preempt.count, pc);
                     |--lockdep_hardirq_enter();
    

    从上面可以看出irq_enter->preempt_count_add->__preempt_count_add主要通过操作了current_thread_info()->preempt.count变量,此变量主要用于计数操作,由参数HARDIRQ_OFFSET可知操作的是硬件中断计数器,它占用4个bit,此计数值非0表示当前处于硬件中断上下文处理,此处是累加了preempt.count的HARDIRQ域的计数值,表示当前处于硬件中断上下文,同样在irq_exit时需要匹配递减。

    /*
     * We put the hardirq and softirq counter into the preemption
     * counter. The bitmask has the following meaning:
     *
     * - bits 0-7 are the preemption count (max preemption depth: 256)
     * - bits 8-15 are the softirq count (max # of softirqs: 256)
     *
     * The hardirq count could in theory be the same as the number of
     * interrupts in the system, but we run all interrupt handlers with
     * interrupts disabled, so we cannot have nesting interrupts. Though
     * there are a few palaeontologic drivers which reenable interrupts in
     * the handler, so we need more than one bit here.
     *
     *         PREEMPT_MASK:        0x000000ff
     *         SOFTIRQ_MASK:        0x0000ff00
     *         HARDIRQ_MASK:        0x000f0000
     *             NMI_MASK:        0x00f00000
     * PREEMPT_NEED_RESCHED:        0x80000000
     */
    /*
     *
    #define PREEMPT_BITS    8
    #define SOFTIRQ_BITS    8
    #define HARDIRQ_BITS    4
    #define NMI_BITS        4
    
    #define PREEMPT_SHIFT   0
    #define SOFTIRQ_SHIFT   (PREEMPT_SHIFT + PREEMPT_BITS)
    #define HARDIRQ_SHIFT   (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
    #define NMI_SHIFT       (HARDIRQ_SHIFT + HARDIRQ_BITS)
    
    #define __IRQ_MASK(x)   ((1UL << (x))-1)
    
    #define PREEMPT_MASK    (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
    #define SOFTIRQ_MASK    (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
    #define HARDIRQ_MASK    (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
    #define NMI_MASK        (__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)
    
    #define PREEMPT_OFFSET  (1UL << PREEMPT_SHIFT)
    #define SOFTIRQ_OFFSET  (1UL << SOFTIRQ_SHIFT)
    #define HARDIRQ_OFFSET  (1UL << HARDIRQ_SHIFT)
    #define NMI_OFFSET      (1UL << NMI_SHIFT)
    

    通过如上可知:current_thread_info()->preempt.count变量的计数值按如下进行划分:
    在这里插入图片描述

    |- -generic_handle_irq

    int generic_handle_irq(unsigned int irq)
        |--struct irq_desc *desc = irq_to_desc(irq);
        |--generic_handle_irq_desc(desc);
        |--desc->handle_irq(desc)
               |--handle_fasteoi_irq(desc)
                      |--handle_irq_event(desc);
                             |--__handle_irq_event_percpu(desc, &flags);
                                    |--for_each_action_of_desc(desc, action)
                                           |  //标记中断被强制线程化
                                           |--if (irq_settings_can_thread(desc) &&
                                           |           !(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)))
                                           |       lockdep_hardirq_threaded();
                                           |--res = action->handler(irq, action->dev_id);   
                                           |--switch (res)
                                              //primary handler处理完毕,需要唤醒中断线程
                                              case IRQ_WAKE_THREAD:
                                                  __irq_wake_thread(desc, action);
                                              //无中断线程
                                              case IRQ_HANDLED:
                                                  *flags |= action->flags;   
    

    generic_handle_irq为通用中断处理

    1. desc->handle_irq(desc): 我们在中断管理基础学习笔记 - 3. 创建软硬中断号映射一文的irq_domain_associate中初始化desc->handle_irq为handle_fasteoi_irq,那里我们提到了与handle_arch_irq是何种关系?此处可以做出回答,handle_arch_irq可以理解为中断从底层处理迈入顶层处理的入口,而desc->handle_irq为中断的回调,handle_arch_irq通过层层跋涉最终会调用到desc->handle_irq,并最终调用irqaction->handler,此处desc->handle_irq为handle_fasteoi_irq。

    2. action->handler:此处就是注册中断一文中介绍的,request_threaded_irq会将参数handler传递给action->handler,此函数如果为空,将采用默认的irq_default_primary_handler,他将返回IRQ_WAKE_THREAD,唤醒中断线程执行,中断线程在注册中断时创建;如果primary handler返回的是IRQ_HANDLED,则表示没有中断线程

    |- -irq_exit

    void irq_exit(void)
        |--__irq_exit_rcu();
        |      |--local_irq_disable();
        |      |--account_irq_exit_time(current);
        |      |  //与irq_enter的对应域匹配,递减
        |      |--preempt_count_sub(HARDIRQ_OFFSET);
        |      |  //退出中断上下文并且当前有软中断pending
        |      |--if (!in_interrupt() && local_softirq_pending())
        |      |      //对软中断进行处理
        |      |      invoke_softirq();
        |      |--tick_irq_exit();
        |--rcu_irq_exit();
        |--lockdep_hardirq_exit();
    
    1. local_softirq_pending:主要用来判断当前是否有软中断pending

    2. invoke_softirq:对软中断进行处理,后面会详细说明

    |- - -local_softirq_pending

    #define local_softirq_pending() (__this_cpu_read(local_softirq_pending_ref))
    

    local_softirq_pending主要用来判断是否有软中断pending,其中local_softirq_pending_ref的定义如下:

    #ifndef local_softirq_pending_ref
    #define local_softirq_pending_ref irq_stat.__softirq_pending 
    #endif
    

    这里irq_stat的定义如下:

    #ifndef __ARCH_IRQ_STAT 
    DEFINE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
    EXPORT_PER_CPU_SYMBOL(irq_stat)
    #endif
    

    irq_cpustat_t的定义如下:

    typedef struct {
            unsigned int __softirq_pending;
    } ____cacheline_aligned irq_cpustat_t;
    

    由此可见local_softirq_pending就是判断__softirq_pending的值是否为0,如果不为0表示软中断pending,__softirq_pending的不同bit位表示不同的软中断,在触发软中断时会设置。下一节介绍软中断时再来详细说明

    参考文档

    奔跑吧,Linux内核

    展开全文
  • 【汇编语言】Arm处理器之中断处理

    千次阅读 2014-12-16 23:49:57
    1)在arm处理器中有8种工作模式(及CPU处理不同任务的模式),一般有5中异常模式,而在这5中模式中有三种中断机制,分别是FIQ模式(高优先级中断模式);IRQ模式(低优先级中断模式);还有一种就是SVC模式(当复位...

    Arm处理器之 中断处理


    1)在arm处理器中有8种工作模式(及CPU处理不同任务的模式),一般有5中异常模式,而在这5中模式中有三种中断机制,分别是FIQ模式(高优先级中断模式);IRQ模式(低优先级中断模式);还有一种就是SVC模式(当复位或者软中断(SWI)指令执行时产生的模式)

     

    2)首先了解什么是中断机制

    在裸机arm中,SOC对硬件中断的支持(中断控制器),那么中断是怎么产生的,当CPU处理器正在执行一条指令的时,突然有外部中断请求或者内部中断的中断信号(中断信号源)

     

    3)由于arm处理器支持硬件中断,对于上图的FIQIRQ中断,在arm内核中会引出两条中断线,当CPU在执行完每条指令后,会自动检测中断请求输入线,以确定是否有外部发来的中断请求信号。由于中断可屏蔽,所以一般arm中断初始为屏蔽状态,即IF1.其实在这里中断请求寄存器不是直接连接到IR中断线的,而是要通过中断控制器来辨别区分以及判断某个时刻多个中断请求的优先级,最终通过中断控制器(控制比较器)传送传送中断请求,而且处理器还会暂停正在译码的指令,指针会跳转到相应的异常向量表。在RAM裸机没有操作系统的情况下,当进入异常处理函数是,内存中我们此时应该建立维护一个异常向量表,初始化栈的空间以便跳转运行的空间,另外还要做一件重要的事情,即是保存现场和处理完异常后的现场恢复,这里通过arm执行指令的流水线发现,当一个中断进入的时候,此时处理器会暂停正在译码的指令,①此时在保护现场的时候,我们应该把保存的LR寄存器的地址值减去4后保存sub lr, lr, #4     stmfd sp!, {r0-r12, lr};

    ③在出栈的时候只要恢复现场就可以了,一般的操作时         ldmfd sp!, {r0-r12, pc}^  @出栈时同时恢复cpsr

    ②其实在保护现场后,我们要做的就是怎么去处理相应的中断异常,在汇编中可以通过 B DO_IRQ_FUNC跳转C函数中去完成编写处理函数。

     

     

    4)中断请求寄存器是于外部接口的中断请求线相连的寄存器,请求中断的外设分别通过IR0~ IR7向中断控制器请求中断服务,并把中断请求的状态保持在中断请求寄存器中,在arm处理器中有3个中断请求寄存器分别处理94个中断请求,当外部有中断请求的时候,会通过相应的逻辑电路的电平会发生变化,这里的中断激发方式有上升沿,下降沿,双边沿,高电平以及低电平,

     

    外部中断管理模块,从 SOC 共有 32 个管脚引出来,
    这些管脚是复用的,可以配置成通用输入/输出 IO,也可以配置成唤
    醒中断的模式(及内部会连接到中断控制器电路)。 GPIO 模块内部有
    三个寄存器对这 32 根管线的中断功能进行管理。

    GPH0.GPH0CON = GPH0.GPH0CON & ~(0xf << 8) | (0x2 << 8);

    这里通过GPIO 通用输出输入接口的引脚来把KEY的按键设置成中唤醒中断控制模式,这样就可以通过按键来实现IRQ的中断机制


    (1)功能寄存器 如图,对管脚的电平激发方式进行管理,可以配
    置成高电平激发中断,低电平 、上升沿 、下降沿、双边沿等。

    WU_INT0_7_CON = WU_INT0_7_CON & ~(0x7 << 8) | (0x2 << 4);


    (2)Mask 寄存器 外部管脚 EINT16 到 EINT31 共 16 根线共用一
    根线 EINT(16-31)连接到中断控制器,也就是共用一个中断号(中断
    号 16),可能需要在某一个时刻只能有一根管脚线的信号通过模块顺
    利进入中断控制器,这个时候就需要将其余的管脚信号屏蔽起来,如
    何屏蔽呢?就是设置 mask 里的对应位。

    WU_INT0_7_MASK = WU_INT0_7_MASK & ~(1 << 2);

    设置MASK寄存器可以使得在一个中断触发时,关闭之后的中断进入,因为处理器在一个特定时间之内只能处理单个中断


    (3) Pend 寄存器 记录中断信号标志。如果信号通过了前面的关
    口,送入了 VIC,就是将对应线上的 pend 寄存器的对应位值 1,如果
    在某一时刻,中断控制器来不起处理当前信号线上的中断请求,那么
    pend 位会一直保持着这个请求,哪怕硬件中断线上的中断激发状态
    已经过去。同样,需要注意是,如果当前线上的中断已经处理,这个
    pend 位也不会自动清零,需要人为的清零。

    (4)当前面准备工作完成时,我们还应该做重要的事,即是打开中断控制器相应的中断源开关,此时中断请求信号就可以通过中断控制器从相应的IRQ或者FIQ中断线触发处理器去执行中断,在这里中断控制器还做了一件重要的事情就是把相应的中断处理函数注册到地址寄存器中去,接下来arm处理器就可以默认通过指向地址来实现中断函数,那么怎么通过一个地址来处理函数,就要涉及到函数指针的概念来引用地址(在这里一个函数的函数名就是函数的入口地址)从而去调用中断函数。形如void (* pfunc)(void);   pfunc = (void *)address;  ;pfunc ();

     

    3)什么是IRQ中断

    IRQ的全称是“Interupt ReQuest”,即“中断要求”。当电脑内的周边硬件需要处理 器去执行某些工作时,该硬件就会发出一个硬件信号,通知处理器工作,而这个信号就是IRQ。那为什么叫做“中断”呢?“中断”的意思是即使处理器正在执行 其他工作,当它收到周边设备传来的中断信号时,处理器也会停下来,优先处理这个信号代表的工作,完成后再继续处理之前未完成的工作。     

    4)什么是 IRQ冲突

     IRQ的数目有限,一部电脑虽然一共有16个IRQ(从IRQ0至IRQ15),但是其中很多IRQ已经预先分配给特定的硬件,具体如 下: IRQ0:系统计时器 IRQ1:键盘 IRQ2:可设置中断控制卡 IRQ3:COM2(串行接口2) IRQ4:COM1(串行接口1)  IRQ5:未预先配置 IRQ6:磁盘机 IRQ7:并行接口 IRQ8:CMOS/时钟 IRQ9:未预先配置 IRQ10:未预先配置  IRQ11:未预先配置 IRQ12:PS/2鼠标 IRQ13:算术处理器(Arithmetic Processor) IRQ14: Primary(主)IDE控制器 IRQ15:Secondary(从)IDE控制器   由上可见,IRQ5、IRQ9、IRQ10和IRQ11都是 空置的。但大家不要以为这就代表着有多余的IRQ可以使用。因为要使用IRQ的周边设备实在是太多了,例如声卡、网卡等PCI或ISA设备都需要配置一个 IRQ。如果有两个设备配置了同一个IRQ的话,就会出现IRQ冲突的问题,从而使两者都不能正常工作。     

    5)遇到IRQ冲突怎么办 

    般主板都有四根或更多PCI插槽。如果全都插上PCI扩展卡,那四个空置的IRQ又怎么够用呢?   其实,某些硬件是可以共用一个IRQ的,而 有些却又偏偏不行。例如PCI声卡需要独自享用一个IRQ,有时甚至需要两个,一个作MIDI(迷笛),一个作Wave(波表)。因此当系统自动分配 IRQ时,若声卡被分配与其他设备共用一个IRQ的话,发生IRQ冲突的可能性极大,而解决之道就是手动分配IRQ,在BIOS内进行设置。

     

    6)软中断 

    编程异常通常叫做软中断 

    软中断是通讯进程之间用来模拟硬中断的 一种信号通讯方式。 

    中断源发中断请求或软中断信号后,CPU 或接收进程在适当的时机自动进行中断 处理或完成软中断信号对应的功能 软中断是软件实现的中断,也就是程序运行时其他程序对它的中断;而硬中断是硬件实现的中断,是程序运行时设备对它的中断。  

    1. 软中断发生的时间是由程序控制的, 而硬中断发生的时间是随机的 

    2. 软中断是由程序调用发生的, 而硬中断是由外设引发的 

    3. 硬件中断处理程序要确保它能快速地完成它的任务, 这样程序执行时才不会等待较长时间

    SWI : 软件中断

    (Soft ware Interrupt)

    SWI{条件}  <24 位编号>

    指令格式 这是一个简单的设施,但可能是最常用的。多数操作系统设施是用 SWI 提供的。没有 SWI 的 RISC OS 是不可想象的。



    展开全文
  • 1.概述 硬件的中断处理函数处于中断上半部分,在CPU关中断的状态下执行,中断线程、软中断(softirq)及小任务(tasklet)属于中断的下半部分...这里说的软中断是中断下半部分的一种处理机制,和执行指令(ARM架构
  • 软中断概念在嵌入式开发可以有两个不同的解释: 其一,软中断在处理器设计中是处理器异常之一,程序软件使用...其二,在kernel代码中实现了一套软中断机制,区别于硬件中断的硬件触发软件处理,而是软件触发软件处理。
  • **复位(RESET) **当处理器复位...**未定义的指令**当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断,可以通过改异常中断机制仿真浮点向量运算。**软件中断**这是一个由用户定义的中
  • 上一期中我们介绍了ARM Linux内核中的底半机制软中断的处理流程,这一期我们将介绍ARM Linux内核中小任务的工作流程和与工作队列相关的关键数据结构。 一、ARM Linux内核中小任务的工作流程 在ARM Linux内核中,小...
  • 目录1. 前言2. irq_create_fwspec_mapping|- -irq_create_...本节记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及
  • 1. ARM处理器的7种工作模式: USER:非特权模式,大部分任务执行在该模式下 FIQ:高优先级中断模式 IRQ:低优先级中断模式 Supervisor:复位/软中断指令执行时会进入该模式 Abort:存取异常模式 Undef:执行未定义...
  • 在编写设备驱动时, tasklet 机制是一种比较常见的机制,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成。  为了最大程度的避免中断处理时间过长而导致中断丢失,有时候我们...
  •  内容简介: 介绍了Linux下的串口驱动的设计层次及接口, 并指出串口与TTY终端之间的关联层次(串口可作TTY终端使用), 以及Linux下的中断处理机制/中断共享机制, 还有串口缓冲机制当中涉及的软中断机制; 其中有关w...
  • 目录1. 前言2....本节记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qemu 5.0.0
  • Linux中断管理

    2019-06-25 12:16:05
    CPU和外设之间的交互,或CPU通过轮询机制查询,或外设通过中断机制主动上报。 对大部分外设中断比轮询效率高,但比如网卡驱动采取轮询比中断效率高。 这里重点关注ARM+Linux组合下中断管理,从底层硬件GIC+CPU,到...
  • 本节记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以qemu 5.0.0内嵌平台为例,中断控制器采用...
  • 本节记录ARM架构下中断是如何管理的,Linux内核中的中断管理机制是如何设计与实现的,以及常用的下半部机制,如软中断、tasklet、workqueue等。本文及后续中断相关笔记均以ARM Vexpress V2P-CAIS_CA7平台为例
  • 这种机制是通过调用软中断ARM SWI异常触发任务切换。 调用软中断: #define OSTaskRun(PRunTcb) osek_switch_type = 1; __asm volatile ( "SWI 0"); #define OSTaskSaveRun(PSavedTcb,PRunTcb) os
  •  在i386体系中,Linux的系统调用接口是通过调用软中断指令“int $ Oxso”使进程从用户态进入内核态的,这个过程也叫做“陷入”。当系统调用接口调用软中断指令“int $ Ox80”时,这个指令会发生一个中断向量码为...
  • 前言 当嵌入式应用异常崩溃时,应用进程会接收到内核发送的信号,如SIGILL/...此外,因为应用可以在执行到任意的地方被软中断,中断之处没有适当的上下文保存/保护机制,信号处理函数返回后,是如何正确返回,使得...
  • 所设计的调试模块通过引入专用的调试中断及与之对应的调试服务程序实现一种处理器响应断点( breakpoint) 的机制,并基于双端口RAM 中一种巧妙的地址映射机制实现同时对多行代码设置断点的功能。实际的工程应用情况...
  • 摘要: 介绍了Windows CE 的体系结构和中断处理机制, 研究了Windows CE 设备驱动程序的类型和初始化过程,以维信诺VGG13264C 132×64 OLED 显示模块的WindowsCE 驱动程序设计为例, 详细阐述了嵌入式Windows CE ...
  • 嵌入式底层驱动开发技术含量较高,掌握Android从应用开发,到系统移植,再到设备驱动开发的全套技术,无疑会极大的提升自己的职业竞争力和薪酬谢水平 ,本课程深入浅出,手敲全部实战项目代码,经历硬件结合的...
  • 嵌入式底层驱动开发技术含量较高,掌握Android从应用开发,到系统移植,再到设备驱动开发的全套技术,无疑会极大的提升自己的职业竞争力和薪酬谢水平 ,本课程深入浅出,手敲全部实战项目代码,经历硬件结合的...
  • 嵌入式底层驱动开发技术含量较高,掌握Android从应用开发,到系统移植,再到设备驱动开发的全套技术,无疑会极大的提升自己的职业竞争力和薪酬谢水平 ,本课程深入浅出,手敲全部实战项目代码,经历硬件结合的...
  • 本平台主要红外、可见光图像跟踪、识别等产品开发公司提供一套完整的硬件基础开发环境,通过完整的双光视觉平台硬件(包括摄像头、MPSOC核心板、接口板),理解整个图像传输、处理的硬件系统,通过整体的双光视觉...
  • 本平台主要红外、可见光图像跟踪、识别等产品开发公司提供一套完整的硬件基础开发环境,通过完整的双光视觉平台硬件(包括摄像头、MPSOC核心板、接口板),理解整个图像传输、处理的硬件系统,通过整体的双光视觉...
  • clk_register

    千次阅读 2012-10-08 10:09:55
    之前写了那么多关于系统时钟的机制,说到底,就那么回事,初始化系统时钟,初始化软中断, 以及初始化那个tvec_base,就这些,其他的也没有什么了,这篇主要关于每个外部设备的时钟的初始化,及获取。  首先贴上...

空空如也

空空如也

1 2 3
收藏数 44
精华内容 17
关键字:

arm软中断机制