Linux用戶搶占和內核搶占詳解(概念, 實現和觸發時機)--Linux進程的管理與調度(二十)

来源:https://www.cnblogs.com/linhaostudy/archive/2018/11/01/9888910.html
-Advertisement-
Play Games

1 非搶占式和可搶占式內核 為了簡化問題,我使用嵌入式實時系統uC/OS作為例子 首先要指出的是,uC/OS只有內核態,沒有用戶態,這和Linux不一樣 多任務系統中, 內核負責管理各個任務, 或者說為每個任務分配CPU時間, 並且負責任務之間的通訊. 內核提供的基本服務是任務切換. 調度(Sche ...


1 非搶占式和可搶占式內核

為了簡化問題,我使用嵌入式實時系統uC/OS作為例子

首先要指出的是,uC/OS只有內核態,沒有用戶態,這和Linux不一樣

多任務系統中, 內核負責管理各個任務, 或者說為每個任務分配CPU時間, 並且負責任務之間的通訊.

內核提供的基本服務是任務切換. 調度(Scheduler),英文還有一詞叫dispatcher, 也是調度的意思.

這是內核的主要職責之一, 就是要決定該輪到哪個任務運行了. 多數實時內核是基於優先順序調度法的, 每個任務根據其重要程度的不同被賦予一定的優先順序. 基於優先順序的調度法指,CPU總是讓處在就緒態的優先順序最高的任務先運行. 然而, 究竟何時讓高優先順序任務掌握CPU的使用權, 有兩種不同的情況, 這要看用的是什麼類型的內核, 是不可剝奪型的還是可剝奪型內核

1.1 非搶占式內核

非搶占式內核是由任務主動放棄CPU的使用權

非搶占式調度法也稱作合作型多任務, 各個任務彼此合作共用一個CPU. 非同步事件還是由中斷服務來處理. 中斷服務可以使一個高優先順序的任務由掛起狀態變為就緒狀態.

非搶占式內核的優點有

  • 中斷響應快(與搶占式內核比較);
  • 允許使用不可重入函數;
  • 幾乎不需要使用信號量保護共用數據, 運行的任務占有CPU,不必擔心被別的任務搶占。這不是絕對的,在印表機的使用上,仍需要滿足互斥條件。

非搶占式內核的缺點有

  • 任務響應時間慢。高優先順序的任務已經進入就緒態,但還不能運行,要等到當前運行著的任務釋放CPU
  • 非搶占式內核的任務級響應時間是不確定的,不知道什麼時候最高優先順序的任務才能拿到CPU的控制權,完全取決於應用程式什麼時候釋放CPU

1.2 搶占式內核

使用搶占式內核可以保證系統響應時間. 最高優先順序的任務一旦就緒, 總能得到CPU的使用權。當一個運行著的任務使一個比它優先順序高的任務進入了就緒態, 當前任務的CPU使用權就會被剝奪,或者說被掛起了,那個高優先順序的任務立刻得到了CPU的控制權。如果是中斷服務子程式使一個高優先順序的任務進入就緒態,中斷完成時,中斷了的任務被掛起,優先順序高的那個任務開始運行。

搶占式內核如下圖所示

搶占式內核的優點有

  • 使用搶占式內核,最高優先順序的任務什麼時候可以執行,可以得到CPU的使用權是可知的。使用搶占式內核使得任務級響應時間得以最優化。

搶占式內核的缺點有:

  • 不能直接使用不可重入型函數。調用不可重入函數時,要滿足互斥條件,這點可以使用互斥型信號量來實現。如果調用不可重入型函數時,低優先順序的任務CPU的使用權被高優先順序任務剝奪,不可重入型函數中的數據有可能被破壞。

2 linux用戶搶占

2.1 linux用戶搶占

當內核即將返回用戶空間時, 內核會檢查need_resched是否設置, 如果設置, 則調用schedule(),此時,發生用戶搶占.

2.2 need_resched標識

內核如何檢查一個進程是否需要被調度呢?

內核在即將返回用戶空間時檢查進程是否需要重新調度,如果設置了,就會發生調度, 這被稱為用戶搶占, 因此內核在thread_info的flag中設置了一個標識來標誌進程是否需要重新調度, 即重新調度need_resched標識TIF_NEED_RESCHED

並提供了一些設置可檢測的函數

函數 描述 定義
set_tsk_need_resched 設置指定進程中的need_resched標誌 include/linux/sched.h, L2920
clear_tsk_need_resched 清除指定進程中的need_resched標誌 include/linux/sched.h, L2926
test_tsk_need_resched 檢查指定進程need_resched標誌 include/linux/sched.h, L2931

而我們內核中調度時常用的need_resched()函數檢查進程是否需要被重新調度其實就是通過test_tsk_need_resched實現的, 其定義如下所示

// http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.6#L3093
static __always_inline bool need_resched(void)
{
    return unlikely(tif_need_resched());
}

// http://lxr.free-electrons.com/source/include/linux/thread_info.h?v=4.6#L106
#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED)

2.3 用戶搶占的發生時機(什麼時候需要重新調度need_resched)

一般來說,用戶搶占發生幾下情況:

  • 從系統調用返回用戶空間;
  • 從中斷(異常)處理程式返回用戶空間

從這裡我們可以看到, 用戶搶占是發生在用戶空間的搶占現象.

更詳細的觸發條件如下所示, 其實不外乎就是前面所說的兩種情況: 從系統調用或者中斷返回用戶空間

  1. 時鐘中斷處理常式檢查當前任務的時間片,當任務的時間片消耗完時,scheduler_tick()函數就會設置need_resched標誌;
  2. 信號量、等到隊列、completion等機制喚醒時都是基於waitqueue的,而waitqueue的喚醒函數為default_wake_function,其調用try_to_wake_up將被喚醒的任務更改為就緒狀態並設置need_resched標誌。
  3. 設置用戶進程的nice值時,可能會使高優先順序的任務進入就緒狀態;
  4. 改變任務的優先順序時,可能會使高優先順序的任務進入就緒狀態;
  5. 新建一個任務時,可能會使高優先順序的任務進入就緒狀態;
  6. 對CPU(SMP)進行負載均衡時,當前任務可能需要放到另外一個CPU上運行

3 linux內核搶占

3.1 內核搶占的概念

對比用戶搶占, 顧名思義, 內核搶占就是指一個在內核態運行的進程, 可能在執行內核函數期間被另一個進程取代.

3.2 為什麼linux需要內核搶占

linux系統中, 進程在系統調用後返回用戶態之前, 或者是內核中某些特定的點上, 都會調用調度器. 這確保除了一些明確指定的情況之外, 內核是無法中斷的, 這不同於用戶進程.

在編譯內核時如果啟用了對內核搶占的支持, 則可以解決這些問題. 如果高優先順序進程有事情需要完成, 那麼在啟用了內核搶占的情況下, 不僅用戶空間應用程式可以被中斷, 內核也可以被中斷,

linux內核搶占是在Linux2.5.4版本發佈時加入的, 儘管使內核可搶占需要的改動特別少, 但是該機制不像搶占用戶空間進程那樣容易實現. 如果內核無法一次性完成某些操作(例如, 對數據結構的操作), 那麼可能出現靜態條件而使得系統不一致.

內核搶占和用戶層進程被其他進程搶占是兩個不同的概念, 內核搶占主要是從實時系統中引入的, 在非實時系統中的確也能提高系統的響應速度, 但也不是在所有情況下都是最優的,因為搶占也需要調度和同步開銷,在某些情況下甚至要關閉內核搶占, 比如前面我們將主調度器的時候, linux內核在完成調度的過程中是關閉了內核搶占的.

內核不能再任意點被中斷, 幸運的是, 大多數不能中斷的點已經被SMP實現標識出來了. 並且在實現內核搶占時可以重用這些信息. 如果內核可以被搶占, 那麼單處理器系統也會像是一個SMP系統

3.3 內核搶占的發生時機

要滿足什麼條件,kernel才可以搶占一個任務的內核態呢?

  • 沒持有鎖。鎖是用於保護臨界區的,不能被搶占。
  • Kernel code可重入(reentrant)。因為kernel是SMP-safe的,所以滿足可重入性。

內核搶占發生的時機,一般發生在:

  1. 當從中斷處理程式正在執行,且返回內核空間之前。當一個中斷處理常式退出,在返回到內核態時(kernel-space)。這是隱式的調用schedule()函數,當前任務沒有主動放棄CPU使用權,而是被剝奪了CPU使用權。
  2. 當內核代碼再一次具有可搶占性的時候,如解鎖(spin_unlock_bh)及使能軟中斷(local_bh_enable)等, 此時當kernel code從不可搶占狀態變為可搶占狀態時(preemptible again)。也就是preempt_count從正整數變為0時。這也是隱式的調用schedule()函數
  3. 如果內核中的任務顯式的調用schedule(), 任務主動放棄CPU使用權
  4. 如果內核中的任務阻塞(這同樣也會導致調用schedule()), 導致需要調用schedule()函數。任務主動放棄CPU使用權

內核搶占,並不是在任何一個地方都可以發生,以下情況不能發生

  1. 內核正進行中斷處理。在Linux內核中進程不能搶占中斷(中斷只能被其他中斷中止、搶占,進程不能中止、搶占中斷),在中斷常式中不允許進行進程調度。進程調度函數schedule()會對此作出判斷,如果是在中斷中調用,會列印出錯信息。
  2. 內核正在進行中斷上下文的Bottom Half(中斷下半部,即軟中斷)處理。硬體中斷返回前會執行軟中斷,此時仍然處於中斷上下文中。如果此時正在執行其它軟中斷,則不再執行該軟中斷。
  3. 內核的代碼段正持有spinlock自旋鎖、writelock/readlock讀寫鎖等鎖,處乾這些鎖的保護狀態中。內核中的這些鎖是為了在SMP系統中短時間內保證不同CPU上運行的進程併發執行的正確性。當持有這些鎖時,內核不應該被搶占。
  4. 內核正在執行調度程式Scheduler。搶占的原因就是為了進行新的調度,沒有理由將調度程式搶占掉再運行調度程式。
  5. 內核正在對每個CPU“私有”的數據結構操作(Per-CPU date structures)。在SMP中,對於per-CPU數據結構未用spinlocks保護,因為這些數據結構隱含地被保護了(不同的CPU有不一樣的per-CPU數據,其他CPU上運行的進程不會用到另一個CPU的per-CPU數據)。但是如果允許搶占,但一個進程被搶占後重新調度,有可能調度到其他的CPU上去,這時定義的Per-CPU變數就會有問題,這時應禁搶占。

4 內核搶占的實現

4.1 內核如何跟蹤它能否被搶占?

前面我們提到了, 系統中每個進程都有一個特定於體繫結構的struct thread_info結構, 用戶層程式被調度的時候會檢查struct thread_info中的need_resched標識TLF_NEED_RESCHED標識來檢查自己是否需要被重新調度.

自然內核搶占·也可以應用同樣的方法被實現, linux內核在thread_info結構中添加了一個自旋鎖標識preempt_count, 稱為搶占計數器(preemption counter).

struct thread_info
{
    /*  ......  */
    int preempt_count;   /* 0 => preemptable, <0 => BUG */
    /*  ......  */
}
preempt_count值 描述
0 禁止內核搶占, 其值標記了使用preempt_count的臨界區的數目
0 開啟內核搶占
<0 鎖為負值, 內核出現錯誤

內核自然也提供了一些函數或者巨集, 用來開啟, 關閉以及檢測搶占計數器preempt_count的值, 這些通用的函數定義在include/asm-generic/preempt.h, 而某些架構也定義了自己的介面, 比如x86架構/arch/x86/include/asm/preempt.h

函數 描述 定義
preempt_count 獲取當前current進程搶占計數器的值 include/asm-generic/preempt.h, line 8
preempt_count_ptr 返回指向當前current進程的搶占計數器的指針 include/asm-generic/preempt.h, line 18
init_task_preempt_count 初始化task的搶占計數器為FORK_PREEMPT_COUNT include/asm-generic/preempt.h, line 26
init_idle_preempt_count 初始化task的搶占計數器為PREEMPT_ENABLED include/asm-generic/preempt.h, line 30
preempt_count_add 將增加current的搶占計數器增加val include/linux/preempt.h, line 132
preempt_count_sub 將增加current的搶占計數器減少val include/linux/preempt.h, line 133
preempt_count_dec_and_test 將current的搶占計數器減少1, 然後看是否可以進程內核搶占, 即檢查搶占計數器是否為0(允許搶占), 同時檢查tif_need_resched標識是否為真 include/linux/preempt.h, line 134, 61
preempt_count_inc current的搶占計數器增加1 include/linux/preempt.h, line 140
preempt_count_dec current的搶占計數器減少1 include/linux/preempt.h, line 141

還有其他函數可用於開啟和關閉內核搶占

函數 描述 定義
preempt_disable 通過preempt_count_inc來停用內核搶占, 並且通過路障barrier同步來避免編譯器的優化 include/linux/preempt.h, line 145
preempt_enable preempt_count_dec_and_test啟用內核搶占, 然後通過__preempt_schedule檢測是夠有必要進行調度 include/linux/preempt.h, line 162
preempt_enable_no_resched 開啟搶占, 但是不進行重調度 include/linuxc/preempt.h, line 151
preempt_check_resched 調用__preempt_schedule檢測是夠有必要進行調度 include/linux/preempt.h, line 176
should_resched 檢查current的搶占計數器是否為參數preempt_offset的值, 同時檢查 tif_need_resched是否為真 include/linux/preempt.h, line 74
preemptible 檢查是否可以內核搶占, 檢查搶占計數器是否為0, 以及是否停用了中斷 /include/linux/preempt.h, line159

4.2 內核如何知道是否需要搶占?

首先必須設置了TLF_NEED_RESCHED標識來通知內核有進程在等待得到CPU時間, 然後會在判斷搶占計數器preempt_count是否為0, 這個工作往往通過preempt_check_resched或者其相關來實現

4.2.1 重新啟用內核搶占時使用preempt_schedule檢查搶占

在內核停用搶占後重新啟用時, 檢測是否有進程打算搶占當前執行的內核代碼, 是一個比較好的時機, 如果是這樣, 應該儘快完成, 則無需等待下一次對調度器的例行調用.

搶占機制中主要的函數是preempt_schedule, 設置了TIF_NEED_RESCHED標誌並不能保證可以搶占內核, 內核可能處於臨界區, 不能被干擾

//  http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L3307

/*
 * this is the entry point to schedule() from in-kernel preemption
 * off of preempt_enable. Kernel preemptions off return from interrupt
 * occur there and call schedule directly.
 */
asmlinkage __visible void __sched notrace preempt_schedule(void)
{
    /*
     * If there is a non-zero preempt_count or interrupts are disabled,
     * we do not want to preempt the current task. Just return..
     */
     /* !preemptible() => preempt_count() != 0 || irqs_disabled()
      * 如果搶占計數器大於0, 那麼搶占被停用, 該函數立即返回
      * 如果
     */
    if (likely(!preemptible())) 
        return;

    preempt_schedule_common();
}
NOKPROBE_SYMBOL(preempt_schedule);
EXPORT_SYMBOL(preempt_schedule);

// http://lxr.free-electrons.com/source/include/linux/preempt.h?v=4.6#L159
 #define preemptible()   (preempt_count() == 0 && !irqs_disabled())

!preemptible => preempt_count() != 0 || irqs_disabled()表明

  • 如果搶占計數器大於0, 那麼搶占仍然是被停用的, 因此內核不能被打斷, 該函數立即結束.
  • 如果在某些重要的點上內核停用了硬體中斷, 以保證一次性完成相關的處理, 那麼搶占也是不可能的.irqs_disabled會檢測是否停用了中斷. 如果已經停用, 則內核不能被搶占

接著如果可以被搶占, 則執行如下步驟


static void __sched notrace preempt_schedule_common(void)
{
    do {
        /*
            preempt_disable_notrace定義在
            http://lxr.free-electrons.com/source/include/linux/preempt.h?v=4.6#L198             等待於__preempt_count_inc();
        */
        preempt_disable_notrace();
        /*  完成一次調度  */
        __schedule(true);

        /*
            preempt_enable_no_resched_notrace
            http://lxr.free-electrons.com/source/include/linux/preempt.h?v=4.6#L204
            等價於__preempt_count_dec
        */
        preempt_enable_no_resched_notrace();

        /*
         * Check again in case we missed a preemption opportunity
         * between schedule and now.
         * 再次檢查, 以免在__scheudle和當前點之間錯過了搶占的時機
         */
    } while (need_resched());
}

我們可以看到, 內核在增加了搶占計數器的計數後, 用__schedule進行了一次調度, 參數傳入preempt = true, 表明調度不是以普通的方式引發的, 而是由於內核搶占. 在內核重調度之後, 代碼流程回到當前進程, 那麼就井搶占計數器減少1.

4.2.2 中斷之後返回內核態時通過preempt_schedule_irq觸發

上面preempt_schedule只是觸發內核搶占的一種方法, 另一種激活搶占的方式是在處理了一個硬體中斷請求之後. 如果處理器在處理中斷請求後返回內核態(返回用戶態則沒有影響), 特定體繫結構的彙編常式會檢查搶占計數器是否為0, 即是否允許搶占, 以及是否設置了重調度標識, 類似於preempt_schedule的處理. 如果兩個條件都滿足則通過preempt_schedule_irq調用調度器, 此時表明搶占請求發自中斷上下文

該函數與preempt_schedule的本質區別在於: preempt_schedule_irq調用時停用了中斷, 防止終端造成的遞歸調用, 其定義在kernel/sched/core.c, line3360

/*
 * this is the entry point to schedule() from kernel preemption
 * off of irq context.
 * Note, that this is called and return with irqs disabled. This will
 * protect us against recursive calling from irq.
 */
asmlinkage __visible void __sched preempt_schedule_irq(void)
{
    enum ctx_state prev_state;

    /* Catch callers which need to be fixed */
    BUG_ON(preempt_count() || !irqs_disabled());

    prev_state = exception_enter();

    do {
        preempt_disable();
        local_irq_enable();
        __schedule(true);
        local_irq_disable();
        sched_preempt_enable_no_resched();
    } while (need_resched());

    exception_exit(prev_state);
}

4.2.3 PREEMPT_ACTIVE標識位和PREEMPT_DISABLE_OFFSET

之前的內核版本中, 搶占計數器中於一個標識位PREEMPT_ACTIVE, 這個位設置後即標識了可以進行內核搶占, 使得preempt_count有一個很大的值, 這樣就不受普通的搶占計數器加1操作的影響了

PREEMPT_ACTIVE的引入, 參見PREEMPT_ACTIVE: add default defines

然後也為其提供了一些置位的函數,其實就是將preempt_count加上/減去一個很大的數, 參見preempt: Disable preemption from preempt_schedule*() callers

//  http://lxr.free-electrons.com/source/include/linux/preempt.h?v=4.3#L58
#define PREEMPT_ACTIVE_BITS     1
#define PREEMPT_ACTIVE_SHIFT    (NMI_SHIFT + NMI_BITS)
#define PREEMPT_ACTIVE  (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT)

但是在linux-4.4版本之後移除了這個標誌, 取而代之的是在linux-4.2時引入的PREEMPT_DISABLE_OFFSET

參見

Rename PREEMPT_CHECK_OFFSET to PREEMPT_DISABLE_OFFSET

preempt: Rename PREEMPT_CHECK_OFFSET to PREEMPT_DISABLE_OFFSET

preempt: Remove PREEMPT_ACTIVE unmasking off in_atomic()

sched: Kill PREEMPT_ACTIVE

sched: Stop setting PREEMPT_ACTIVE

參考

內核隨記(二)——內核搶占與中斷返回

PREEMPT_ACTIVE

5 總結

一般來說,CPU在任何時刻都處於以下三種情況之一:

  1. 運行於用戶空間,執行用戶進程
  2. 運行於內核空間,處於進程上下文
  3. 運行於內核空間,處於中斷上下文

5.1 用戶搶占

一般來說, 當進程從系統調用或者從中斷(異常)處理程式返回用戶空間時會觸發主調度器進行用戶搶占

  • 從系統調用返回用戶空間
  • 從中斷(異常)處理程式返回用戶空間

為了對一個進程需要被調度進行標記, 內核在thread_info的flag中設置了一個標識來標誌進程是否需要重新調度, 即重新調度need_resched標識TIF_NEED_RESCHED, 內核在即將返回用戶空間時會檢查標識TIF_NEED_RESCHED標誌進程是否需要重新調度,如果設置了,就會發生調度, 這被稱為用戶搶占

5.2 內核搶占

如果內核處於相對耗時的操作中, 比如文件系統或者記憶體管理相關的任務, 這種行為可能會帶來問題. 這種情況下, 內核代替特定的進程執行相當長的時間, 而其他進程無法執行, 無法調度, 這就造成了系統的延遲增加, 用戶體驗到”緩慢”的響應. 因此linux內核引入了內核搶占.

linux內核通過在thread_info結構中添加了一個自旋鎖標識preempt_count, 稱為搶占計數器(preemption counter)來作為內核搶占的標記,

內核搶占的觸發大致也是兩類, 內核搶占關閉後重新開啟時, 中斷返回內核態時

  • 內核重新開啟內核搶占時使用preempt_schedule檢查內核搶占
  • 中斷之後返回內核態時通過preempt_schedule_irq觸發內核搶占

中斷之後返回內核態時通過preempt_schedule_irq觸發內核搶占


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

-Advertisement-
Play Games
更多相關文章
  • 設置防火牆iptables開放3306介面 在/etc/sysconfig下麵有個iptables文件,在控制台輸入命令 iptables -I INPUT -p tcp --dport 3306 -j ACCEPT 然後進行保存:service iptables save 更新防火牆規則:serv ...
  • 機緣巧合下,在快要畢業時找到了一份網路工程師的工作,對於學習通信工程的我來說,也不算是跨專業就業吧。在入職之前也瞭解了一下網路工程師的學習路徑,網路工程師是從事電腦信息系統的設計、建設、運行和維護工作,需掌握網路技術的理論知識和操作技能。然後我就開始了這方面的學習,比如網路的設計、CCNA等。 工 ...
  • 在某些應用場合我們可能需要通過一個設備通過WIFI將圖像傳到其它的機器進行顯示或者圖形分析,那怎麼可以低成本地實現呢?其實很簡單,我們只需要一塊 Raspberry Zero W 和一個RPI 攝像頭就行了,兩個加起來成本也只不過150左右。 這個組合不單單隻是實現一個圖傳,最重要的是Raspber ...
  • 參考博客:Docker CE 鏡像源站 參考博客:docker啟動異常driver not supported 1. 說明 之前部署docker服務的時候都是安裝最新的docker版本,並使用docker swarm部署大數據組件。 但是在近期的一次部署發現 docker 18.06.1 版本,在使 ...
  • ssh是什麼 ssh是一種通信協議,用於與另一臺電腦通信,有多個實現版本,openssh屬於開源實現 中間人攻擊 假設有中間人冒充用戶主機的情況 口令登錄 登錄成功後 登錄失敗的情況 公/私鑰對 公鑰登錄 失敗解決 ...
  • 1 - 在啟動grub菜單,選擇編輯選項啟動 2 - 按鍵盤e鍵,來進入編輯界面 3 - 找到Linux 16的那一行,將ro改為rw init=/sysroot/bin/sh 4 - 現在按下 Control+x ,使用單用戶模式啟動 5 - 現在,可以使用下麵的命令訪問系統 chroot /sy ...
  • ex2fsck 檢查ext2、ext3、ext4文件系統,如果系統已經掛載了,那麼不建議去檢查,因為這樣是不安全的。此命令的適用範圍:RedHat、RHEL、Ubuntu、CentOS、SUSE、openSUSE、Fedora。 1、語法 e2fsck [-pacnyrdfkvtDFV] [-b s ...
  • 查看方式 1、先切換到:cd usr/local/tomcat5/logs2、tail -f catalina.out3、Ctrl+c 是退出tail命令/alt+E+R重置 Linux系統中tail命令 實例1:顯示文件末尾內容 tail -n 5 log2014.log 顯示文件最後5行內容 實 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...