對NXP提供的BSP里邊所使用的u-boot的環境變量`bootcmd`的解析

為什么我們要解析環境變量bootcmd

承接博文 https://blog.csdn.net/wenhao_ir/article/details/145902134 繼續解析u-boot的環境變量bootcmd

為什么要解析u-boot的這個環境變量bootcmd?因為如果u-boot在倒計時完后,首先執行的是就是下面這條命令:

run_command(env_get("bootcmd"), 0);

其中 env_get("bootcmd") 取出 bootcmd 環境變量的內容,并將其傳遞給 run_command() 執行。

所以我們有必要去解析環境變量bootcmd的內容。

打印出u-boot的環境變量的具體內容

說明:本文使用的u-boot是博文 https://blog.csdn.net/wenhao_ir/article/details/145662136 中經過我修改移植后的u-boot。在博文 https://blog.csdn.net/wenhao_ir/article/details/145662136 中有燒寫方法和它的百度網盤下載地址。

u-boot運行后,可用下面的命令單獨打印出變量 bootcmd 的內容:

printenv bootcmd

這里,由于各環境變量之間有依賴關系,所以需要用下面的命令完整打印出所有環境變量的內容:

printenv

在這里插入圖片描述

baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
bootcmd_mfg=run mfgtool_args;if iminfo ${initrd_addr}; then if test ${tee} = yes; then bootm ${tee_addr} ${initrd_addr} ${fdt_addr}; else bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; fi; else echo "Run fastboot ..."; fastboot 0; fi;
bootdelay=3
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
emmc_ack=1
emmc_dev=1
eth1addr=00:01:3f:2d:3e:4d
ethact=ethernet@20b4000
ethprime=eth1
fastboot_dev=mmc1
fdt_addr=0x83000000
fdt_file=undefined
fdt_high=0xffffffff
fdtcontroladdr=9df6d770
findfdt=if test $fdt_file = undefined; then if test $board_name = ULZ-EVK && test $board_rev = 14X14; then setenv fdt_file imx6ulz-14x14-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi;
findtee=if test $tee_file = undefined; then if test $board_name = ULZ-EVK && test $board_rev = 14X14; then setenv tee_file uTee-6ulzevk; fi; if test $board_name = EVK && test $board_rev = 9X9; then setenv tee_file uTee-6ullevk; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv tee_file uTee-6ullevk; fi; if test $tee_file = undefined; then echo WARNING: Could not determine tee to use; fi; fi;
image=zImage
initrd_addr=0x86800000
initrd_high=0xffffffff
ip_dyn=yes
kboot=bootz 
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
loadtee=fatload mmc ${mmcdev}:${mmcpart} ${tee_addr} ${tee_file}
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc clk_ignore_unused 
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${tee} = yes; then run loadfdt; run loadtee; bootm ${tee_addr} - ${fdt_addr}; else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; fi;
mmcdev=1
mmcpart=1
mmcroot=/dev/mmcblk1p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${tee} = yes; then ${get_cmd} ${tee_addr} ${tee_file}; ${get_cmd} ${fdt_addr} ${fdt_file}; bootm ${tee_addr} - ${fdt_addr}; else if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; fi;
script=boot.scr
sd_dev=1
serial#=2e1181d769237caa
splashimage=0x8c000000
tee=no
tee_addr=0x84000000
tee_file=undefinedEnvironment size: 3388/8188 bytes

從中可見環境變量bootcmd的具體內容為:

bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi

接下來我們需要對環境變量bootcmd中的語句進行分析,并理出我們這里的執行流程。

對環境變量bootcmd的內容進行分行和縮進處理

環境變量 bootcmd 的內容如下:

bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi

全寫在一行中,對我們閱讀很不友好,不妨先利用chatgpt將其進行分行和縮進處理,分行和縮進處理后如下:

bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev};  # 重復執行了一次,可能是為了確保正確切換到目標 mmc 設備if mmc rescan; thenif run loadbootscript; thenrun bootscript;elseif run loadimage; thenrun mmcboot;elserun netboot;fi;fi;elserun netboot;fi;

對環境變量bootcmd的每一條語句的解析

所有環境變量及環境變量bootcmd的內容在上面的內容中已經給出,在開始分析環境變量bootcmd中的各語句的意義,當知道其每條語句的意義后,也就知道了其執行流程了。

環境變量 bootcmd 的內容如下:

bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi

分行和縮進處理后如下:

bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev};  # 重復執行了一次,可能是為了確保正確切換到目標 mmc 設備if mmc rescan; thenif run loadbootscript; thenrun bootscript;elseif run loadimage; thenrun mmcboot;elserun netboot;fi;fi;elserun netboot;fi;

以下是對 bootcmd 中每一句語句的解析:

分析前,先了解下這個 bootcmd 主要完成的任務,如下:

  1. 查找設備樹(FDT)文件
  2. 查找 TEE(可信執行環境)文件
  3. 選擇 eMMC/SD 設備
  4. 嘗試從 MMC 設備加載 U-Boot 啟動腳本 boot.scr
  5. 如果 boot.scr 不存在,則嘗試直接加載 Linux 內核
  6. 如果 MMC 啟動失敗,則回退到網絡啟動

1. 運行 findfdt 以確定設備樹(DTB)文件的名字

run findfdt;
  • findfdt 變量的內容:
    findfdt=if test $fdt_file = undefined; then \if test $board_name = ULZ-EVK && test $board_rev = 14X14; then \setenv fdt_file imx6ulz-14x14-evk.dtb; \fi; \if test $board_name = EVK && test $board_rev = 9X9; then \setenv fdt_file imx6ull-9x9-evk.dtb; \fi; \if test $board_name = EVK && test $board_rev = 14X14; then \setenv fdt_file imx6ull-14x14-evk.dtb; \fi; \if test $fdt_file = undefined; then \echo WARNING: Could not determine dtb to use; \fi; \
    fi;
    
  • 作用:根據 board_nameboard_rev 確定要使用的設備樹文件,并存儲到 fdt_file 變量中。
  • 具體在這里,由于環境變量 board_name 的值為board_name=EVK,環境變量board_rev的值為14X14,所以環境變量fdt_file的值被設為imx6ull-14x14-evk.dtb,即內核的設備樹文件的名字為imx6ull-14x14-evk.dtb

2. 運行 findtee 以確定 TEE 鏡像的文件名

關于TEE 鏡像是什么東西,請參考我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/146054128

run findtee;
  • findtee 變量的內容:
    findtee=if test $tee_file = undefined; then \if test $board_name = ULZ-EVK && test $board_rev = 14X14; then \setenv tee_file uTee-6ulzevk; \fi; \if test $board_name = EVK && test $board_rev = 9X9; then \setenv tee_file uTee-6ullevk; \fi; \if test $board_name = EVK && test $board_rev = 14X14; then \setenv tee_file uTee-6ullevk; \fi; \if test $tee_file = undefined; then \echo WARNING: Could not determine tee to use; \fi; \
    fi;
    
  • 作用:根據 board_nameboard_rev 確定 TEE 鏡像文件(如 OP-TEE),并存儲到 tee_file 變量中。
  • 具體在這里,由于環境變量 board_name 的值為board_name=EVK,環境變量board_rev的值為14X14,所以環境變量tee_file的值被設為uTee-6ullevk

3. 選擇 eMMC 設備

mmc dev ${mmcdev};
mmc dev ${mmcdev};
  • mmcdev=1,表示默認使用 mmc1(可能是 eMMC)。可以用命令mmc list查看eMMC設備在u-boot中的編號,詳情見我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/146016551 【搜索“利用MMC子系統查看和修改”】
  • 這里調用 mmc dev 兩次,可能是為了確保設備切換正確。
  • 關于這個命令的詳情,見我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/146016551

4. 重新掃描 MMC 設備

if mmc rescan; then
  • 作用:檢查 eMMC/SD 設備是否可用,并更新分區信息。
  • 如果成功(即 MMC 設備存在),則繼續執行下面的加載步驟。
  • 如果失敗(即 MMC 設備不可用),則跳過直接進入 netboot

5. 嘗試加載 U-Boot 啟動腳本(boot.scr)

在這里插入圖片描述

