xenomai在linux4.19内核下的ipipe中断处理(ARM64)

目录

1 让linux变成实时操作系统的方法——xenomai

1.1 概述

1.2 xenomai的安装

1.3 ipipe补丁的下载链接

2 ARM64异常向量表

2.1 简介

2.2 内核启动时异常向量表的设置

2.3 内核模式(异常级别1)的中断入口:el1_irq

2.3.1 打过ipipe补丁的el1_irq汇编函数

2.3.2 irq_handler

2.3.3 handle_arch_irq()——中断处理程序C语言部分的入口

3 中断控制器GIC-400

3.1 简介

3.2 中断控制器匹配表和中断控制器驱动初始化

3.2.1 中断控制器匹配表

3.2.2 中断控制器初始化过程

3.3 GIC-400的中断处理函数gic_handle_irq()

4 深入分析ipipe中断处理

4.1 结构体struct ipipe_domain和它的实例化对象ipipe_root_domain / ipipe_head_domain

4.1.1 简介

4.1.2 全局变量:ipipe_head_domain / ipipe_root_domain / ipipe_root

4.2 Root domain的初始化和中断的处理

4.2.1 Root domain初始化大致流程

4.2.2 Root domian中断的处理流程

4.3 Head domain的初始化和中断的处理

4.3.1 Head domain初始化大致流程

4.3.2 Head domain中断的处理流程

4.4 ipipe中断处理的核心函数__ipipe_dispatch_irq()

4.4.1 函数功能说明

4.4.2 结合代码分析

附录:内核启动时相关代码的执行流程


 

1 让linux变成实时操作系统的方法——xenomai

1.1 概述

https://www.cnblogs.com/wsg1100/p/12822346.html

1.2 xenomai的安装

https://gitlab.denx.de/Xenomai/xenomai/-/wikis/Installing_Xenomai_3

https://www.cnblogs.com/wsg1100/p/12864199.html

1.3 ipipe补丁的下载链接

https://xenomai.org/downloads/ipipe/

 

2 ARM64异常向量表

2.1 简介

        ARM64处理器定义了4个异常级别:0~3。异常级别越高,权限越高。

        通常ARM64处理器在异常级别0执行进程,在异常级别1执行内核。ARM64处理器的异常级别0就是我们常说的用户模式,异常级别1就是我们常说的内核模式。

                                                                                                                                                                                                    《Linux内核深度解析》P403

 

        当异常发生时,处理器需要执行异常处理程序。存储异常处理程序的内存位置称为异常向量,通常把所有异常向量存放在一张表中,称为异常向量表。对于ARM64处理器的异常级别1、2和3,每个异常级别都有自己的异常向量表,异常向量表的起始虚拟地址存放在寄存器VBAR_ELn(向量基准地址寄存器,Vector Base Address Register)中。

        每个异常向量表有16项,分为4组,每组4项,每项长度128字节(可以存放32条指令)。

                                                                                                                                                                                                      《Linux内核深度解析》P405

                                                                                                                                                     《ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile.pdf》P2048

 

2.2 内核启动时异常向量表的设置

在ARM64架构下,在异常级别1的异常向量表中,中断的入口有3个。

<1> 如果处理器处在内核模式(异常级别1),中断的入口是el1_irq

<2> 如果处理器正在用户模式(异常级别0)下执行64位应用程序,中断的入口是el0_irq

<3> 如果处理器正在用户模式(异常级别0)下执行32位应用程序,中断的入口是el0_irq_compat

                                                                                                                    《Linux内核深度解析》P423

2.3 内核模式(异常级别1)的中断入口:el1_irq

2.3.1 打过ipipe补丁的el1_irq汇编函数

文件:arch/arm64/kernel/entry.S

    .align  6
el1_irq:
    kernel_entry 1
    enable_da_f
#if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_IPIPE)
    bl  trace_hardirqs_off
#endif

    irq_handler

