国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

riscv 啟動流程分析

這篇具有很好參考價值的文章主要介紹了riscv 啟動流程分析。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

riscv Linux 目錄分布

通過文章 將 Linux 移植到新的處理器架構,第 1 部分:基礎 可知,我們進行一個新的處理器架構的移植,需要做到以下 3 點:
1、確定這是不是一個新的架構移植。
2、了解我們要移植的硬件。
3、了解內核的基本概念。
在riscv已經被移植支持的情況下,我們現在要做的是分析,Linux內核是如何支持riscv架構的。

- configs/:支持系統(tǒng)的默認配置 (i.e. *_defconfig files)
- include/asm/ :Linux源碼內部使用的頭文件
- include/uapi/asm: 對于要導出到用戶空間(例如 libc )的頭文件
- kernel/:通用內核管理
- lib/:優(yōu)化過的那套函數 (e.g. memcpy(), memset(), etc.)
- mm/:內存管理
  • configs文件中主要是一些配置文件,編譯時可以選擇默認配置進行編譯,配置項較多,我們暫時不進行分析。

  • include/asm/ 目錄下定義了大量頭文件,用于內核編譯時使用。

  • include/uapi/asm目錄下定義了很多結構體以及宏定義,可以供應用層使用,可以更方便的與內核統(tǒng)一使用一些定義好的數據。

  • kernel/目錄下有許多C文件,包含CPU獲取id,信號,中斷,ops,smp,time等功能。

  • lib/目錄下供9個文件,其中5個為匯編實現的代碼。用于底層基礎函數的實現。mm/目錄下進行內存的管理,包括虛擬內存分配,頁錯誤處理,cache刷新等。

架構相關的includ目錄存在于架構相關文件夾,非架構相關的存在與include/asm-gereric目錄下。

內核第一個運行的地方——head.S

kernel_entry*
 start_kernel
    setup_arch*
     trap_init*
        mm_init
            mem_init*
        init_IRQ*
        time_init*
        rest_init
            kernel_thread
            kernel_thread
            cpu_startup_entry

內核的整體啟動流程如上所示,我們從代碼中進行分析,具體內核在啟動過程中做了什么。
首先我們找到head.S文件。

ENTRY(_start_kernel)
        /* Mask all interrupts */
        csrw CSR_IE, zero
        csrw CSR_IP, zero

在內核啟動時,一開始就關閉了所有中斷。Technical Report UCB/EECS-2016-129 一文中講了,CSR 的寄存器分布。
關閉中斷后,關閉了 FPU 功能,以檢測內核空間內非法使用的定位點。后面是通過一系列的宏定義進行一些環(huán)境的配置,使得一些功能能夠跑起來。
這些宏定義有

ENTRY(_start_kernel)
        關閉所有中斷
#ifdef CONFIG_RISCV_M_MODE
        /* 刷新icache */
        /* 復位所有寄存器,除了 ra, a0, a1 */
        /*
        設置一個PMP以允許訪問所有內存。有些機器可能不會實現pmp,因此我們設置了一個快速陷阱處理程序來跳過接觸任何陷阱上的pmp。
         */
        /*
        a0中的hardtid稍后才會出現,我們沒有固件可以處理它。
         */
#endif /* CONFIG_RISCV_M_MODE */
        /* 加載全局指針 */
        /*
         *關閉FPU,檢測內核空間中非法使用浮點數的情況
         */
#ifdef CONFIG_RISCV_BOOT_SPINWAIT
        /* 彩票系統(tǒng)只需要自旋等待啟動方法 */
#ifndef CONFIG_XIP_KERNEL
        /* 選擇一個hart來運行主啟動序列 */
#else
        /* Hart_lottery在flash中包含一個神奇的數字 */
        /* 如果在RAM中沒有設置hart_lottery,這是第一次 */
#endif /* CONFIG_XIP */
#endif /* CONFIG_RISCV_BOOT_SPINWAIT */
#ifdef CONFIG_XIP_KERNEL
/*恢復a-的復制*/
#endif
#ifndef CONFIG_XIP_KERNEL
        /*為展開的無ELF的鏡像清除BSS段 */