if run loadbootscript; then run bootscript;
  • loadbootscript 變量的內容:
    loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
    
    • 嘗試從 MMC 設備 加載 boot.scr 文件loadaddr(0x80800000)。
    • 在這里,由于mmcdev的值為1mmcpart的值為1loadaddr的值為0x80800000script的內容為boot.scr,所以這里實際上嘗試把存儲在eMMC的boot1(Boot partition 1)中的文件boot.scr加載到內存的0x80800000位置。同時由于命令是fatload,所以boot1(Boot partition 1)中的文件系統的格式為FAT。如果加載成功,再執行環境變量 bootscript 中的內容。
  • bootscript 變量的內容如下:
    bootscript=echo Running bootscript from mmc ...; source
    
    • echo Running bootscript from mmc ...; :這部分只是打印信息,告訴用戶 “正在從 MMC 運行 bootscript…”。
    • source :這個命令的作用是 執行存儲在 RAM 中的 boot.scr 腳本。u-boot的source 命令會解析 loadaddr 處的 boot.scr 并執行其中的 U-Boot 命令。
    • boot.scr 是一個預編譯的 U-Boot 腳本,里面可能會定義更詳細的啟動過程,如:加載內核、加載設備樹、設定 bootargs、執行 bootzbootm等。
  • 具體在這里,由于我們并不會向boot1(Boot partition 1)中寫入文件boot.scr,即文件boot.scr并不存在,所以并不會執行這個分支。

6. 如果 boot.scr 不存在,則直接加載 Linux 內核

在這里插入圖片描述

else if run loadimage; then run mmcboot;

加載內核鏡像zImage到內存

  • loadimage 變量的內容:
    loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
    
    • 嘗試從 MMC 設備加載 zImageloadaddr(0x80800000)。
    • 在這里,由于mmcdev的值為1mmcpart的值為1loadaddr的值為0x80800000image的內容為zImage,所以這里實際上嘗試把存儲在eMMC的boot1(Boot partition 1)中的內核鏡像文件zImage加載到內存的0x80800000位置。同時由于命令是fatload,所以boot1(Boot partition 1)中的文件系統的格式為FAT。如果加載成功,再執行環境變量 mmcboot 中的內容。
  • mmcboot 變量的內容:
    mmcboot=echo Booting from mmc ...; run mmcargs; \if test ${tee} = yes; then \run loadfdt; run loadtee; \bootm ${tee_addr} - ${fdt_addr}; \else \if test ${boot_fdt} = yes || test ${boot_fdt} = try; then \if run loadfdt; then \bootz ${loadaddr} - ${fdt_addr}; \else \if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; \fi; \else \bootz; \fi; \fi;
    

首先打印輸出信息Booting from mmc ...

傳遞給內核的環境變量bootargs的設置(根文件系統的掛載)

然后執行環境變量mmcargs中的內容,環境變量mmcargs的內容為:

mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}

可見在這里設置了u-boot傳遞給內核的環境變量bootargs的值:
console的值為ttymxc0,所以內核啟動后選擇的當前終端為設備節點名為ttymxc0的終端,這實際上就是IMX6ULL的串口形成的終端。又由于baudrate的值為115200,所以這個終端所使用的串口的波特率被設置為115200
mmcroot的值為mmcroot=/dev/mmcblk1p2 rootwait rw,它的作用是指定 根文件系統(rootfs)的位置和掛載選項,具體解釋如下:

字段解析

參數作用
/dev/mmcblk1p2指定根文件系統所在的 設備節點,即 mmcblk1(eMMC/SD 設備 1)的 第 2 分區(用戶數據區的邏輯分區的第2個分區)
rootwait等待根文件系統設備準備好,用于 eMMC/SD 設備啟動時可能的延遲問題。
rw以讀寫模式(read-write)掛載根文件系統
  1. /dev/mmcblk1p2(根文件系統路徑)
    • /dev/mmcblk1p2 代表 eMMC 或 SD 設備 mmcblk1用戶數據區的邏輯分區的第2個分區p2)。
    • 設備名稱:
      • mmcblk0 → 這是Linux系統啟動后第一個 eMMC/SD 設備的名字。
      • mmcblk1 → 這是Linux系統啟動后第二個 eMMC/SD 設備的名字。
    • 分區編號:
      • mmcblk1p1 →用戶數據區的邏輯分區的第1個分區,注意不是eMMC設備的Boot partition 1分區。
      • mmcblk1p2 → 用戶數據區的邏輯分區的第2個分區,注意不是eMMC設備的Boot partition 2分區。
      • mmcblk1boot0-eMMC設備的Boot partition 1分區。
      • mmcblk1boot1-eMMC設備的Boot partition 2分區。

