Linux內核Kernel啟動過程

来源:https://www.cnblogs.com/anywherego/p/18217546
-Advertisement-
Play Games

在上一篇電腦啟動過程文章中介紹了電腦啟動的基本流程,本篇文章主要介紹Linux內核Kernel的啟動過程。 一、內核啟動的基本流程 sequenceDiagram participant Bootloader participant Kernel participant InitProcess ...


在上一篇電腦啟動過程文章中介紹了電腦啟動的基本流程,本篇文章主要介紹Linux內核Kernel的啟動過程。

一、內核啟動的基本流程

sequenceDiagram participant Bootloader participant Kernel participant InitProcess Bootloader->>Kernel: 載入內核映像 Kernel->>Kernel: 內核解壓 Kernel->>Kernel: 內核啟動 Kernel->>Kernel: 調用start_kernel函數 Kernel->>InitProcess: 啟動初始進程

1. 啟動載入程式 (Bootloader)

啟動載入程式(如GRUB、LILO、syslinux等)負責將內核映像從存儲設備載入到記憶體中,並準備好內核啟動所需的環境。

  • 載入內核映像:啟動載入程式將壓縮的內核映像(如vmlinuz)從硬碟載入到記憶體中。內核映像通常是一個gzip或其他格式壓縮的二進位文件。
  • 載入initrd/initramfs:如果使用initrd(初始RAM盤)或initramfs(初始RAM文件系統),啟動載入程式也會將這些文件載入到記憶體中,以便內核在啟動時使用。

2. 內核解壓階段

在內核映像的開頭,有一個小的解壓縮程式,它負責解壓內核的主體部分。

  • 解壓內核:內核映像被載入到記憶體後,解壓縮程式會運行並將壓縮的內核映像解壓到適當的記憶體位置。
  • 跳轉到解壓後的內核:一旦解壓完成,控制權會被移交給解壓後的內核代碼的入口點。

3. 內核啟動(Kernel Startup)

解壓後的內核代碼會從一個固定的入口點開始執行,這個入口點是平臺和架構相關的。對於x86架構,通常是startup_32或startup_64函數。

  • 架構特定的初始化:根據具體的硬體架構,內核會執行一些必要的初始化步驟,比如設置CPU的運行模式,初始化分頁機制,建立基本的記憶體映射等。
  • 初始化內核堆棧:內核設置好自己的堆棧,以便後續的函數調用和操作。
  • 調用start_kernel函數:完成基礎的硬體初始化後,內核會調用start_kernel函數,這是內核初始化的核心部分。

4. start_kernel函數

start_kernel函數位於init/main.c文件中,負責完成大部分內核的初始化工作。

  • 初始化控制台:設置內核的印表機制,以便後續的輸出可以顯示出來。
  • 初始化記憶體管理子系統:建立初始的記憶體管理結構,準備好記憶體分配機制。
  • 檢測和初始化硬體設備:內核會檢測並初始化系統中的各種硬體設備和驅動程式。
  • 啟動中斷處理機制:設置和啟動中斷處理機制,使得內核可以響應硬體中斷。
  • 初始化內核調度器:初始化內核調度器,以便管理進程調度。
  • 載入初始進程:內核創建並啟動第一個用戶空間進程,通常是/sbin/init。

5. 啟動初始進程

init進程是用戶空間的第一個進程,負責進一步的系統初始化工作,包括啟動系統服務和守護進程。

  • init進程的初始化:init進程執行系統初始化腳本,設置各種系統參數和啟動服務。
  • 啟動用戶空間服務:最終,init進程啟動配置的所有用戶空間服務和守護進程,從而完成系統的啟動過程。

二、內核文件載入及解壓縮

1.為什麼是壓縮文件

Linux內核映像通常是一個壓縮文件,主要有以下原因:

  • 減少存儲空間: 壓縮內核映像可以顯著減少其在存儲設備上的占用空間。這對嵌入式系統、存儲資源有限的設備以及需要快速分發和更新內核的環境尤其重要
  • 加快載入速度: 壓縮文件占用的空間更小,這意味著啟動載入程式從磁碟讀取文件到記憶體中的時間會更短。雖然解壓縮內核映像需要一些時間,但現代處理器的解壓縮速度非常快,通常解壓縮的時間比從存儲設備讀取更多數據的時間要少。這會整體上加快啟動過程。
  • 提高傳輸效率: 在網路上傳輸內核映像時,壓縮文件可以顯著減少帶寬使用量。這對於需要遠程更新內核的系統(OTA)非常有利。
  • 便於管理和分發: 壓縮內核映像更便於在各種介質上分發,比如光碟、U盤等。一個較小的文件更容易管理、備份和分發。
  • 標準化處理: 使用壓縮內核映像是一種標準做法,啟動載入程式(如GRUB)已經能夠很好地支持這種格式,能自動識別並處理壓縮的內核映像。這使得系統啟動過程更簡單可靠。