#endif
        /* 保存hart ID和DTB物理地址*/
        /* 初始化頁表并重新定位到虛擬地址 */
#ifdef CONFIG_BUILTIN_DTB
#else
#endif /* CONFIG_BUILTIN_DTB */
#ifdef CONFIG_MMU
#endif /* CONFIG_MMU *
        /* Restore C environment */
#ifdef CONFIG_KASAN
#endif
        /* 啟動內核 */
#if CONFIG_RISCV_BOOT_SPINWAIT
        /* 設置陷阱向量永遠旋轉以幫助調試 */
        /*
這個人沒有中彩票,所以我們等待中獎的人在啟動過程中走得足夠遠,它應該繼續(xù)。
         */
        /* FIXME:我們應該WFI,以節(jié)省一些能源在這里。*/
#endif /* CONFIG_RISCV_BOOT_SPINWAIT */
END(_start_kernel)

內核運行的第一個C文件——init/main.c

第一個運行的C語言函數為start_kernel,在該函數中進行內核的第一個線程的創(chuàng)建。在創(chuàng)建之前,會執(zhí)行架構相關的函數,從而適配硬件。

kernel_entry*
 start_kernel
    setup_arch*
     trap_init*
        mm_init
            mem_init*
        init_IRQ*
        time_init*
        rest_init
            kernel_thread
            kernel_thread
            cpu_startup_entry

setup_arch()

首先分析 setup_arch 這個函數,該函數屬于架構相關函數,對應的文件在 arch/riscv/kernel 文件下。

parse_dtb()

這個函數首先要執(zhí)行的是解析設備樹,這說明 riscv 像 arm 一樣,使用設備樹進行設備驅動的管理,我們查看 x86 架構下的 setup_arch 則無設備樹相關的配置。設備樹解析函數通過 drivers/of 目錄下的設備樹驅動進行解析,并取出設備樹中 model 名稱。
設備樹解析調用的函數時 parse_dtb,函數中調用了一個全局變量 dtb_early_va,這個變量是在 head.S 中進行的賦值,head.S 中調用該函數時,提前將變量放置于寄存器 a0 中,用于 C 函數的傳參。
設備樹地址傳參代碼:

#ifdef CONFIG_BUILTIN_DTB
        la a0, __dtb_start
        XIP_FIXUP_OFFSET a0
#else
        mv a0, s1
#endif /* CONFIG_BUILTIN_DTB */
        call setup_vm

setup_initial_init_mm()

設備樹解析完成后,進行了早期內存的初始化,給出了代碼段的起始與結束位置,數據段的結束位置,堆地址結束位置。