關于上面這些設備名稱和分區編號名是如何獲取的,即eMMC各分區在Linux內核中的名字,請參看我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145967306 【搜索“可以使用以下命令查看eMMC的分區信息”】

  1. rootwait(等待設備就緒)

    • eMMC/SD 設備在 U-Boot 交權給 Linux 內核后,可能需要時間初始化
    • rootwait 讓內核 無限等待根文件系統設備出現(而不是直接報錯)。
  2. rw(讀寫掛載根文件系統)

    • rw 讓 Linux 以讀寫模式 掛載根文件系統
    • 如果使用 ro(read-only),根文件系統會以只讀模式掛載,適用于只讀系統(如某些嵌入式設備)。
  3. 注:默認情況下,Linux啟動后init 進程可能會重新掛載根文件系統,原因見博文 https://blog.csdn.net/wenhao_ir/article/details/146067376

傳遞給內核的環境變量bootargs設置完后,即run mmcargs執行完后,再執行后面的代碼加載設備樹文件。

設置

  mmcboot=echo Booting from mmc ...; run mmcargs; \if test ${tee} = yes; then \run loadfdt; run loadtee; \bootm ${tee_addr} - ${fdt_addr}; \else \if test ${boot_fdt} = yes || test ${boot_fdt} = try; then \if run loadfdt; then \bootz ${loadaddr} - ${fdt_addr}; \else \if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; \fi; \else \bootz; \fi; \fi;
  • 如果 tee=yes,則先加載 TEE 并使用 bootm 命令引導。由于這里tee的值為no,所以不會執行這個分支,所以本博文中不去仔細分析這個分支。
  • 讀下面的內容前,前先閱讀博文 https://blog.csdn.net/wenhao_ir/article/details/146068588 了解命令 bootz的詳細情況。
  • 否則,嘗試加載設備樹(DTB)并使用命令 bootz 啟動。
  • 如果 DTB 加載失敗(但 boot_fdt=try),則直接 bootz 繼續引導內核。注意:命令 bootz不加參數時,默認從環境變量loadaddr中去獲取內核在內存中的地址。
  • 如果boot_fdt的值既不為yes也不為try,則不加載設備樹,直接用命令 bootz 啟動內核。注意:命令 bootz不加參數時,默認從環境變量loadaddr中去獲取內核在內存中的地址。

具體到這里來說,由于boot_fdt的值為try,所以執行下面紅框中的分支:
在這里插入圖片描述
語句run loadfdt;表示加載設備樹文件到內存中,環境變量loadfdt的內容如下:

loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}

在這里,由于mmcdev的值為1mmcpart的值為1fdt_addrr的值為0x83000000fdt_file的內容之前已經通過運行命令run findfdt設置為了imx6ull-14x14-evk.dtb,所以這里實際上嘗試把存儲在eMMC的boot1(Boot partition 1)中的設備樹文件imx6ull-14x14-evk.dtb加載到內存的為0x83000000的位置。同時由于命令是fatload,所以boot1(Boot partition 1)中的文件系統的格式為FAT。

這里:我們可以順便算下內核鏡像和設備樹文件在內存中相隔多遠:
0x83000000-0x80800000=0x2800000=Dec2800000B=2800000KB=40MB,即二者在內存中相隔了40MB。

如果設置樹文件加載成功,再執行下面這條命令:

bootz ${loadaddr} - ${fdt_addr};

關于bootz命令的詳解,見博文 https://blog.csdn.net/wenhao_ir/article/details/146068588

這樣 bootz 會按下面的參數啟動Linux內核:

  1. 從內存的 loadaddr(0x80800000) 位置加載 zImage
  2. - 表示 不使用bootz命令的第2個參數 ramdisk
  3. 從內存的 fdt_addr(0x83000000) 加載內核需要的設備樹(DTB)文件。

7. 如果 MMC 設備啟動失敗,則進行網絡啟動

else run netboot;
fi; fi; else run netboot; fi
  • netboot 變量的內容:
    netboot=echo Booting from net ...; ${usb_net_cmd}; run netargs; \if test ${ip_dyn} = yes; then \setenv get_cmd dhcp; \else \setenv get_cmd tftp; \fi; \${get_cmd} ${image}; \if test ${tee} = yes; then \${get_cmd} ${tee_addr} ${tee_file}; \${get_cmd} ${fdt_addr} ${fdt_file}; \bootm ${tee_addr} - ${fdt_addr}; \else \if test ${boot_fdt} = yes || test ${boot_fdt} = try; then \if ${get_cmd} ${fdt_addr} ${fdt_file}; then \bootz ${loadaddr} - ${fdt_addr}; \else \if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; \fi; \else \bootz; \fi; \fi;
    
  • 嘗試從網絡(TFTP/NFS)加載內核和設備樹,并執行啟動。
  • 關于上面這些從網絡啟動的命令的詳細解釋,見我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145902134

