linux-2.6.22.6內核啟動分析之head.S引導段代碼

来源:https://www.cnblogs.com/053179hu/archive/2018/07/11/9289106.html
-Advertisement-
Play Games

學習目標: 瞭解arch/arm/kernel/head.S作為內核啟動的第一個文件所實現的功能! 前面通過對內核Makefile的分析,可以知道arch/arm/kernel/head.S是內核啟動的第一個文件。另外,U-boot調用內核時,r1寄存器中存儲“機器類型ID”,內核會使用它。 打開a ...


學習目標:

瞭解arch/arm/kernel/head.S作為內核啟動的第一個文件所實現的功能!


前面通過對內核Makefile的分析,可以知道arch/arm/kernel/head.S是內核啟動的第一個文件。另外,U-boot調用內核時,r1寄存器中存儲“機器類型ID”,內核會使用它。

打開arch/arm/kernel/head.S文件,可以看到stext函數是內核入口函數,函數內容如下:

76     .section ".text.head", "ax"                             /* 定義一個.text.head段,段的屬性a是允許段,x是可執行 */
77     .type    stext, %function                               /* 定義u-boot進入內核的入口函數 */
78 ENTRY(stext)                                                /* 入口地址stext函數 */
79     msr    cpsr_c,  #PSR_F_BIT | PSR_I_BIT | SVC_MODE       /* 關中斷,設置CPU工作在管理模式 */
80
81 mrc p15, 0, r9, c0, c0 /* 獲取CPU的ID */
82 bl __lookup_processor_type /* 調用函數,輸入參數r9=cpuid,返回值r5=procinfo */
83 movs r10, r5 /* 不支持當前CPU,則返回值r5=0 */ 84 beq __error_p /* 如果r5=0,則列印錯誤 */ 85 bl __lookup_machine_type /* 調用函數,返回值r5=machinfo */
86 movs r8, r5 /* 不支持當前開發板,返回值r5=machinfo */ 87 beq __error_a /* 如果r5=0,則列印錯誤 */ 88 bl __create_page_tables /* 創建頁表 */

先來大概介紹入口函數stext每條語句實現的功能,然後再詳細分析__lookup_processor_type和__lookup_machine_type這兩個函數:

第79行通過設置CPSR寄存器來確保處理器進入管理模式,並且禁止中斷。

第81行讀取協處理器CP15的寄存器C0獲得CPU ID。

第82行調用__lookup_processor_type函數,檢測內核是否支持當前CPU。如果支持,r5寄存器返回一個用來描述處理器結構的地址,否則r5的值為0。

第85行調用__lookup_machine_type函數,確定內核是否支持當前開發板。如果支持,r5寄存器返回一個用來描述這個開發板的結構的地址,否則r5的值為0。

第88行調用__create_page_table函數,其中的__create_page_table函數用來創建以及頁表以建立虛擬地址到物理地址的映射關係,它用到__lookup_processer_type函數返回的proc_info_list結構。

如果__lookup_processor_type、__lookup_machine_type這兩個函數中有一個返回值為0,則內核不能啟動,如果配置內核時使能了CONFIG_DEBUG_LL,還會列印錯誤提示信息。


在介紹__lookup_processor_type和__lookup_machine_type這兩個函數之前,還要先插講一些內容。我們在前面簡要的說過__lookup_processor_type和__lookup_machine_type這兩個函數分別是用來檢測內核是否支持當前架構的處理器、是否支持當前開發板,如果內核想實現檢測功能,那麼內核中一定會存放自己所支持的處理器架構信息以及所支持的開發板信息,下麵先來找到內核中這些信息是如何被定義的。

內核中,定義了若幹個pro_info_list結構,表示它所支持的CPU。對於ARM架構的CPU,這些結構體的源碼在arch/arm/mm/目錄下,例如proc-arm920.S中的如下代碼,它表示arm920架構CPU的pro_info_list結構。

448     .section ".proc.info.init", #alloc, #execinstr
449 
450     .type    __arm920_proc_info,#object
451 __arm920_proc_info:
452     .long    0x41009200             /* cpu val */
453     .long    0xff00fff0             /* cpu mask */

內核所支持每個處理器架構都有自己的pro_info_list結構體用來保存自己CPU信息,這些不同處理器使用的pro_info_list結構體都被強制定義在“.proc_info_init”段中。在連接內核時,這些結構體被組織在一起,起始地址為__proc_info_begin,結束地址為__proc_info_end。可以從連接腳本arch/arm/kernel/vmlinux.lds中看出來。