[0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[0.000000] Machine model: riscv-virtio,qemu
[0.000000]start_code=0x80002000,end_code=0x806ae52c,end_data=0x812d2a00,brk=0x81322000

通過以上打印信息可知各個段的分配地址。CPU 內部的 RAM 尋址需要預留一些空間,所有 ram 起始地址就從 0x80000000 開始,地址空間分配完成之后將 boot_command_line 地址傳出,供后續(xù)使用。

early_ioremap_setup()

早期 ioremap 初始化,將 I/O 的物理地址映射到虛擬地址。當 CPU 讀取一段物理地址時,它可以讀取到映射了 I/O 設備的物理 RAM 區(qū)域。ioremap 就是用來把設備內存映射到內核地址空間的。
該函數是一個架構不相關的函數,位于 mm/early_ioremap.c,

jump_label_init()

架構無關函數,位于 kernel 目錄下,初始化 jump-label 子系統(tǒng),jump-label 用于取消 if 判斷分支,通過運行時修改代碼,來提高執(zhí)行的效率。

parse_early_param()

架構無關函數,解析早期傳入的參數。

efi_init()

paging_init()

完成系統(tǒng)分頁機制的初始化工作, 建立頁表, 從而內核可以完成虛擬內存的映射和轉換工作,這一個函數執(zhí)行完成之后,就可以通過虛擬地址來訪問實際的物理地址了。

misc_mem_init()

  • 測試 ram 是否正常
  • numa 架構初始化
  • 內存模型 sparse 初始化
  • 初始化 zone,用于管理物理內存地址區(qū)域
  • 保留內核崩潰時內核信息導出時所用的內存區(qū)域。
  • 打印內存分配情況 __memblock_dump_all(),實際未輸出

init_resources()

初始化內存資源,把系統(tǒng)的 ram 以及其他需要保留的 ram 進行保留

sbi_init()

函數相關打印如下,具體作用暫未分析

[    0.000000] SBI specification v0.2 detected
[    0.000000] SBI implementation ID=0x1 Version=0x9
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI HSM extension detected

kasan_init()

初始化 kasan 動態(tài)監(jiān)測內存錯誤的工具,初始化完成之后,可以在內存使用越界或者釋放后訪問時,產生出錯報告,幫助分析內核異常。

setup_smp()

配置 SMP 系統(tǒng),使芯片可以多核運行。

riscv_fill_hwcap()

打印信息如下:
[ 0.000000] riscv: ISA extensions acdfimsu
[ 0.000000] riscv: ELF capabilities acdfim
具體作用暫未分析。

trap_init()

未定義該函數

mem_init()

mem_init() 是架構相關函數,我們分析一下該函數具體做了哪些工作。

void __init mem_init(void)
{
#ifdef CONFIG_FLATMEM
	BUG_ON(!mem_map);
#endif /* CONFIG_FLATMEM */

#ifdef CONFIG_SWIOTLB
	if (swiotlb_force == SWIOTLB_FORCE ||
	    max_pfn > PFN_DOWN(dma32_phys_limit))
		swiotlb_init(1);//軟件DMA映射,解決部分DMA外設無法訪問高地址內存的問題。
	else
		swiotlb_force = SWIOTLB_NO_FORCE;
#endif
	memblock_free_all();//釋放空閑頁面給伙伴分配器

	print_vm_layout();//打印內存分布情況
}

init_IRQ()

中斷初始化是一個架構相關的函數,首先從設備樹中取出中斷控制器(()interrupt-controller)這一節(jié)點。
通過命令將 qemu 的 DTB 文件導出.

sudo env PATH=/labs/linux-lab/boards/riscv64/virt/bsp/qemu/v6.0.0/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  qemu-system-riscv64 -bios /labs/linux-lab/boards/riscv64/virt/bsp/bios/opensbi/generic/fw_jump.elf -M virt,dumpdtb=my.dtb  -m 128M -net nic,model=virtio -net tap -device virtio-net-device,netdev=net0,mac=$(tools/qemu/macaddr.sh) -netdev tap,id=net0 -smp 4 -kernel /labs/linux-lab/build/riscv64/virt/linux/v5.17/arch/riscv/boot/Image -no-reboot  -drive if=none,file=/labs/linux-lab/build/riscv64/virt/bsp/root/2019.05/rootfs.ext2,format=raw,id=virtio-vda -device virtio-blk-device,drive=virtio-vda -nographic   -append "route=$(ifconfig br0 | grep 'inet ' | tr -d -c '^[0-9. ]' | awk '{print $1}') iface=eth0 rw fsck.repair=yes rootwait root=/dev/vda console=ttyS0"

并將 dtb 文件反編譯成 dts 文件。

dtc -I dtb -O dts -o qemu-virt.dts my.dtb

init_IRQ()->irqchip_init()->of_irq_init()->
在 of_irq_init() 中遍歷設備樹,通過 __irq_of_table 進行匹配,匹配成功后進行初始化 irq。
查看設備樹,找到 interrupt-controller 的 compatible 為 riscv,cpu-intc,

cpu@0 {
        phandle = <0x07>;
        device_type = "cpu";
        reg = <0x00>;
        status = "okay";
        compatible = "riscv";
        riscv,isa = "rv64imafdcsu";
        mmu-type = "riscv,sv48";

        interrupt-controller {
                #interrupt-cells = <0x01>;
                interrupt-controller;
                compatible = "riscv,cpu-intc";
                phandle = <0x08>;
        };
};

通過匹配,最終調用的驅動是driver/irqchip/irq-riscv-intc.c

static int __init riscv_intc_init(struct device_node *node,
                                  struct device_node *parent)
{
        int rc, hartid;
        pr_info("[nfk test] %s-%s-%d\r\n",__FILE__,__FUNCTION__,__LINE__);
        hartid = riscv_of_parent_hartid(node);//獲取CPU id
        if (hartid < 0) {
                pr_warn("unable to find hart id for %pOF\n", node);
                return 0;
        }
        else
        {
                pr_info("[nfk test] get hartid=%d\r\n",hartid);
        }

        /*
         * The DT will have one INTC DT node under each CPU (or HART)
         * DT node so riscv_intc_init() function will be called once
         * for each INTC DT node. We only need to do INTC initialization
         * for the INTC DT node belonging to boot CPU (or boot HART).
         */
        if (riscv_hartid_to_cpuid(hartid) != smp_processor_id())
                return 0;
                //每一個CPU都會有其DT NODE,當前我們只需要初始化
                //boot CPU 的DT NODE

        intc_domain = irq_domain_add_linear(node, BITS_PER_LONG,
                                            &riscv_intc_domain_ops, NULL);//向系統(tǒng)注冊一個irq domain,
        //最終調用__irq_domain_add(),進行內存申請,domain回調函數配置,此處僅完成了irq_domain的注冊,后面的中斷映射關系還需要在具體驅動中實現。
        if (!intc_domain) {//intc_domain就是interrupt-controller的軟件抽象
                pr_err("unable to add IRQ domain\n");
                return -ENXIO;
        }

        rc = set_handle_irq(&riscv_intc_irq);//配置中斷處理函數
        if (rc) {
                pr_err("failed to set irq handler\n");
                return rc;
        }

        cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING,
                          "irqchip/riscv/intc:starting",
                          riscv_intc_cpu_starting,
                          riscv_intc_cpu_dying);//對熱插拔函數進行配置

        pr_info("%d local interrupts mapped\n", BITS_PER_LONG);

        return 0;
}