8.小結

  • 優先使用 MMC 啟動
  • 如果 boot.scr 存在,則執行 boot.scr
  • 如果 boot.scr 不存在,則直接加載 zImage 并啟動
  • 如果 MMC 啟動失敗,則使用網絡啟動

對我們有重要作用的信息匯總

關于內核鏡像文件的說明

①內核鏡像文件存儲在eMMC的boot1(Boot partition 1)中,其文件名為zImage,加載到內存中的位置為:0x80800000位置,要求boot1(Boot partition 1)的文件系統為FAT。

關于設備樹文件的說明

②設置樹文件存儲在eMMC的boot1(Boot partition 1)中,其文件名為imx6ull-14x14-evk.dtb,加載到內存中的位置為:0x83000000位置,要求boot1(Boot partition 1)的文件系統為FAT。

關于根文件系統掛載的說明

③根文件系統的掛載位置在設備節點/dev/mmcblk1p2,mmcblk1p2 的1代表eMMC設備的編號為1,p2代表用戶數據區的第2個邏輯分區。由于根文件系統的掛載是在內核啟動后進行的,所以掛載位置的設備節點的名字是Linux系統中的名字。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/71471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71471.shtml
英文地址,請注明出處:http://en.pswp.cn/web/71471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NSSCTF [SWPUCTF 2024 秋季新生賽]金絲雀

5948.[SWPUCTF 2024 秋季新生賽]金絲雀 canary繞過和64位的ret2libc(格式化字符串泄露) (1) motalymotaly-VMware-Virtual-Platform:~/桌面$ file xn xn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.…

神經網絡中梯度計算求和公式求導問題