#ifdef CONFIG_IPIPE
    cbz     w0, 2f
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_off
#endif
#endif
#ifdef CONFIG_PREEMPT
    ldr w24, [tsk, #TSK_TI_PREEMPT] // get preempt count
    cbnz    w24, 1f             // preempt count != 0
    ldr x0, [tsk, #TSK_TI_FLAGS]    // get flags
    tbz x0, #TIF_NEED_RESCHED, 1f   // needs rescheduling?
    bl  el1_preempt
1:
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_on
#endif
2:
    kernel_exit 1
ENDPROC(el1_irq)

el1_irq汇编函数会调用irq_handler函数来处理中断。

2.3.2 irq_handler

文件:arch/arm64/kernel/entry.S

打过ipipe补丁的irq_handler函数

原始的irq_handler函数

/*
 * Interrupt handling.
 */
    .macro  irq_handler
#ifdef CONFIG_IPIPE
    ldr x1, =handle_arch_irq_pipelined
#else
    ldr_l   x1, handle_arch_irq
#endif
    mov x0, sp
    irq_stack_entry
    blr x1
    irq_stack_exit
    .endm

/*
 * Interrupt handling.
 */
    .macro  irq_handler

 

 


    ldr_l   x1, handle_arch_irq


    mov x0, sp
    irq_stack_entry
    blr x1
    irq_stack_exit
    .endm

原始linux内核下的irq_handler函数会调用handle_arch_irq()函数处理中断;

打过xenomai补丁的内核下的irq_handler函数最终会handle_arch_irq_pipelined()函数处理中断,而handle_arch_irq_pipelined()函数还是会调用handler_arch_irq()函数,如下:

//arch/arm64/kernel/irq.c
#ifdef CONFIG_IPIPE

asmlinkage int handle_arch_irq_pipelined(struct pt_regs *regs)
{
    handle_arch_irq(regs);
    return __ipipe_root_p && !irqs_disabled();
}

#endif

2.3.3 handle_arch_irq()——中断处理程序C语言部分的入口

全局函数指针handle_arch_irq(),是中断处理程序C语言部分的入口。                            《Linux内核深度解析》P421

全局函数指针handle_arch_irq会在GIC-400的中断控制器驱动里被赋值,后面会具体分析。

3 中断控制器GIC-400

3.1 简介

        外围设备不是把中断请求直接发给处理器,而是发给中断控制器,由中断控制器转发给处理器。ARM公司提供了一种标准的中断控制器,称为通用中断控制器(Generic Interrupt Controller,GIC)。

        目前GIC架构规范有了4个版本:v1~v4。GIC v2最多支持8个处理器,GIC v3最多支持128个处理器,GIC v3和GIC v4只支持ARM64处理器。

 

GIC硬件实现形态有两种

<1> 厂商研发自己的ARM处理器,向ARM公司购买GIC授权,ARM公司提供的GIC型号有:GIC-400、GIC-500 和GIC-600。GIC-400遵循GIC v2规范,GIC-500和GIC-600遵循GIC v3规范。

<2> 厂商直接直接向ARM公司购买处理器授权,这些处理器包含了GIC。

                                                                                                                                                                                                              《Linux内核深度解析》P412

                                                                                                                                                                《CoreLink_GIC-400_Generic_Interrupt_Controller_Technical_Reference_Manual.pdf》

 

3.2 中断控制器匹配表和中断控制器驱动初始化

3.2.1 中断控制器匹配表

IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);		//drivers/irqchip/irq-gic.c

将宏IRQCHIP_DECLARE展开后如下:
static const struct of_device_id __of_table_gic_400  __used_section(__irqchip_of_table) = {
    .compatible = "arm,gic-400",
    .data = gic_of_init
}

        在 GIC v2 控制器的驱动程序中,定义了多个类型为 of_device_id 的静态变量,成员 compatible 是驱动程序支持的设备的名称,成员 data 是初始化函数,编译器把这些静态变量放在专用的 section”__irqchip_of_table”里面。我们把section”__irqchip_of_table”称为中断控制器匹配表,里面每个表项的格式是机构体 of_device_id。

                                                                                                                                                                                                                《Linux 内核深度解析》P417

//System.map
ffff000008bf8200 T __irqchip_of_table
ffff000008bf8200 t __of_table_pl390
ffff000008bf82c8 t __of_table_msm_qgic2
ffff000008bf8390 t __of_table_msm_8660_qgic
ffff000008bf8458 t __of_table_cortex_a7_gic
ffff000008bf8520 t __of_table_cortex_a9_gic
ffff000008bf85e8 t __of_table_cortex_a15_gic
ffff000008bf86b0 t __of_table_arm1176jzf_dc_gic
ffff000008bf8778 t __of_table_arm11mp_gic
ffff000008bf8840 t __of_table_gic_400
ffff000008bf8908 t __of_table_gic_v3
ffff000008bf89d0 t irqchip_of_match_end

编译 ARM64 架构的内核时,链接器执行链接脚本 arch/arm64/kernel/vmlinux.lds.S,使用全局变量__irqchip_of_table 存放 section”__irqchip_of_table”的起始地址,也就是中断控制器匹配表的起始地址。

                                                                                                                                                                                                                                  《Linux 内核深度解析》P417

3.2.2 中断控制器初始化过程

start_kernel(); //init/main.c

    → init_IRQ(); //arch/arm64/kernel/irq.c

        → irqchip_init(); //drivers/irqchip/irqchip.c

            → of_irq_init(__irqchip_of_table); //drivers/of/irq.c

                → gic_of_init(); //drivers/irqchip/irq-gic.c

                    → __gic_init_bases(); //drivers/irqchip/irq-gic.c

                        →set_smp_cross_call(gic_raise_softirq);   //arch/arm64/kernel/smp.c;把全局指针__smp_cross_call设置为函数gic_raise_softirq,用来发送软件生成的中断,

                                                                                                                                                                                                               即一个处理器向其他处理器  发送中断。

                        → set_handle_irq(gic_handle_irq);        //kernel/irq/handle.c;把全局函数指针handle_arch_irq设置为gic_handle_irq,该函数是中断处理的C语言入口

                        → gic_init_chip(); //drivers/irqchip/irq-gic.c;初始化中断控制器描述符irq_chip。

                        → gic_init_bases(); //drivers/irqchip/irq-gic.c;为中断控制器分配中断域,初始化中断控制器的各种寄存器。

                    → gicv2m_init(); //drivers/irqchip/irq-gic-v2m.c

 

3.3 GIC-400的中断处理函数gic_handle_irq()

GIC-400驱动程序把全局函数指针handle_arch_irq设置为gic_handle_irq,该函数是中断处理的C语言入口。下面来分析gic_handle_irq()函数。

打过ipipe补丁的gic_handle_irq函数原始的gic_handle_irq函数

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqstat, irqnr;
    struct gic_chip_data *gic = &gic_data[0];
    void __iomem *cpu_base = gic_data_cpu_base(gic);

    do {
        irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
        irqnr = irqstat & GICC_IAR_INT_ID_MASK;

        if (likely(irqnr > 15 && irqnr < 1020)) {
            if (static_branch_likely(&supports_deactivate_key))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            isb(); //保证所有它前面的指令都执行完毕之后,才执行它后面的指令。
            ipipe_handle_domain_irq(gic->domain, irqnr, regs);
            continue;
        }
        if (irqnr < 16) {
            writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            if (static_branch_likely(&supports_deactivate_key))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
            /*
             * Ensure any shared data written by the CPU sending
             * the IPI is read after we've read the ACK register
             * on the GIC.
             *
             * Pairs with the write barrier in gic_raise_softirq
             */
            smp_rmb(); //读内存屏障
            ipipe_handle_multi_ipi(irqnr, regs);
#endif
            continue;
        }
        break;
    } while (1);
}

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqstat, irqnr;
    struct gic_chip_data *gic = &gic_data[0];
    void __iomem *cpu_base = gic_data_cpu_base(gic);

    do {
        irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
        irqnr = irqstat & GICC_IAR_INT_ID_MASK;

        if (likely(irqnr > 15 && irqnr < 1020)) {
            if (static_branch_likely(&supports_deactivate_key))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            isb(); //保证所有它前面的指令都执行完毕之后,才执行它后面的指令。
            handle_domain_irq(gic->domain, irqnr, regs);
            continue;
        }
        if (irqnr < 16) {
            writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
            if (static_branch_likely(&supports_deactivate_key))
                writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
            /*
             * Ensure any shared data written by the CPU sending
             * the IPI is read after we've read the ACK register
             * on the GIC.
             *
             * Pairs with the write barrier in gic_raise_softirq
             */
            smp_rmb(); //读内存屏障
            handle_IPI(irqnr, regs);
#endif
            continue;
        }
        break;
    } while (1);
}

 