2.文件類型vmlinuxz和bzImage

在連接壓縮映像文件之前,我們先來瞭解一下未經壓縮的編譯文件vmlinux

2.1 什麼是vmlinux?

vmlinux內核編譯過程中生成的一個包含所有內核代碼和數據的二進位文件。它是未經壓縮和未經過處理的內核映像,通常位於內核源碼目錄的根目錄下,特性如下:

  • 未壓縮:vmlinux 是內核的未壓縮映像。它包含所有內核代碼、內核模塊以及相關的數據結構。
  • ELF 格式:vmlinux 通常是一個 ELF(Executable and Linkable Format)文件,這是一個標準的可執行文件格式,用於存儲可執行文件、目標代碼和共用庫等。
  • 符號信息:vmlinux 文件中包含調試符號和符號表信息,這些信息對內核調試和分析非常重要。
  • 沒有文件尾碼:雖然 vmlinux 通常沒有文件尾碼,但它是一個標準的 ELF 文件,可以通過文件頭信息識別其格式。

2.2 vmlinux的生成過程

編譯Linux內核時,vmlinux是在鏈接階段生成的。以下是一個簡化的生成過程:

  • 編譯各個源文件:內核的各個源文件(.c.S 文件)首先被編譯為目標文件(.o 文件)。
  • 鏈接目標文件:所有目標文件通過鏈接器(如ld)鏈接在一起,生成一個完整的內核映像,這個映像就是 vmlinux。

鏈接命令舉例:ld -o vmlinux [object files] [linker scripts]

2.3 vmlinuxz和bzImage的生成過程

在獲得編譯文件vmlinux後,通常使用壓縮工具做進一步處理。

  • 壓縮內核映像:將 vmlinux 壓縮生成 vmlinuz。通常使用 gzip 或其他壓縮工具。

壓縮命令: gzip -c vmlinux > vmlinuz

  • 生成引導載入程式格式的內核映像:一些系統需要特定格式的內核映像,例如 bzImage(適用於 x86 架構)。

生成命令:make bzImage

2.4 其他壓縮格式

vmlinuzbzImagezImageuImage 都是不同的 Linux 內核映像文件格式,它們各自有不同的用途和特性。

  • vmlinuz:通用的壓縮內核映像名稱,主要用於各種 Linux 發行版。通常使用 gzip 壓縮。
  • bzImage:大內核映像,解決了早期 zImage 的記憶體限制問題。用於 x86 架構,支持較大的內核映像。
  • zImage:較老的內核映像格式,適用於小內核映像,受限於低記憶體地址空間。
  • uImage:U-Boot 使用的內核映像格式,廣泛用於嵌入式系統。包含 U-Boot 頭部信息,支持多種壓縮演算法。

2.5 Android系統文件

在 Android 系統中,內核的壓縮文件格式通常是zImageImage.gz,具體取決於所使用的啟動載入程式和設備的要求。

  • zImage:在一些早期的 Android 設備上,內核映像可能採用 zImage 格式。這種格式的內核映像通常會被啟動載入程式直接載入並解壓,然後啟動內核。
  • Image.gz:Image.gz 是指經過 gzip 壓縮的內核映像。這種格式的內核映像通常是 Linux 內核編譯過程中生成的 vmlinuz 文件,只是在 Android 系統中可能被重新命名為 Image.gz。啟動載入程式會載入這個壓縮的內核映像,併在載入到記憶體後解壓縮,然後啟動內核。

3.內核載入過程

sequenceDiagram participant BootLoader participant KernelEntry participant DecompressionCode participant DecompressionFunction participant KernelStartup BootLoader->>KernelEntry: 跳轉到內核入口點 KernelEntry->>DecompressionCode: 跳轉到解壓縮代碼 DecompressionCode->>DecompressionFunction: 調用解壓縮函數 DecompressionFunction->>DecompressionFunction: 解壓內核映像 DecompressionFunction->>DecompressionCode: 返回解壓結果 DecompressionCode->>KernelStartup: 跳轉到內核啟動

3.1 內核映像載入到記憶體中