[ 0.000000] riscv-intc: [nfk test] drivers/irqchip/irq-riscv-intc.c-riscv_intc_init-99
[ 0.000000] riscv-intc: get hartid=0
[ 0.000000] riscv-intc: hartid 0,cpuid 1 not smp processor_id
[ 0.000000] riscv-intc: [nfk test] drivers/irqchip/irq-riscv-intc.c-riscv_intc_init-99
[ 0.000000] riscv-intc: get hartid=1
[ 0.000000] riscv-intc: hartid 1,cpuid 2 not smp processor_id
[ 0.000000] riscv-intc: [nfk test] drivers/irqchip/irq-riscv-intc.c-riscv_intc_init-99
[ 0.000000] riscv-intc: get hartid=2
[ 0.000000] riscv-intc: hartid 2,cpuid 3 not smp processor_id
[ 0.000000] riscv-intc: [nfk test] drivers/irqchip/irq-riscv-intc.c-riscv_intc_init-99
[ 0.000000] riscv-intc: get hartid=3
[ 0.000000] riscv-intc: 64 local interrupts mapped

中斷初始化的打印如上所示。

time_init()

架構相關函數time_init(),

void __init time_init(void)
{
        struct device_node *cpu;
        u32 prop;
        /*設備樹中解析CPU,并且讀取他的timebase-frequency*/
        cpu = of_find_node_by_path("/cpus");
        if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop))
                panic(KERN_WARNING "RISC-V system with no 'timebase-frequency' in DTS\n");
        of_node_put(cpu);//減少引用計數
        riscv_timebase = prop;

        lpj_fine = riscv_timebase / HZ; 
        //遍歷設備樹,進行時鐘初始化,類似于of_irq_init(),linux-lab-disk中的虛擬開發(fā)板當前匹配為空
        of_clk_init(NULL);

        timer_probe();
}

timer_probe()中遍歷設備樹,通過__timer_of_table進行匹配,匹配成功后進行初始化timer。