在原始的gic_handle_irq函数里,如果irq number在15 1020之间,则调用handle_domain_irq 处理。
irq number 如果小于15的话,则属于cpu之间的中断,而不是hw发送过来的中断,调用handle_ipi处理。

                                                                                                                                                                   https://blog.csdn.net/tiantao2012/article/details/52232490

4 深入分析ipipe中断处理

4.1 结构体struct ipipe_domain和它的实例化对象ipipe_root_domain / ipipe_head_domain

4.1.1 简介

        结构体struct ipipe_domain用来区分中断是实时中断还是普通的linux中断域。有2个实例化对象:ipipe_root_domain和ipipe_head_domain。其中ipipe_root_domain表示普通的linux中断域,ipipe_head_domain表示实时中断域。

机构体struct ipipe_domain的实现如下:

//include/linux/ipipe_domain.h
struct ipipe_domain {
    int context_offset;
    struct ipipe_irqdesc {
        unsigned long control;
        ipipe_irq_ackfn_t ackfn;
        ipipe_irq_handler_t handler;
        void *cookie;
    } ____cacheline_aligned irqs[IPIPE_NR_IRQS];
    const char *name;
    struct mutex mutex;
};

 

        在申请中断的时候,通过把ipipe_root_domain或者ipipe_head_domain传递给ipipe_request_irq()函数的第一个参数来指明当前申请的中断是普通linux中断还是实时中断。ipipe_request_irq()的参数如下:

int ipipe_request_irq(struct ipipe_domain *ipd,
        unsigned int irq,
        ipipe_irq_handler_t handler,
        void *cookie,
        ipipe_irq_ackfn_t ackfn);

 

4.1.2 全局变量:ipipe_head_domain / ipipe_root_domain / ipipe_root

3个变量定义如下

#define ipipe_root_domain (&ipipe_root)		//include/linux/ipipe_domain.h

//kernel/ipipe/core.c
struct ipipe_domain ipipe_root;
EXPORT_SYMBOL_GPL(ipipe_root);

struct ipipe_domain *ipipe_head_domain = &ipipe_root;
EXPORT_SYMBOL_GPL(ipipe_head_domain);

        默认情况下,ipipe_head_domain == ipipe_root_domain = &ipipe_root。当调用了ipipe_register_head()函数注册了Head domain后, ipipe_head_domain会被赋值为新注册的Head domain(当前系统下是xnsched_realtime_domain)。

4.2 Root domain的初始化和中断的处理

4.2.1 Root domain初始化大致流程

start_kernel();                                     //init/main.c

    → __ipipe_init_early();                   //kernel/ipipe/core.c

        → init_stage();                            //kernel/ipipe/core.c

    → __ipipe_init();                              //kernel/ipipe/core.c

        → __ipipe_enable_pipeline();      //arch/arm64/kernel/ipipe.c

__ipipe_enable_pipeline()实现如下:

/*
 * __ipipe_enable_pipeline() -- We are running on the boot CPU, hw
 * interrupts are off, and secondary CPUs are still lost in space.
 */
void __ipipe_enable_pipeline(void)
{
    unsigned long flags;
    unsigned int irq;

    flags = ipipe_critical_enter(NULL);

    /* virtualize all interrupts from the root domain. */
    for (irq = 0; irq < IPIPE_NR_ROOT_IRQS; irq++)
        ipipe_request_irq(ipipe_root_domain,
                  irq,
                  (ipipe_irq_handler_t)__ipipe_do_IRQ,
                  NULL, NULL);

#ifdef CONFIG_SMP
    __ipipe_ipis_request();
#endif /* CONFIG_SMP */

    ipipe_critical_exit(flags);
}

        __ipipe_enable_pipeline()函数把0~1023号中断的中断处理函数全部绑定为__ipipe_do_IRQ(),调用__ipipe_ipis_request()函数将编号从 1024~1033的中断的中断处理函数设置为__ipipe_do_IPI()。

        因为__ipipe_enable_pipeline()里面ipipe_request_irq()的第一个参数是ipipe_root_domain,所以,这里处理的全是普通的linux中断。