啟動載入程式(Bootloader)負責將壓縮的內核映像載入到記憶體中,並準備好啟動內核的環境。

  • 載入內核和initrd:GRUB 會根據配置文件(通常是 grub.cfg)載入壓縮的內核映像和可選的 initrd/initramfs 文件。
  • 設置內核參數:GRUB 會設置內核啟動參數,這些參數可以通過命令行傳遞給內核。
  • 跳轉到內核入口點:GRUB 將控制權轉移到內核映像的入口點。對於 x86 架構,這個入口點通常在內核映像的開頭。

3.1.1 啟動BootLoader

以BIOS為例

  • CPU在重置後執行的第一條指令的記憶體地址0xfffffff0,它包含一個 jump 指令,這個指令通常指向BIOS入口點。
  • BIOS會進行一系列硬體初始化和自檢,然後根據設置(例如啟動順序)選擇一個啟動設備(如硬碟、光碟、USB 等)
  • 將控制權轉移到啟動設備的啟動扇區代碼。

3.1.2 載入內核文件

  • 啟動設備的啟動扇區代碼被執行,通常這段代碼非常小,只占用一個扇區(512位元組)。
  • 啟動扇區代碼負責完成一些基本的初始化操作,然後跳轉到更複雜的引導載入程式,如 GRUB 的核心映像(core image)。
  • 核心映像開始執行,它負責進一步的初始化操作,如載入GRUB的模塊和配置文件(grub.cfg)。
  • 根據grub.cfg文件中的配置,GRUB載入壓縮的內核映像(vmlinuz)和可選的initrd/initramfs文件。
  • 內核映像載入完成後,GRUB 將控制權轉移給內核的入口點代碼,完成控制權從 BIOS 到內核的轉移。

3.2 內核解壓

以下以x86系統為例

3.2.1 關鍵文件和代碼路徑

  • arch/x86/boot/header.S:啟動代碼的彙編部分,定義了內核入口點。
  • arch/x86/boot/compressed/head_64.S 和 arch/x86/boot/compressed/misc.c:解壓縮代碼。
  • arch/x86/kernel/head_64.S 和 arch/x86/kernel/head.c:解壓後的內核啟動代碼。

3.2.2 主要步驟

3.2.2.1 啟動載入程式跳轉到內核入口點:
  • BootLoader根據grub.cfg文件中的配置載入內核映像(vmlinuz)到記憶體,並跳轉到內核映像的入口點,即內核代碼的起始地址。
3.2.2.2 解壓縮程式的初始化:
  • 內核入口點代碼(在 header.S 中)會設置初始的 CPU 狀態和記憶體環境,然後跳轉到解壓縮代碼的入口。
  • 32位方法startup_32,64位方法startup_64。(長模式的32到64轉換這裡不做討論,有興趣的可以自行查閱資料)
ENTRY(startup_32)
// 設置 CPU 狀態和記憶體環境
jmp decompress_kernel // 跳轉到解壓縮代碼
3.2.2.3 解壓縮代碼執行:
  • 解壓縮代碼的入口點在 arch/x86/boot/compressed/head_64.S 中。
  • arch/x86/boot/compressed/head_64.S 會設置解壓環境,如設置段寄存器、建立臨時堆棧等。
ENTRY(decompress_kernel)
// 設置硬體環境
// 調用解壓縮函數入口方法
jmp decompress_kernel_method
3.2.2.4 調用解壓縮函數:
  • 在arch/x86/boot/compressed/misc.c 中,decompress_kernel 函數負責選擇解壓演算法並解壓內核映像。
void decompress_kernel(...) {
    // 選擇解壓演算法
    // 調用相應的解壓函數
    decompress_method(); // 調用特定的解壓演算法,如 inflate()
}
3.2.2.5 解壓縮完成後跳轉到內核入口:
  • 解壓完成後,解壓縮代碼會跳轉到解壓後的內核入口點。
  • arch/x86/boot/compressed/head_64.S中定義了一個跳轉指令,內核入口點的地址載入到寄存器中(例如 %eax),通常是內核主函數(start_kernel),跳轉後即將控制權轉移到解壓後的內核代碼。
jmp *%eax

3.2.3 名詞解釋

3.2.3.1 跳轉入口點和控制權轉移
  • 在技術實現上跳抓入口點和控制權轉移是一致的,都是通過改變程式計數器(Program Counter,PC)或指令指針(Instruction Pointer,IP)的值來實現的。