void __init timer_probe(void)
{
        struct device_node *np;
        const struct of_device_id *match;
        of_init_fn_1_ret init_func_ret;
        unsigned timers = 0;
        int ret;
        pr_info("[nfk test] %s-%s-%d\n",__FILE__,__FUNCTION__,__LINE__);
        for_each_matching_node_and_match(np, __timer_of_table, &match) {//遍歷設備樹,匹配timer
                if (!of_device_is_available(np))
                        continue;

                pr_info("[nfk test] %s-%s-%d\n",__FILE__,__FUNCTION__,__LINE__);
                init_func_ret = match->data;

                ret = init_func_ret(np);//timer初始化
                if (ret) {
                        if (ret != -EPROBE_DEFER)
                                pr_err("Failed to initialize '%pOF': %d\n", np,
                                       ret);
                        continue;
                }

                timers++;
        }

        timers += acpi_probe_device_table(timer);//注冊timer

        if (!timers)
                pr_crit("%s: no matching timers found\n", __func__);
        pr_info("[nfk test] %s-%s-%d\n",__FILE__,__FUNCTION__,__LINE__);
}

添加調試信息,打印如下:

[ 0.000000] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-23
[ 0.000000] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-28
[ 0.000000] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-28
[ 0.000000] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-28
[ 0.000000] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-28
[ 0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [3]
[ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[ 0.000126] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns
[ 0.002668] [nfk test] drivers/clocksource/timer-probe.c-timer_probe-46

通過以上信息,可知,匹配到了4次timer,通過中間的相關打印信息,找到驅動drivers/clocksource/timer-riscv.c。

static int __init riscv_timer_init_dt(struct device_node *n)
{
        int cpuid, hartid, error;
        struct device_node *child;
        struct irq_domain *domain;

        hartid = riscv_of_processor_hartid(n);//獲取node所在的hartid
        if (hartid < 0) {
                pr_warn("Not valid hartid for node [%pOF] error = [%d]\n",
                        n, hartid);
                return hartid;
        }

        cpuid = riscv_hartid_to_cpuid(hartid);//獲取cpu id
        if (cpuid < 0) {
                pr_warn("Invalid cpuid for hartid [%d]\n", hartid);
                return cpuid;
        }

        if (cpuid != smp_processor_id())
                return 0;//判斷是否未boot cpu

        domain = NULL;
        child = of_get_compatible_child(n, "riscv,cpu-intc");
        if (!child) {//獲取中斷的domain
                pr_err("Failed to find INTC node [%pOF]\n", n);
                return -ENODEV;
        }
        domain = irq_find_host(child);
        of_node_put(child);
        if (!domain) {
                pr_err("Failed to find IRQ domain for node [%pOF]\n", n);
                return -ENODEV;
        }

        riscv_clock_event_irq = irq_create_mapping(domain, RV_IRQ_TIMER);//建立中斷映射
        if (!riscv_clock_event_irq) {
                pr_err("Failed to map timer interrupt for node [%pOF]\n", n);
                return -ENODEV;
        }

        pr_info("%s: Registering clocksource cpuid [%d] hartid [%d]\n",
               __func__, cpuid, hartid);
        error = clocksource_register_hz(&riscv_clocksource, riscv_timebase);//注冊timer
        if (error) {
                pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
                       error, cpuid);
                return error;
        }
        sched_clock_register(riscv_sched_clock, 64, riscv_timebase);

        error = request_percpu_irq(riscv_clock_event_irq,
                                    riscv_timer_interrupt,
                                    "riscv-timer", &riscv_clock_event);
                                    //注冊中斷處理函數
        if (error) {
                pr_err("registering percpu irq failed [%d]\n", error);
                return error;
        }

        error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
                         "clockevents/riscv/timer:starting",
                         riscv_timer_starting_cpu, riscv_timer_dying_cpu);//熱插拔配置
        if (error)
                pr_err("cpu hp setup state failed for RISCV timer [%d]\n",
                       error);
        return error;
}

關于設備樹匹配函數分析

以下函數是進行循環(huán)匹配的函數。

for_each_matching_node_and_match(np, __timer_of_table, &match)
for_each_matching_node_and_match(np, __irqchip_of_table, &match)

我們找到他的根本調用,參數描述如下,分別是設備樹節(jié)點,要掃描的結構體,匹配到的結構體。