4.2.2 Root domian中断的处理流程

el1_irq						//arch/arm64/kernel/entry.S
    → handle_arch_irq_pipelined();		//arch/arm64/kernel/irq.c
        → gic_handle_irq();			//drivers/irqchip/irq-gic.c
            → ipipe_handle_domain_irq();	//arch/arm64/include/asm/ipipe.h
                → __ipipe_grab_irq();          	//arch/arm64/kernel/ipipe.c
                    → __ipipe_dispatch_irq(irq, 0);  	//kernel/ipipe/core.c
                        → __ipipe_set_irq_pending();
                        → __ipipe_sync_pipeline();

Root domain中断处理的第一步——中断记录(IRQ log)

        __ipipe_set_irq_pending()函数负责将当前的中断编号记录到每CPU变量的irqpend_map 成员里。

//kernel/ipipe/core.c
/* Must be called hw IRQs off. */
void __ipipe_set_irq_pending(struct ipipe_domain *ipd, unsigned int irq)
{
    struct ipipe_percpu_domain_data *p = ipipe_this_cpu_context(ipd);
    int l0b = irq / BITS_PER_LONG;

    IPIPE_WARN_ONCE(!hard_irqs_disabled());

    if (likely(!test_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control))) {
        __set_bit(irq, p->irqpend_map);
        __set_bit(l0b, &p->irqpend_0map);
    } else
        __set_bit(irq, p->irqheld_map);

    p->irqall[irq]++;
}
EXPORT_SYMBOL_GPL(__ipipe_set_irq_pending);

Root domain中断处理的第二步——执行对应中断的中断处理函数

__ipipe_sync_pipeline();
    -> __ipipe_sync_stage();
        -> __ipipe_do_sync_stage();
            -> __ipipe_next_irq();          //取出irqpend_map里记录的所有中断
            -> ipd->irqs[irq].handler();    //执行对应中断的处理函数

 

4.3 Head domain的初始化和中断的处理

4.3.1 Head domain初始化大致流程

xenomai_init();                     		//kernel/xenomai/init.c
    → mach_setup()                              //kernel/xenomai/init.c
        → ipipe_register_head();                //kernel/ipipe/core.c
            → init_head_stage();               	//kernel/ipipe/core.c

内核启动时会有如下信息:

# dmesg | grep -Ei 'xeno|pipe'
[ 0.000000] IPIPE_CRITICAL_IPI = 1031
[ 0.000166] Interrupt pipeline (release #5)[ 0.321553] [Xenomai] scheduling class idle registered.
[ 0.321559] [Xenomai] scheduling class rt registered.
[ 0.321634] IPIPE_CRITICAL_IPI = 1031
[ 0.321641] I-pipe: head domain Xenomai registered.
[ 0.323029] [Xenomai] Cobalt v3.1

 

4.3.2 Head domain中断的处理流程

el1_irq						//arch/arm64/kernel/entry.S
    → handle_arch_irq_pipelined();		//arch/arm64/kernel/irq.c
        → gic_handle_irq();			//drivers/irqchip/irq-gic.c
            → ipipe_handle_domain_irq();	//arch/arm64/include/asm/ipipe.h
                → __ipipe_grab_irq();          	//arch/arm64/kernel/ipipe.c
                    → __ipipe_dispatch_irq(irq, 0);  	//kernel/ipipe/core.c
                        → dispatch_irq_head();		//kernel/ipipe/core.c
                            → head->irqs[irq].handler(irq, head->irqs[irq].cookie);

4.4 ipipe中断处理的核心函数__ipipe_dispatch_irq()

4.4.1 函数功能说明

When interrupt pipelining is enabled, IRQs are first delivered to the pipeline entry point via a call to the generic __ipipe_dispatch_irq() routine. Before this happens, the event has been propagated through the arch-specific code for handling an IRQ.

If an out-of-band handler exists for the interrupt received, :c:func:`__ipipe_dispatch_irq` invokes it immediately, after switching the execution context to the head stage if not current yet.

Otherwise, if the execution context is currently over the root stage and unstalled, the pipeline core delivers it immediately to the in-band handler.

In all other cases, the interrupt is only set pending into the per-CPU log, then the interrupt frame is left.

                                                                                                                                                                         《kernel_src_dir/Documentation/ipipe.rst》(kernel打过ipipe补丁后才会有这个文件)

        I-Pipe(interrupt Pipeline)分发Linux和Xenomai之间的中断,并以Domain优先级顺序传递中断。I-Pipe传递中断如下图所示,对于实时内核注册的中断,会直接得到处理。对于linux的中断,先将中断记录在i-log,等实时任务让出CPU后,linux得到运行,该中断才得到处理。         https://www.cnblogs.com/wsg1100/p/12833126.html

4.4.2 结合代码分析

//kernel/ipipe/core.c
void __ipipe_dispatch_irq(unsigned int irq, int flags) /* hw interrupts off */
{
    ......
    /* 时钟节拍中断优先处理
     * Sticky interrupts must be handled early and separately, so
     * that we always process them on the current domain.
     */
    ipd = __ipipe_current_domain;
    control = ipd->irqs[irq].control;
    if (control & IPIPE_STICKY_MASK)
        goto log;


    ipd = ipipe_head_domain;        //切换到Head domain
    control = ipd->irqs[irq].control;
    if (ipd == ipipe_root_domain)
        /*
         * The root domain must handle all interrupts, so
         * testing the HANDLE bit would be pointless.
         */
        goto log;

    if (control & IPIPE_HANDLE_MASK) {
        if (unlikely(flags & IPIPE_IRQF_NOSYNC))
            __ipipe_set_irq_pending(ipd, irq);
        else
            dispatch_irq_head(irq);       //如果是Head domain的中断,执行这一行。
        return;
    }

    ipd = ipipe_root_domain;            //如果不是Head domain的中断,切换到Root domain
log:
    __ipipe_set_irq_pending(ipd, irq);  //先将Root domain的中断进行记录(i-log)
    ......
sync:
    __ipipe_sync_pipeline(ipipe_head_domain);    //执行所有记录中断的处理函数。请看前面的4.2.2小节
}

 

 

 

附录:内核启动时相关代码的执行流程

 

start_kernel();

 

__ipipe_init_early();

 

 

设置Root domain的关键数据

init_IRQ();

→ irqchip_init();

→ of_irq_init();

→ gic_of_init();

 

__gic_init_bases();

set_smp_cross_call();

全局指针__smp_cross_call 设置为函数 gic_raise_softirq,用来发送软件生成的中断,即一个处理器向其他处理器发送中断。

set_handle_irq();

全局函数指针 handle_arch_irq 设置为 gic_handle_irq,该函数是中断处理的 C 语言入口

gic_init_chip();

初始化中断控制器描述符 irq_chip。

gic_init_bases();

为中断控制器分配中断域,初始化中断控制器的各种寄存器。

 

 

__ipipe_init();

 

__ipipe_enable_pipeline();

 

 

(1)Root domain中编号从0~1023(hardware interrupts)的中断的中断处理函数都设置为__ipipe_do_IRQ()。

 

(2)调用__ipipe_ipis_request()函数将编号从1024~1033(virtual interrupts)的中断的中断处理函数设置为__ipipe_do_IPI()

 

 

 

 

rest_init();

...…

→ do_basic_setup();

→ do_initcalls();

→ xenomai_init();

xnsched_register_classes();

xnsched_register_class();

注册调度类

mach_setup()

ipipe_register_head()

调用ipipe_register_head()函数注册Head domain(xnsched_realtime_domain)。

sys_init()

xnsched_init_all()

初始化调度功能,并注册IPI中的调度中断

 

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值