程式計數器或指令指針是一個特殊的寄存器,用於存儲正在執行的指令的記憶體地址。當處理器執行一條指令時,程式計數器會自動遞增到下一條指令的地址,從而控制執行流程。這樣就實現了執行流程的轉移,從而使得程式執行從一個代碼段轉移到另一個代碼段。

  • "跳轉到入口點"強調了執行流程從某個特定的位置(入口點)開始執行,而"控制權轉移"則更加廣泛地描述了執行流程從一個執行上下文到另一個執行上下文的轉移過程。
  • 在內核載入和啟動的上下文中,這兩個術語通常可以互換使用,因為在設置了內核入口點後,執行流程的轉移也意味著控制權的轉移。
3.2.3.2 initrd/initramfs文件

載入 vmlinuz(Linux 內核映像)時,通常還會載入 initrd(initial ramdisk)或 initramfs(initial ram filesystem)文件。initrd 和 initramfs 文件的主要作用是在內核啟動的早期階段提供一個臨時的根文件系統,幫助內核完成啟動過程。

特性如下

  • 硬體驅動支持: 在系統啟動時,內核可能需要載入某些硬體驅動程式(如文件系統驅動、磁碟驅動、網路驅動等)來訪問根文件系統。這些驅動程式可能並未內置在內核映像中,而是作為模塊存在。initrd/initramfs 提供了一個早期的文件系統,內核可以從中載入必要的模塊。
  • 根文件系統掛載:在一些複雜的存儲配置中,如 LVM(Logical Volume Manager)、RAID、加密文件系統等,內核需要在掛載實際根文件系統之前進行一些初始化操作。這些操作通常通過 initrd/initramfs 中的腳本完成。
  • 通用內核:發行版通常提供通用內核以支持多種硬體配置。使用 initrd/initramfs 可以在啟動時動態載入適配不同硬體配置的模塊,而無需為每種硬體配置編譯一個特定的內核。

載入過程

  • 啟動載入程式(BootLoader)將內核映像和initrd/initramfs文件載入到記憶體中,並將控制權交給內核。
  • 內核啟動時會識別並載入initrd/initramfs文件,將其作為初始根文件系統掛載。
  • 內核從臨時根文件系統中載入必要的模塊並運行初始化腳本。
  • 初始化腳本完成必要的硬體初始化和配置後,會掛載實際的根文件系統(如 /dev/sda1)。
  • 初始化腳本切換到實際根文件系統,然後移除initrd/initramfs文件。

三、內核啟動(start_kernel)

start_kernel是Linux內核中非常重要的一個函數,它是整個內核初始化的核心函數,負責初始化內核的各個子系統、驅動程式以及其他關鍵組件,並最終將控制權轉移到用戶空間。

1.start_kernel方法介紹

1.1 第一個C函數的位置

start_kernel方法的定義通常位於init/main.c文件中,也是Linux啟動過程中執行的第一個C函數

1.2 主要功能

  • 初始化內核的基本設置:如記憶體管理、進程管理等。
  • 初始化各個子系統:如文件系統、網路子系統、設備驅動程式等。
  • 啟動第一個用戶進程:將控制權從內核轉移到用戶空間。

2.start_kernel源碼解析

asmlinkage __visible void __init start_kernel(void)  
{  
    char *command_line;  
    extern const struct kernel_param __start___param[], __stop___param[];  
  
    /* ... 其他初始化代碼 ... */  
  
    /* 設置頁表和記憶體管理 */  
    paging_init();  
    mem_init();  
    kmem_cache_init();  
  
    /* 設備和驅動程式初始化 */  
    driver_init();  
    init_irq_proc();  
    softirq_init();  
    time_init();  
    console_init();  
  
    /* 文件系統初始化 */  
    vfs_caches_init_early();  
    mnt_init();  
    init_rootfs();  
    init_mount_tree();  
  
    /* 初始化進程 */  
    pid_cache_init();  
    proc_caches_init();  
    /* 啟動 init 進程 */
    rest_init();  
  
    /* ... 其他初始化代碼 ... */  
  
    /* 調用內核參數解析函數 */  
    kernel_param_init(karg_strings, num_args);  
  
    /* ... 其他初始化代碼 ... */  
  
    /* 永遠不會返回 */  
    cpu_idle();  
}

四、啟動初始進程(init process)

1.進程概念介紹

1.1 內核進程(Kernel Thread)和用戶進程(User Process)

1.1.1 內核進程(Kernel Thread)