35    __proc_info_begin = .;          #.proc_info_init段起始地址(連接程式是動態確定)
36      *(.proc.info.init)
37    __proc_info_end = .;            #.proc_info_init結束地址(連接程式時動態確定)

再來看內核中支持開發板信息內容,是如何被定義和存放的。內核中對於每種所支持的開發板都會使用巨集MACHINE_START、MACHINE_END來定義一個machine_desc結構,這個結構定義了開發板相關的一些屬性和函數,比如機器ID、起始I/O物理地址、Bootloader傳入參數的地址、中斷初始化函數等等。例如SMDK2410開發板,在arch/arm/mach-sc32410/mach-smdk2410.c中定義。

198 MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
199                     * to SMDK2410 */
200     /* Maintainer: Jonas Dietsche */
201     .phys_io    = S3C2410_PA_UART,
202     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
203     .boot_params    = S3C2410_SDRAM_PA + 0x100,
204     .map_io        = smdk2410_map_io,
205     .init_irq    = s3c24xx_init_irq,
206     .init_machine    = smdk2410_init,
207     .timer        = &s3c24xx_timer,
208 MACHINE_END

在內核的include/asm/-arm/mach/arch.h文件中找到第198、202行的巨集MACHINE_START、MACHINE_END定義,這兩個巨集定義如下:

50 #define MACHINE_START(_type,_name)            \
51 static const struct machine_desc __mach_desc_##_type    \
52  __used                            \
53  __attribute__((__section__(".arch.info.init"))) = {    \
54     .nr        = MACH_TYPE_##_type,        \
55    .name        = _name,
56 
57 #define MACHINE_END                \
58 };

 按照上述巨集定義,將198行~208行代碼展開如下所示:

static const struct machine_desc __mach_desc_SMDK2410    \
 __used                            \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr        = MACH_TYPE_SMDK2410,        \
    .name        = "SMDK2410",
        .phys_io    = S3C2410_PA_UART,
    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S3C2410_SDRAM_PA + 0x100,
    .map_io        = smdk2410_map_io,
    .init_irq    = s3c24xx_init_irq,
    .init_machine    = smdk2410_init,
    .timer        = &s3c24xx_timer,
};

將198~208行的代碼展開,可以看出這段內容定義了一個靜態常量的machine_desc類型結構體__mach_desc_SMDK2410。其中的MACH_TYPE_SMDK2410在arch/arm/tools/mach-types中定義,最後這個文件會被轉換成一個頭文件include/asm/arm/mach-type.h被其它文件包含。machine_des結構體在include/asm-arm/mach/arch.h文件中定義。__attribute__((__section__(".arch.info.init")))語句表示將所有的machine_desc結構都存放在“.arch.info.init”段中,在連接內核時,它們被組織到一起,開始地址為__arch_info_begin,結束地址為__arch_info_end。可以從連接腳本arch/arm/kernel/vmlinux.lds中看出來。 

38   __arch_info_begin = .;                #.arch.info.init段起始地址(連接時動態確定)
39     *(.arch.info.init)
40   __arch_info_end = .;                  #.arch.info.init段結束地址(連接時動態確定)

有了上面的插講內容作為鋪墊,下麵對 __lookup_processor_type和__lookup_machine_type這兩個函數如何去實現各自功能理解就會更加方便了。先來看 __lookup_processor_type函數,在arch/arm/kernel/head-common.S文件中定義如下:

145     .type    __lookup_processor_type, %function
146 __lookup_processor_type:
147     adr    r3, 3f
148     ldmda  r3, {r5 - r7}
149     sub    r3, r3, r7            @ get offset between virt&phys
150     add    r5, r5, r3            @ convert virt addresses to
151     add    r6, r6, r3            @ physical address space
152 1:  ldmia    r5, {r3, r4}            @ value, mask
153     and    r4, r4, r9            @ mask wanted bits
154     teq    r3, r4
155     beq    2f
156     add    r5, r5, #PROC_INFO_SZ        @ sizeof(proc_info_list)
157     cmp    r5, r6
158     blo    1b
159     mov    r5, #0                @ unknown processor
160 2:  mov    pc, lr
176     .long    __proc_info_begin
177     .long    __proc_info_end
178 3:  .long    .
179     .long    __arch_info_begin
180     .long    __arch_info_end