以下是公式一推導出公式二的過程。 表達式一 ? E ? w j k ? 2 ( t k ? o k ) ? sigmoid ( ∑ j w j k ? o j ) ? ( 1 ? sigmoid ( ∑ j w j k ? o j ) ) ? ? ? w j k ( ∑ j w j k ? o j ) \frac{\partial E}{\partial w_{jk}} -2(t_k - o_k) \cdot \text{sigm…

koa-session設置Cookie后獲取不到

在谷歌瀏覽器中請求獲取不到cookie問題之一(谷歌安全策略) 場景 前端使用 axios 請求,項目地址:http://192.168.8.1:5173 import axios from axiosconst request axios.create({baseURL: http://127.0.0.1:3001/,timeout: 60000,…

單元測試與仿真程序之間的選擇

為什么寫這篇文章 現在的工作需求,讓我有必要總結和整理一下。 凡事都有適用的場景。首先這里我需要提示一下,這里的信息,可能并不普適。 但是可以肯定一點的是,有些人,不論做事還是寫書,上下文還沒有交待…

如何在Android中實現圖片加載和緩存

在Android中實現圖片加載和緩存是提升應用性能和用戶體驗的關鍵環節。高效的圖片加載和緩存策略能夠減少內存占用、避免應用卡頓,并快速響應用戶的圖片查看需求。以下是在Android中實現圖片加載和緩存的幾種常見方法: 一、使用第三方圖片加載庫 1. Gli…

FusionInsight MRS云原生數據湖

FusionInsight MRS云原生數據湖 1、FusionInsight MRS概述2、FusionInsight MRS解決方案3、FusionInsight MRS優勢4、FusionInsight MRS功能 1、FusionInsight MRS概述 1.1、數據湖概述 數據湖是一個集中式存儲庫,允許以任意規模存儲所有結構化和非結構化數據。可以…

推薦幾款優秀的PDF轉電子畫冊的軟件

當然可以!以下是幾款優秀的PDF轉電子畫冊的軟件推薦,內容簡潔易懂,這些軟件都具有易用性和互動性,適合不同需求的用戶使用。? ? FLBOOK|在線創作平臺 支持PDF直接導入生成仿真翻頁電子書。提供15主題模板與字體庫&a…

【GoTeams】-2:項目基礎搭建(下)

本文目錄 1. 回顧2. Zap日志3. 配置4. 引入gprc梳理gRPC思路優雅關閉gRPC 1. 回顧 上篇文章我們進行了路由搭建,引入了redis,現在來看看對應的效果。 首先先把前端跑起來,然后點擊注冊獲取驗證碼。 再看看控制臺輸出和redis是否已經有記錄&…

深度學習反向傳播

一、白話解釋 梯度其實就是導數,除了用符號求導也可以用近似求導: 然后更新ww-學習率*導數 反向傳播就是鏈式求導 向前計算:對每個節點求偏導 在前向傳播的時候,進行一次前向計算的時候就可以把每一條線的偏導數都知道 前向傳…

JavaWeb-HttpServletRequest請求域接口

文章目錄 HttpServletRequest請求域接口HttpServletRequest請求域接口簡介關于請求域和應用域的區別 請求域接口中的相關方法獲取前端請求參數(getParameter系列方法)存儲請求域名參數(Attribute系列方法)獲取客戶端的相關地址信息獲取項目的根路徑 關于轉發和重定向的細致剖析…

deepseek在pycharm 中的配置和簡單應用

對于最常用的調試python腳本開發環境pycharm,如何接入deepseek是我們窺探ai代碼編寫的第一步,熟悉起來總沒壞處。 1、官網安裝pycharm社區版(免費),如果需要安裝專業版,需要另外找破解碼。 2、安裝Ollama…

AAA協議:從零認識網絡的“身份管家”

AAA(Authentication, Authorization, Accounting,認證、授權和計費)是網絡世界的“身份管理員”,負責確認“你是誰”、決定“你能干啥”、記錄“你干了啥”。如果你用過華三的交換機或路由器,可能在配置用戶管理時見過…

動態規劃01背包問題系列一>最后一塊石頭的重量II

這里寫目錄標題 題目分析:狀態表示:狀態轉移方程:初始化:填表順序:返回值:代碼呈現:優化版本:代碼呈現: 題目分析: 狀態表示: 狀態轉移方程&#…

逐行拆解 C 語言:數據類型、變量

今日,我們即將踏上一段充滿趣味與挑戰的學習之旅,深度鉆研數據類型的多樣奧秘,解鎖變量創建的實用技巧。不僅如此,還會邂逅兩個實用的基礎庫函數,探索它們在程序中穿針引線的奇妙作用。同時,幾個簡潔卻強大…

【音視頻】ffplay簡單過濾器

一、ffplay簡單過濾器 視頻旋轉:借助transpose濾鏡 ffplay -i 1.mp4 -vf transpose1這里選擇不同的數字是不同的方向: 視頻翻轉:借助hflip/vflip實現水平和垂直翻轉: 水平翻轉 ffplay 1.mp4 -vf hflip垂直翻轉 ffplay 1.mp4 …

springboot中注解有什么用

注解(Annotation)是 Java 的一個重要特性,我用幾個具體例子來解釋: 1、標記功能 Service // 告訴Spring這是一個服務類 public class UserService { }Data // 告訴Lombok自動生成getter/setter public class User {private…

Excel中COUNTIF用法解析

COUNTIF 是 Excel 中一個非常實用的函數,用于統計滿足某個條件的單元格數量。它的基本語法如下: 基本語法 COUNTIF(范圍, 條件) 范圍:需要統計的單元格區域,例如 A1:A10 或整列 A:A。 條件:用于判斷哪些單元格需要被…

java根據List<Object>中的某個屬性排序(數據極少,順序固定)

public static void main(String[] args) { List<HashMap<String, Object>> dydj new ArrayList<>(); // 模擬原始數據 HashMap<String, Object> map1 new HashMap<>(); map1.put(“city_name”, “張家口”); map1.put(“wjs”, 0); map1.put…

4G工業路由器在公交充電樁中的應用與優勢

隨著電動公交車的普及&#xff0c;公交充電樁的穩定運行和高效管理是交通營運部門最關心的問題。4G工業路由器憑借其卓越的數據采集和通訊能力&#xff0c;成為實現充電樁智能化管理的關鍵。 公交充電樁運維管理需求概述&#xff1a; 1.實時性&#xff1a;實時監控充電狀態、剩…

利用golang embed特性嵌入前端資源問題解決

embed嵌入前端資源&#xff0c;配置前端路由的代碼如下 func StartHttpService(port string, assetsFs embed.FS) error {//r : gin.Default()gin.SetMode(gin.ReleaseMode)r : gin.New()r.Use(CORSMiddleware())// 靜態文件服務dist, err : fs.Sub(assetsFs, "assets/di…