內核進程是由內核創建和調度的線程,運行在內核態,用於處理內核的各類任務。與用戶進程不同,內核進程不直接與用戶空間交互,主要用於執行內核內部的工作,如處理中斷、管理設備、調度任務等。

  • 運行空間:內核進程運行在內核地址空間,而普通用戶進程運行在用戶地址空間。
  • 許可權:內核進程可以直接訪問內核數據結構,而用戶進程通過系統調用與內核交互。
  • 交互:內核進程通常不與用戶交互,其生命周期完全由內核管理。
  • 創建方式:內核進程的創建通常通過kernel_thread函數實現。

註意內核進程是獨立的,與0、1、2號進程無關

1.1.2 用戶進程(User Process)

用戶進程是在用戶空間中執行的進程,用戶通過編寫和執行應用程式來創建用戶進程。用戶進程通過系統調用與內核交互,進行資源分配、文件操作、網路通信等。

  • 運行空間:用戶進程運行在用戶態,受限於用戶空間的許可權,不能直接訪問硬體和內核數據結構。
  • 許可權:內核線程運行在內核態,具有更高的許可權,能夠直接操作內核資源。

1.2 0號進程、1號進程、2號進程

  • 0號進程:是內核進程,運行在內核態,負責在系統空閑時執行。
  • 1號進程:是用戶進程,雖然最初由內核創建,但主要運行在用戶態,負責系統初始化和管理用戶空間的其他用戶進程。
  • 2號進程:是內核進程,運行在內核態,負責創建和管理其他內核線程。這些內核線程通常用於執行內核中的非同步任務,如磁碟I/O、網路操作等。

1.2.1 0號進程(swapper/idle/空閑進程)

  • 0號進程是Linux啟動的第一個進程,它的task_struct的comm欄位為"swapper",也稱為swapper進程、idel進程、空閑進程。
  • 0號進程是在系統引導過程中由內核創建的第一個進程。它的任務是進入空閑迴圈,當系統中沒有其他可運行的進程時,它會被調度執行,以避免CPU閑置。

0號進程(idle進程)是在系統引導過程中,由內核初始化代碼創建的。在x86架構中,這個過程發生在彙編啟動代碼(通常在arch/x86/kernel/head.S中),該代碼會設置基本的CPU和記憶體環境,然後跳轉到C語言的start_kernel函數。

1.2.2 1號進程(init進程)和2號進程(kthreadd進程)

  • 1號進程和2號進程都是在rest_init函數中創建的
  • 1號進程通過kernel_init創建
  • 2號進程通過kthreadd創建

2.rest_init函數-初始化入口

rest_init函數負責創建初始進程併進行一些進一步的初始化工作。其代碼實現如下:

static noinline void __ref rest_init(void)
{
    // 通知RCU(Read-Copy Update)子系統,調度器即將開始。這是確保RCU在調度器開始運行前正確初始化的關鍵步驟。
    rcu_scheduler_starting();
    // 創建pid=1的1號進程
    pid = kernel_thread(kernel_init, NULL, CLONE_FS);
    /** 處理1號進程相關代碼 **/
    
    // 創建pid=2的2號進程
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    /** 處理2號進程相關代碼 **/
    /** 其他初始化代碼 **/
}

2.1 kernel_thread函數

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
    return do_fork(flags | CLONE_VM | CLONE_UNTRACED, (unsigned long)fn,
                   (unsigned long)arg, NULL, NULL, 0);
}
  • do_fork:這是內核中實現創建新進程(或線程)的核心函數。通過該函數,內核可以複製當前進程的上下文,生成一個新的進程(或線程)。

2.2 kernel_init函數 - 初始化1號進程(init)

kernel_init負責啟動初始用戶空間進程(/sbin/init或指定的init進程)。

static int __ref kernel_init(void *unused)
{
    /** 其他初始化代碼 **/
    // 啟動用戶空間進程
    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
    } else if (execute_command) {
        run_init_process(execute_command);
    } else {
        run_init_process("/sbin/init");
    }

    return 0;
}

3. 系統啟動

init進程啟動後,通過後續工作完成了操作系統的載入和啟動

3.1 系統初始化腳本

init進程讀取系統的初始化腳本(如/etc/inittab、/etc/init.d/腳本)或systemd的單元文件(unit files),執行系統初始化任務。這包括設置系統環境、掛載文件系統、啟動網路服務、啟動守護進程等。