/**
 * of_find_matching_node_and_match - Find a node based on an of_device_id
 *                                   match table.
 * @from:       The node to start searching from or NULL, the node
 *              you pass will not be searched, only the next one
 *              will; typically, you pass what the previous call
 *              returned. of_node_put() will be called on it
 * @matches:    array of of device match structures to search in
 * @match:      Updated to point at the matches entry which matched
 *
 * Return: A node pointer with refcount incremented, use
 * of_node_put() on it when done.
 */

搞清楚入參之后,我們找一下__timer_of_table從何處定義。

#define TIMER_OF_DECLARE(name, compat, fn) \
        OF_DECLARE_1_RET(timer, name, compat, fn)

下一層宏定義

#define OF_DECLARE_1_RET(table, name, compat, fn) \ 
                _OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret)

下一層宏定義

#define _OF_DECLARE(table, name, compat, fn, fn_type)                   \
        static const struct of_device_id __of_table_##name              \
                __used __section("__" #table "_of_table")               \
                __aligned(__alignof__(struct of_device_id))             \
                 = { .compatible = compat,                              \
                     .data = (fn == (fn_type)NULL) ? fn : fn  }

所以我們根據宏定義TIMER_OF_DECLARE尋找與設備樹節(jié)點可以匹配的驅動,我們首先看一下傳入的node的信息。

truct device_node {
        const char *name;
        phandle phandle;
        const char *full_name;
        struct fwnode_handle fwnode;

        struct  property *properties;
        struct  property *deadprops;    /* removed properties */
        struct  device_node *parent;
        struct  device_node *child;
        struct  device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
        struct  kobject kobj;
#endif
        unsigned long _flags;
        void    *data;
#if defined(CONFIG_SPARC)
        unsigned int unique_id;
        struct of_irq_controller *irq_trans;
#endif
};

最終調用匹配的函數如下所示:

static int __of_device_is_compatible(const struct device_node *device,
                                     const char *compat, const char *type, const char *name)
{
        struct property *prop;
        const char *cp;
        int index = 0, score = 0;

        /* Compatible match has highest priority */
        if (compat && compat[0]) {
                prop = __of_find_property(device, "compatible", NULL);
                for (cp = of_prop_next_string(prop, NULL); cp;
                     cp = of_prop_next_string(prop, cp), index++) {
                        if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
                                score = INT_MAX/2 - (index << 2);
                                break;
                        }
                }
                if (!score)
                        return 0;
        }

        /* Matching type is better than matching name */
        if (type && type[0]) {
                if (!__of_node_is_type(device, type))
                        return 0;
                score += 2;
        }

        /* Matching name is a bit better than not */
        if (name && name[0]) {
                if (!of_node_name_eq(device, name))
                        return 0;
                score++;
        }

        return score;
}

可以看到,能夠搜到的
drivers/clocksource/timer-riscv.c
TIMER_OF_DECLARE(riscv_timer, “riscv”, riscv_timer_init_dt);文章來源地址http://www.zghlxwxcb.cn/news/detail-423519.html