在調用__enable_mmu函數之前使用的都是物理地址,而內核卻以虛擬地址鏈接的。所以在訪問pro_info_list結構之前,先將它的虛擬地址轉換為物理地址,上面代碼的147行~151行就是實現上述的轉換。

第147行先獲得178行物理地址。adr指令基於pc寄存器計算地址,此時MMU功能關閉,PC寄存器中使用的還是物理地址,所以執行“adr r3,3f”後,r3記憶體放的是178行代碼的物理地址, 指令中的3f的f是forward的意思,意思是跳到程式的後面(往下)。

第148行用來獲取第176行~178行定義的數據:__proc_info_begin、__pro_info_end和"."。前兩個變數是在連接內核時確定,它們是虛擬地址,在前面插講中我們對這__proc_info_begin、__pro_info_end已經做出了詳細介紹,"."表示當前的代碼在編譯鏈接後的虛擬地址。ldmda r3, {r5-r7}指令,從源地址[r3]讀取4個位元組數據放到寄存器中,每讀一次r3-4,而指令執行後r3內容不變,數據存放到寄存器規則是低地址對於低寄存器編號,高地址對於高寄存器編號。

第149行計算物理地址和虛擬地址的差值,第150~151根據這個差值計算__pro_info_begin、__pro_info_end的物理地址。

下麵的代碼一次讀取存放在“.proc_info_init”段中每個cpu架構的pro_info_list結構體前面兩個成員,判斷cpu_val是否等於r9&cpu_mask,r9是讀取head.S中獲取的CPU ID.如果比較相等,則表示內核支持當前CPU,直接返回這個結構地址。如果“.proc_info_init”段所有pro_info_list結構都不支持這個CPU,則返回0。

第160行是子函數調用返回語句。

接著再來分析__lookup_machine_type這個函數,同樣的這個函數也在在arch/arm/kernel/head-common.S文件中定義,其代碼如下所示:

193     .type    __lookup_machine_type, %function
194 __lookup_machine_type:
195     adr    r3, 3b                     @address of 3b, Physical address
196     ldmia    r3, {r4, r5, r6}   @r4="." virtual address of 3b,r5=__arch_info_begin ,r6=__arch_info_end
197     sub    r3, r3, r4            @ get offset between virt&phys
198     add    r5, r5, r3            @ convert virt addresses to
199     add    r6, r6, r3            @ physical address space
200 1:  ldr    r3, [r5, #MACHINFO_TYPE]    @ get machine type
201     teq    r3, r1                @ matches loader number?
202     beq    2f                @ found
203     add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc
204     cmp    r5, r6
205     blo    1b
206     mov    r5, #0                @ unknown machine
207 2:  mov    pc, lr

第195行先獲得178行物理地址。adr指令基於pc寄存器計算地址,此時MMU功能關閉,PC寄存器中使用的還是物理地址,所以執行“adr r3,3f”後,r3記憶體放的是178行代碼的物理地址, 指令中的3b的b是backward的意思,意思是跳到程式的前面(往上)。

第196行用來獲取第178行~180行定義的數據:__arch_info_begin、__arch_info_end和"."。前兩個變數都是在連接內核時確定,它們是虛擬地址,在前面插講中我們對這__arch_info_begin、__arch_info_end已經做出了詳細介紹,"."表示當前的代碼在編譯鏈接後的虛擬地址。ldmia r3, {r5-r7}指令,從源地址[r3]讀取4個位元組數據放到寄存器中,每讀一次r3+4,而指令執行後r3內容不變,數據存放到寄存器規則是低地址對於低寄存器編號,高地址對於高寄存器編號。

第197行計算物理地址和虛擬地址的差值,第198~199行根據這個差值計算__arch_info_begin、__arch_info_end的物理地址。

下麵的代碼讀取存放在“.arch_info_init”段中每個machine_des結構體機器類型ID,判斷uboot傳入機器ID(通過r1寄存器傳入)是否等於內核中存放的不同machine_des結構體的機器類型ID。如果比較相等,則表示內核支持當前開發板,直接返回這個結構地址。如果“.arch_info_init”段所有存放machine_des結構都不支持這個開發板,則返回0。

第207行是子函數調用返回語句。


繼續分析arch/arm/kernel/head.S代碼

97     ldr    r13, __switch_data        @ address to jump to after
98                         @ mmu has been enabled
99     adr    lr, __enable_mmu        @ return (PIC) address
100    add    pc, r10, #PROCINFO_INITFUNC

第97行把__switch_data函數地址存放到r13寄存器中,存放的地址為虛擬地址。

第99行把__enable_mmu函數地址存放到lr(r4)寄存器中,存放地址為物理地址。

第100行#PROCINFO_INITFUNC為16,程式計數器pc值=r10+16,由上面分析可以知道r10內容為內核中描述當前處理器結構的地址,也就是arch/arm/mm/pro-arm920.S文件中__arm920_proc_info入口地址,pc=r10+16,即跳轉下麵代碼第502處執行__arm920_setup函數。

451 __arm920_proc_info:
452    .long    0x41009200
453    .long    0xff00fff0
454    .long   PMD_TYPE_SECT | \
455        PMD_SECT_BUFFERABLE | \
456         PMD_SECT_CACHEABLE | \
457         PMD_BIT4 | \
458         PMD_SECT_AP_WRITE | \
459         PMD_SECT_AP_READ
460    .long   PMD_TYPE_SECT | \
461         PMD_BIT4 | \
462         PMD_SECT_AP_WRITE | \
463         PMD_SECT_AP_READ
464    b    __arm920_setup
465    .long    cpu_arch_name
...................
476 #endif
477    .size    __arm920_proc_info, . - __arm920_proc_info

  __arm920_setup函數內容如下:

385     .type    __arm920_setup, #function
386 __arm920_setup:
387     mov    r0, #0
388     mcr    p15, 0, r0, c7, c7        @ invalidate I,D caches on v4
389     mcr    p15, 0, r0, c7, c10, 4        @ drain write buffer on v4
390 #ifdef CONFIG_MMU
391     mcr    p15, 0, r0, c8, c7        @ invalidate I,D TLBs on v4
392 #endif
393     adr    r5, arm920_crval
394     ldmia    r5, {r5, r6}
395     mrc    p15, 0, r0, c1, c0        @ get control register v4
396     bic    r0, r0, r5
397     orr    r0, r0, r6
398     mov    pc, lr
399    .size    __arm920_setup, . - __arm920_setup

 __arm920_setup函數功能是禁止ICache、DCache、數據Cache、指令Cache,最後將lr寄存器內容傳送給PC程式計數器,由上面知道lr寄存器存放為__enable_mmu函數地址,此時程式將跳轉到__enable_mmu函數處執行。__enable_mmu函數在arch/arm/kernel/head.S文件中,內容如下:

152     .type    __enable_mmu, %function
153 __enable_mmu:
154 #ifdef CONFIG_ALIGNMENT_TRAP
155     orr    r0, r0, #CR_A
156 #else
157     bic    r0, r0, #CR_A
158 #endif
159 #ifdef CONFIG_CPU_DCACHE_DISABLE
160     bic    r0, r0, #CR_C
161 #endif
162 #ifdef CONFIG_CPU_BPREDICT_DISABLE
163     bic    r0, r0, #CR_Z
164 #endif
165 #ifdef CONFIG_CPU_ICACHE_DISABLE
166     bic    r0, r0, #CR_I
167 #endif
168     mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
169               domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
170               domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
171               domain_val(DOMAIN_IO, DOMAIN_CLIENT))
172     mcr    p15, 0, r5, c3, c0, 0        @ load domain access register
173    mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer
174     b    __turn_mmu_on

 __turn_mmu_on代碼如下:

187     .align    5
188     .type    __turn_mmu_on, %function
189 __turn_mmu_on:
190     mov    r0, r0
191     mcr    p15, 0, r0, c1, c0, 0        @ write control reg
192     mrc    p15, 0, r3, c0, c0, 0        @ read id reg
193     mov    r3, r3
194     mov    r3, r3
195     mov    pc, r13

 __enable_mmu函數用來使能MMU,最後將r13寄存器內容賦給PC寄存器,由上面分析可知r13寄存器內容為__switch_data函數地址(此處地址為虛擬地址,因為已經開啟了MMU功能),最終程式跳轉到arm/arch/kernel/head-common.S文件__switch_data入口地址。__switch_data地址存放內容如下:

14     .type    __switch_data, %object
15 __switch_data:
16     .long    __mmap_switched
17     .long    __data_loc            @ r4
18     .long    __data_start            @ r5
19     .long    __bss_start            @ r6
20     .long    _end                @ r7
21     .long    processor_id            @ r4
22     .long    __machine_arch_type        @ r5
23     .long    cr_alignment            @ r6
24     .long    init_thread_union + THREAD_START_SP @ sp

 __switch_data地址處存放內容為__mmap_switched地址,PC跳轉到__mmp_switched處執行,__mmp_swirched函數內容如下:

34     .type    __mmap_switched, %function
35 __mmap_switched:
36     adr    r3, __switch_data + 4
37 
38     ldmia    r3!, {r4, r5, r6, r7}
39     cmp    r4, r5                @ Copy data segment if needed
40 1:  cmpne    r5, r6
41     ldrne    fp, [r4], #4
42     strne    fp, [r5], #4
43     bne    1b
44 
45     mov    fp, #0                @ Clear BSS (and zero fp)
46 1:  cmp    r6, r7
47     strcc    fp, [r6],#4
48     bcc    1b
49 
50     ldmia    r3, {r4, r5, r6, sp}
51     str    r9, [r4]            @ Save processor ID
52     str    r1, [r5]            @ Save machine type
53     bic    r4, r0, #CR_A            @ Clear 'A' bit
54     stmia    r6, {r0, r4}            @ Save control register values
55     b    start_kernel

 第36~43行實現複製數據段。

第45~48行實現清除BSS段。

第50行設置棧指針。

第51行保存CPU ID。

第52行存機器類型ID。

第55行跳轉到start_kernel函數執行。

 

總結:引導階段代碼做了以下內容
1、首先檢查內核是否支持當前架構處理器,然後檢測是否支持當前開發板,若支持執行後續操作。
2、設置頁表、使能MMU
3、執行調用內核執行第一個C函數start_kernel之前的常規工作,包括複製數據段、清除BSS段等

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原先做伺服器程式, 都是部署在xx雲上, 也沒理解雲是個啥, 不就是個伺服器(虛擬機)租賃商嗎? 好吧, 其實這個是IaaS, 而接下來要學習的ServiceFabric(以下簡稱SF)是PaaS. 首先SF和Orleans類似, 都是基於actor模型, 然後編程方式也很像, 大概就是定義公開介面 ...
  • 最近學習redhat7,進行網路配置,以前都是橋接直連,然後直接組網。由於一些原因現在虛擬機做內網使用,不用上網,只能使用僅主機模式。在僅主機模式下進行虛擬機組網。 僅主機模式下各個虛擬機只能和主機通信,各個虛擬機之間也可以通信,可以搭建自己的區域網環境。 安裝虛擬機時候選擇,或者虛擬機的網路設置: ...
  • 第14章 uCOS-III操作系統版本二代示波器實現 本章教程為大家講解uCOS-III操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變數共用問題。同時,工程調試方法也專門做了說明。 14.1 註意事項(重要必讀) 14.2 ...
  • 背景 cisco vpn client軟體對於Windows 10以後,會發現安裝不上,或者撥入不上的情況,換回Windows7又正常,尤其是最近win10 推送1709版本後,很多原來的解決方案都失效。 下麵介紹一種全新的方法。 傳統解決辦法 1. 關閉系統所有視窗,控制面板一定要關閉。 2. 運 ...
  • 需求:將Linux系統的的某個文件夾(裡面包含文件夾和文件)下載到我Windows系統某個文件夾里 之前我使用xshell下載,但是通過 rz :上傳sz:下載 命令中的sz命令,下載失敗。 下載 code文件到本地 以下是code文件里的內容: 通過sz dir/* 命令: 通過查找資料得出結論是 ...
  • 一、Linux的發展 1.1969年在貝爾實驗室誕生Unix,是開源免費的,之後逐漸轉變為收費系統。 2.1986年譚邦寧研發mini Unix,但主要用來教學。 3.斯托曼創建FSF(自由軟體基金會) 項目:GNU 葛奴計劃 4.GPL:FSF制定通用公共許可 開源免費傳播 任意修改,修改之後必須 ...
  • 前言 轉帖請註明出處: http://www.cnblogs.com/Troy-Lv5/ 版本管理當然是選擇git..反正我是被svn坑怕了... 這次安裝的是git 2.18.0 點擊下載 準備安裝 1. 刪除原有Git, 由於系統預設使用的是1.8.3.1的git, 所以要先刪除掉 2. 下載g ...
  • ssh服務 1.檢查是否有被安裝,命令 2.檢查ssh有沒有在運行,命令 3.如何啟動ssh 啟動命令 service sshd start 停止命令 service sshd stop 重啟命令 service sshd restart 查看狀態 service sshd status ***** ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...