3.2 啟動用戶界面

  • 圖形登錄管理器:如果系統配置為使用圖形界面,init進程會啟動圖形登錄管理器(如GDM、LightDM、SDDM)。這些登錄管理器負責提供圖形化的登錄界面,供用戶輸入用戶名和密碼。
  • 啟動桌面環境:用戶登錄成功後,登錄管理器會啟動用戶的桌面環境(如GNOME、KDE、Xfce)。桌面環境提供完整的圖形用戶界面,允許用戶運行應用程式、管理文件、設置系統等。

3.3 圖形界面啟動流程(systemd示例)

  • systemd初始化:systemd作為init進程啟動,讀取其配置文件(通常在/lib/systemd/system/和/etc/systemd/system/)。
  • 啟動目標(target):systemd根據配置文件啟動系統目標(如graphical.target)。graphical.target包含了啟動圖形界面所需的所有服務。
  • 啟動顯示管理器:systemd啟動圖形顯示管理器服務(如gdm.service、lightdm.service)。
  • 顯示管理器運行:顯示管理器提供圖形登錄界面,用戶登錄後啟動用戶會話。
  • 啟動桌面環境:用戶會話啟動後,顯示管理器啟動桌面環境,用戶進入圖形用戶界面。

五、流程圖總結

前面使用了大量文字來說明,這裡使用一張流程圖來做概要總結

graph TD A[電腦通電] -->|載入BIOS| C(BIOS) C -->|解壓vmlinuz| D(內核Kernel) D -->|start_kenerl| E(init進程) E -->|載入操作系統| F[登錄界面]
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Percona Toolkit 神器全攻略(實用類) Percona Toolkit 神器全攻略系列共八篇,前文回顧: 前文回顧 Percona Toolkit 神器全攻略 全文約定:$為命令提示符、greatsql>為GreatSQL資料庫提示符。在後續閱讀中,依據此約定進行理解與操作 實用類 在 ...
  • MySQL語句優化包括創建合適的索引、使用覆蓋索引、避免不必要的子查詢、通過 EXPLAIN 分析查詢計劃、進行批量操作、減少鎖定範圍、使用預編譯語句和查詢緩存,以顯著提高查詢性能和資料庫運行效率。 ...
  • 恭喜Apache SeaTunnel社區又迎來一位PMC Member@liugddx!在社區持續活躍的兩年間,大家經常看到這位開源愛好者出現在社區的各種活動中,為項目和社區發展添磚加瓦。如今成為項目PMC Member,意味著在社區中的責任更重了,他有什麼感想呢?對於社區未來的發展,他希望從哪些角 ...
  • 轉載自劉茫茫看山 問題背景 某天我們的租戶反饋資料庫連接缺少必要的驅動,我們通過日誌查看確實是缺少部分資料庫的驅動,因為DolphinScheduler預設只帶了Oracle和MySQL的驅動,並且需要將pom文件中的test模式去掉才可以在打包的時候引入。我們的任務量比較大,在3.0存在容錯機制的 ...
  • 參數介紹: MySQL社區版MySQL 5.7.8開始,新增了MAX_EXECUTION_TIME這個系統變數,它用來限制SQL語句的執行時間,確切來說是限制只讀SELECT語句。如果查詢語句的執行時長超過這個閾值,MySQL將自動停止該SQL語句的執行。如果其值為 0,表示不啟用該超時限制功能。該 ...
  • 前言 前端函數和應用側函數相互調用是指前端頁面中的JavaScript函數和應用程式側的函數之間進行相互調用。 在前端開發中,常常會使用JavaScript函數來處理用戶的交互事件和操作。這些函數可以在前端頁面中定義,例如通過事件監聽器或者按鈕點擊事件來觸發函數的執行。這些前端函數可以使用DOM ...
  • 前言 Web是一種基於互聯網的技術和資源的網路服務系統。它是指由許多互連的電腦組成的全球性電腦網路,使用戶能夠通過瀏覽器訪問和互動式使用各種信息和資源,如網頁、文檔、圖片、視頻、音頻等。通過Web,用戶可以瀏覽網頁、發送電子郵件、參與線上社交網路、進行線上購物等各種活動。Web的核心技術包括 ...
  • 在 recyclerview 中,想要無論滑動到哪,每次按遙控器落焦,需要落焦在左側第一個 item 上面,如果不能觸屏還好,觸屏會導致焦點丟失 根據系統的反饋,如果你滑動了列表,剛好列表的 item 卡在一半的位置,此時系統的落焦規則,不一定會到第一個 之前試過一個效果一般的方案,就是通過 fin ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...