到了這里,關于riscv 啟動流程分析的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Linux - 如何通過端口號確定程序主目錄?

    今天遇到一個項目想看它的 Log,因為日志文件在它主目錄下,但是又不知道主目錄在哪?但是知道它的端口號……所以就想了下是否可以通過端口號摸索出它的主目錄?! 我的項目是?dolphinscheduler,端口號:123456 為例 此時,我們發(fā)現?/data/dolphinscheduler 這條信息,這其實就

    2024年02月12日
    瀏覽(16)
  • Android SystemServer 啟動流程分析

    Android SystemServer 啟動流程分析

    和你一起終身學 習,這里是程序員Android 經典好文推薦,通過閱讀本文,您將收獲以下知識點: 一、SystemServer 啟動的服務有哪些 二、SystemServer啟動總體流程概述 三、SystemServer 如何啟動,是誰啟動的? 四、 SystemServer 啟動入門 main 方法 五、SystemServer Run 方法初始與啟動 六、

    2024年02月13日
    瀏覽(24)
  • SpringBoot的啟動流程源碼分析

    SpringBoot的啟動流程源碼分析

    new 一個IOC容器,傳入配置好的文件xml,在這個地方打bug 在這個debug的棧幀中,下面幾個不用看,直接看到getBean 內容如圖所示,name傳的就是我們在xml的bean標簽的id,這里是instanceA 進入到doGetBean后,因為我是從IOC初始化容器debug進來的所以第一次通過。 Object sharedInstance = getSingl

    2024年01月24日
    瀏覽(18)
  • STM32啟動詳細流程分析(一)

    STM32啟動詳細流程分析(一)

    ? 大家不妨設想一下,cpu 的工作是什么,cpu 是沒有主觀意識的,它只會按照特定的指令執(zhí)行相應的操作,用專業(yè)術語來說就是: 取指 - 譯碼 - 執(zhí)行 ,譯碼和執(zhí)行肯定是在 cpu 內部進行操作的,并且前提是已經取到了指令。那現在問題來了,指令在哪? cpu上電復位后執(zhí)行的第

    2024年02月12日
    瀏覽(26)
  • 分布式事務-TCC案例分析流程圖

    分布式事務-TCC案例分析流程圖

    防止cancel方法在最后執(zhí)行出現問題,用戶收到提示已經退款成功但是由于cancel過慢或者出現問題(雖然最后會重試成功但是用戶體驗很差),可以做以下的業(yè)務sql模型優(yōu)化(增加一個凍結金額)。

    2024年02月07日
    瀏覽(24)
  • SpringBoot3.X源碼分析(啟動流程)

    SpringBoot3.X源碼分析(啟動流程)

    1 啟動入口 靜態(tài)輔助類,可用于運行使用默認配置(即我們添加的一系列注解)的指定源的 SpringApplication 。 primarySource - 要載入的主要源,即指定源,這里為傳入的 Application.class ?Class? :泛型決定了任何類都可以傳入 args - 應用程序參數(通常從main方法傳遞) 返回:正在運

    2024年01月23日
    瀏覽(20)
  • android framework之Applicataion啟動流程分析

    android framework之Applicataion啟動流程分析

    Application啟動流程框架分析 啟動方式一:通過Launcher啟動app 啟動方式二:在某一個app里啟動第二個app的Activity. 以上兩種方式均可觸發(fā)app進程的啟動。但無論哪種方式,最終通過通過調用AMS的startActivity()來啟動application的。 ?? 根據上圖分析, 要啟動一個Application,需要涉及五

    2024年02月11日
    瀏覽(26)
  • SpringBoot配置外部Tomcat項目啟動流程源碼分析

    SpringBoot配置外部Tomcat項目啟動流程源碼分析

    SpringBoot應用默認以Jar包方式并且使用內置Servlet容器(默認Tomcat),該種方式雖然簡單但是默認不支持JSP并且優(yōu)化容器比較復雜。故而我們可以使用習慣的外置Tomcat方式并將項目打War包。 ① 同樣使用Spring Initializer方式創(chuàng)建項目 ② 打包方式選擇\\\"war\\\" ③ 選擇添加的模塊 ④ 創(chuàng)建的

    2024年02月04日
    瀏覽(25)
  • Linux開機啟動流程

    Linux開機啟動流程

    Linux開機啟動流程詳細步驟如下圖: 其中: POST:Power On Self Test???????????????????????--加電自檢 BIOS:?Basic Input Output System? ? ? ? ? ?--基礎輸入輸出系統(tǒng) MBR:?Master Boot Record? ? ? ? ? ? ? ? ? ? ? --主引導記錄 GRUB: ?GRand Unified Bootloader? ? ? ? ? --統(tǒng)一引導程序

    2024年04月23日
    瀏覽(16)
  • PostgreSQL基于Patroni方案的高可用啟動流程分析

    什么是Patroni 在很多生產環(huán)境中,分布式數據庫以高可用性、數據分布性、負載均衡等特性,被用戶廣泛應用。而作為高可用數據庫的解決方案——Patroni,是專門為PostgreSQL數據庫設計的,一款以Python語言實現的高可用架構模板。該架構模板,旨在通過外部共享存儲軟件(ku

    2024年02月07日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包