Linux CFS調度器之虛擬時鐘vruntime與調度延遲--Linux進程的管理與調度(二十六)

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

1 虛擬運行時間(今日內容提醒) 1.1 虛擬運行時間的引入 CFS為了實現公平,必須懲罰當前正在運行的進程,以使那些正在等待的進程下次被調度。 具體實現時,CFS通過每個進程的虛擬運行時間(vruntime)來衡量哪個進程最值得被調度。 CFS中的就緒隊列是一棵以vruntime為鍵值的紅黑樹,虛 ...


1 虛擬運行時間(今日內容提醒)

1.1 虛擬運行時間的引入

CFS為了實現公平,必須懲罰當前正在運行的進程,以使那些正在等待的進程下次被調度。

具體實現時,CFS通過每個進程的虛擬運行時間(vruntime)來衡量哪個進程最值得被調度。

CFS中的就緒隊列是一棵以vruntime為鍵值的紅黑樹,虛擬時間越小的進程越靠近整個紅黑樹的最左端。因此,調度器每次選擇位於紅黑樹最左端的那個進程,該進程的vruntime最小

虛擬運行時間是通過進程的實際運行時間和進程的權重(weight)計算出來的。

在CFS調度器中,將進程優先順序這個概念弱化,而是強調進程的權重。一個進程的權重越大,則說明這個進程更需要運行,因此它的虛擬運行時間就越小,這樣被調度的機會就越大。

那麼,在用戶態進程的優先順序nice值與CFS調度器中的權重又有什麼關係?在內核中通過prio_to_weight數組進行nice值和權重的轉換。

1.2 CFS虛擬時鐘

完全公平調度演算法CFS依賴於虛擬時鐘, 用以度量等待進程在完全公平系統中所能得到的CPU時間. 但是數據結構中任何地方都沒有找到虛擬時鐘. 這個是由於所有的必要信息都可以根據現存的實際時鐘和每個進程相關的負荷權重推算出來.

假設現在系統有A,B,C三個進程,A.weight=1,B.weight=2,C.weight=3.那麼我們可以計算出整個公平調度隊列的總權重是cfs_rq.weight = 6,很自然的想法就是,公平就是你在重量中占的比重的多少來拍你的重要性,那麼,A的重要性就是1/6,同理,B和C的重要性分別是2/6,3/6.很顯然C最重要就應改被先調度,而且占用的資源也應該最多,即假設A,B,C運行一遍的總時間假設是6個時間單位的話,A占1個單位,B占2個單位,C占三個單位。這就是CFS的公平策略.

CFS調度演算法的思想:理想狀態下每個進程都能獲得相同的時間片,並且同時運行在CPU上,但實際上一個CPU同一時刻運行的進程只能有一個。也就是說,當一個進程占用CPU時,其他進程就必須等待。CFS為了實現公平,必須懲罰當前正在運行的進程,以使那些正在等待的進程下次被調度.

具體實現時,CFS通過每個進程的虛擬運行時間(vruntime)來衡量哪個進程最值得被調度. CFS中的就緒隊列是一棵以vruntime為鍵值的紅黑樹,虛擬時間越小的進程越靠近整個紅黑樹的最左端。因此,調度器每次選擇位於紅黑樹最左端的那個進程,該進程的vruntime最小.

虛擬運行時間是通過進程的實際運行時間和進程的權重(weight)計算出來的。在CFS調度器中,將進程優先順序這個概念弱化,而是強調進程的權重。一個進程的權重越大,則說明這個進程更需要運行,因此它的虛擬運行時間就越小,這樣被調度的機會就越大。而,CFS調度器中的權重在內核是對用戶態進程的優先順序nice值, 通過prio_to_weight數組進行nice值和權重的轉換而計算出來的

2 虛擬時鐘相關的數據結構

2.1 調度實體的虛擬時鐘信息

為了實現完全公平調度,內核引入了虛擬時鐘(virtual clock)的概念,實際上我覺得這個虛擬時鐘為什叫虛擬的,是因為這個時鐘與具體的時鐘晶振沒有關係,他只不過是為了公平分配CPU時間而提出的一種時間量度,它與進程的權重有關,這裡就知道權重的作用了,權重越高,說明進程的優先順序比較高,進而該進程虛擬時鐘增長的就慢

既然虛擬時鐘是用來衡量調度實體(一個或者多個進程)的一種時間度量, 因此必須在調度實體中存儲其虛擬時鐘的信息

struct sched_entity
{
    struct load_weight      load;           /* for load-balancing負荷權重,這個決定了進程在CPU上的運行時間和被調度次數 */
    struct rb_node          run_node;
    unsigned int            on_rq;          /*  是否在就緒隊列上  */

    u64                     exec_start;         /*  上次啟動的時間*/

    u64                     sum_exec_runtime;
    u64                     vruntime;
    u64                     prev_sum_exec_runtime;
    /* rq on which this entity is (to be) queued: */
    struct cfs_rq           *cfs_rq;
    ...
};

sum_exec_runtime是用於記錄該進程的CPU消耗時間,這個是真實的CPU消耗時間。在進程撤銷時會將sum_exec_runtime保存到prev_sum_exec_runtime中

vruntime是本進程生命周期中在CPU上運行的虛擬時鐘。那麼何時應該更新這些時間呢?這是通過調用update_curr實現的, 該函數在多處調用.

2.2 就緒隊列上的虛擬時鐘信息

完全公平調度器類sched_fair_class主要負責管理普通進程, 在全局的CPU就讀隊列上存儲了在CFS的就緒隊列struct cfs_rq cfs

進程的就緒隊列中就存儲了CFS相關的虛擬運行時鐘的信息, struct cfs_rq定義如下:

struct cfs_rq
{
    struct load_weight load;   /*所有進程的累計負荷值*/
    unsigned long nr_running;  /*當前就緒隊列的進程數*/

    // ========================
    u64 min_vruntime;  //  隊列的虛擬時鐘, 
    // =======================
    struct rb_root tasks_timeline;  /*紅黑樹的頭結點*/
    struct rb_node *rb_leftmost;    /*紅黑樹的最左面節點*/

    struct sched_entity *curr;      /*當前執行進程的可調度實體*/
        ...
};

3 update_curr函數計算進程虛擬時間

所有與虛擬時鐘有關的計算都在update_curr中執行, 該函數在系統中各個不同地方調用, 包括周期性調度器在內.

update_curr的流程如下

  • 首先計算進程當前時間與上次啟動時間的差值
  • 通過負荷權重和當前時間模擬出進程的虛擬運行時鐘
  • 重新設置cfs的min_vruntime保持其單調性

3.1 計算時間差

首先, 該函數確定就緒隊列的當前執行進程, 並獲取主調度器就緒隊列的實際時鐘值, 該值在每個調度周期都會更新

/*  確定就緒隊列的當前執行進程curr  */
struct sched_entity *curr = cfs_rq->curr;

其中輔助函數rq_of用於確定與CFS就緒隊列相關的struct rq實例, 其定義在kernel/sched/fair.c, line 248

cfs_rq就緒隊列中存儲了指向就緒隊列的實例,參見kernel/sched/sched.h, line412, 而rq_of就返回了這個指向rq的指針, rq_of定義在kernel/sched/fair.c, line 249

rq_clock_task函數返回了運行隊列的clock_task成員.

/*  rq_of -=> return cfs_rq->rq 返回cfs隊列所在的全局就緒隊列  
*  rq_clock_task返回了rq的clock_task  */
u64 now = rq_clock_task(rq_of(cfs_rq));
u64 delta_exec;

如果就隊列上沒有進程在執行, 則顯然無事可做, 否則內核計算當前和上一次更新負荷權重時兩次的時間的差值

/*   如果就隊列上沒有進程在執行, 則顯然無事可做  */
if (unlikely(!curr))
    return;

/*  內核計算當前和上一次更新負荷權重時兩次的時間的差值 */
delta_exec = now - curr->exec_start;
if (unlikely((s64)delta_exec <= 0))
    return;

然後重新更新更新啟動時間exec_start為now, 以備下次計算時使用

最後將計算出的時間差, 加到了先前的統計時間上

/*  重新更新啟動時間exec_start為now  */
curr->exec_start = now;

schedstat_set(curr->statistics.exec_max,
              max(delta_exec, curr->statistics.exec_max));

/*  將時間差加到先前統計的時間即可  */
curr->sum_exec_runtime += delta_exec;
schedstat_add(cfs_rq, exec_clock, delta_exec);

3.2 模擬虛擬時鐘

有趣的事情是如何使用給出的信息來模擬不存在的虛擬時鐘. 這一次內核的實現仍然是非常巧妙地, 針對最普通的情形節省了一些時間. 對於運行在nice級別0的進程來說, 根據定義虛擬時鐘和物理時間相等. 在使用不同的優先順序時, 必鬚根據進程的負荷權重重新衡定時間

curr->vruntime += calc_delta_fair(delta_exec, curr);
update_min_vruntime(cfs_rq);

其中calc_delta_fair函數是計算的關鍵

//  http://lxr.free-electrons.com/source/kernel/sched/fair.c?v=4.6#L596
/*
 * delta /= w
 */
static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{
    if (unlikely(se->load.weight != NICE_0_LOAD))
        delta = __calc_delta(delta, NICE_0_LOAD, &se->load);

    return delta;
}

忽略舍入和溢出檢查, calc_delta_fair函數所做的就是根據下列公式計算:

每一個進程擁有一個vruntime, 每次需要調度的時候就選運行隊列中擁有最小vruntime的那個進程來運行, vruntime在時鐘中斷裡面被維護, 每次時鐘中斷都要更新當前進程的vruntime, 即vruntime以如下公式逐漸增長

那麼curr->vruntime += calc_delta_fair(delta_exec, curr); 即相當於如下操作

條件 公式
curr.nice != NICE_0_LOAD
curr.nice == NICE_0_LOAD curr−>vruntime+=delta

在該計算中可以派上用場了, 回想一下子, 可知越重要的進程會有越高的優先順序(即, 越低的nice值), 會得到更大的權重, 因此累加的虛擬運行時間會小一點,

根據公式可知, nice = 0的進程(優先順序120), 則虛擬時間和物理時間是相等的, 即current->se->load.weight等於NICE_0_LAD的情況.

3.3 重新設置cfs_rq->min_vruntime

接著內核需要重新設置min_vruntime. 必須小心保證該值是單調遞增的, 通過update_min_vruntime函數來設置

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

static void update_min_vruntime(struct cfs_rq *cfs_rq)
{
    /*  初始化vruntime的值, 相當於如下的代碼
    if (cfs_rq->curr != NULL)
        vruntime = cfs_rq->curr->vruntime;
    else
        vruntime = cfs_rq->min_vruntime;
    */
    u64 vruntime = cfs_rq->min_vruntime;

    if (cfs_rq->curr)
        vruntime = cfs_rq->curr->vruntime;


    /*  檢測紅黑樹是都有最左的節點, 即是否有進程在樹上等待調度
     *  cfs_rq->rb_leftmost(struct rb_node *)存儲了進程紅黑樹的最左節點
     *  這個節點存儲了即將要被調度的結點  
     *  */
    if (cfs_rq->rb_leftmost)
    {
        /*  獲取最左結點的調度實體信息se, se中存儲了其vruntime
         *  rb_leftmost的vruntime即樹中所有節點的vruntiem中最小的那個  */
        struct sched_entity *se = rb_entry(cfs_rq->rb_leftmost,
                           struct sched_entity,
                           run_node);
        /*  如果就緒隊列上沒有curr進程
         *  則vruntime設置為樹種最左結點的vruntime
         *  否則設置vruntiem值為cfs_rq->curr->vruntime和se->vruntime的最小值
         */
        if (!cfs_rq->curr)  /*  此時vruntime的原值為cfs_rq->min_vruntime*/
            vruntime = se->vruntime;
        else                /* 此時vruntime的原值為cfs_rq->curr->vruntime*/
            vruntime = min_vruntime(vruntime, se->vruntime);
    }

    /* ensure we never gain time by being placed backwards. 
     * 為了保證min_vruntime單調不減
     * 只有在vruntime超出的cfs_rq->min_vruntime的時候才更新
     */
    cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);
#ifndef CONFIG_64BIT
    smp_wmb();
    cfs_rq->min_vruntime_copy = cfs_rq->min_vruntime;
#endif
}

我們通過分析update_min_vruntime函數設置cfs_rq->min_vruntime的流程如下

  • 首先檢測cfs就緒隊列上是否有活動進程curr, 以此設置vruntime的值

如果cfs就緒隊列上沒有活動進程curr, 就設置vruntime為curr->vruntime;

否則又活動進程就設置為vruntime為cfs_rq的原min_vruntime;

  • 接著檢測cfs的紅黑樹上是否有最左節點, 即等待被調度的節點, 重新設置vruntime的值為curr進程和最左進程rb_leftmost的vruntime較小者的值

  • 為了保證min_vruntime單調不減, 只有在vruntime超出的cfs_rq->min_vruntime的時候才更新

update_min_vruntime依據當前進程和待調度的進程的vruntime值, 設置出一個可能的vruntime值, 但是只有在這個可能的vruntime值大於就緒隊列原來的min_vruntime的時候, 才更新就緒隊列的min_vruntime, 利用該策略, 內核確保min_vruntime只能增加, 不能減少.

update_min_vruntime函數的流程等價於如下的代碼

//  依據curr進程和待調度進程rb_leftmost找到一個可能的最小vruntime值
if (cfs_rq->curr != NULL cfs_rq->rb_leftmost == NULL)
    vruntime = cfs_rq->curr->vruntime;
else if(cfs_rq->curr == NULL && cfs_rq->rb_leftmost != NULL)
        vruntime = cfs_rq->rb_leftmost->se->vruntime;
else if (cfs_rq->curr != NULL cfs_rq->rb_leftmost != NULL)
    vruntime = min(cfs_rq->curr->vruntime, cfs_rq->rb_leftmost->se->vruntime);
else if(cfs_rq->curr == NULL cfs_rq->rb_leftmost == NULL)
    vruntime = cfs_rq->min_vruntime;

//  每個隊列的min_vruntime只有被樹上某個節點的vruntime((curr和程rb_leftmost兩者vruntime的較小值)超出時才更新
cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);

其中尋找可能vruntime的策略我們採用表格的形式可能更加直接

活動進程curr 待調度進程rb_leftmost 可能的vruntime值 cfs_rq
NULL NULL cfs_rq->min_vruntime 維持原值
NULL 非NULL rb_leftmost->se->vruntime max(可能值vruntime, 原值)
非NULL NULL curr->vruntime max(可能值vruntime, 原值)
非NULL 非NULL min(curr->vruntime, rb_leftmost->se->vruntime) max(可能值vruntime, 原值)

4 紅黑樹的鍵值entity_key和entity_before

完全公平調度調度器CFS的真正關鍵點是, 紅黑樹的排序過程是進程的vruntime來進行計算的, 準確的來說同一個就緒隊列所有進程(或者調度實體)依照其鍵值se->vruntime - cfs_rq->min_vruntime進行排序.

鍵值通過entity_key計算, 該函數在linux-2.6之中被定義, 但是後來的內核中移除了這個函數, 但是我們今天仍然講解它, 因為它對我們理解CFS調度器和虛擬時鐘vruntime有很多幫助, 我們也會講到為什麼這麼有用的一個函數會被移除

我們可以在早期的linux-2.6.30(僅有entity_key函數)和linux-2.6.32(定義了entity_keyentity_befire函數)來查看

static inline s64 entity_key(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
    return se->vruntime - cfs_rq->min_vruntime;
}

鍵值較小的結點, 在CFS紅黑樹中排序的位置就越靠左, 因此也更快地被調度. 用這種方法, 內核實現了下麵兩種對立的機制

  • 在程式運行時, 其vruntime穩定地增加, 他在紅黑樹中總是向右移動的.

因為越重要的進程vruntime增加的越慢, 因此他們向右移動的速度也越慢, 這樣其被調度的機會要大於次要進程, 這剛好是我們需要的

  • 如果進程進入睡眠, 則其vruntime保持不變. 因為每個隊列min_vruntime同時會單調增加, 那麼當進程從睡眠中蘇醒, 在紅黑樹中的位置會更靠左, 因為其鍵值相對來說變得更小了.

好了我們瞭解了entity_key計算了紅黑樹的鍵值, 他作為CFS對紅黑樹中結點的排序依據. 但是在新的內核中entity_key函數卻早已消失不見, 這是為什麼呢?

sched: Replace use of entity_key

我們在linux-2.6.32的kernel/sched_fair.c中搜索entity_key函數關鍵字, 會發現內核僅在__enqueue_entity(定義在linux-2.6.32的kernel/sched_fair.c, line 309)函數中使用了entity_key函數用來比較兩個調度實體的虛擬時鐘鍵值的大小

即相當於如下代碼

if (entity_key(cfs_rq, se) < entity_key(cfs_rq, entry))

等價於
if (se->vruntime-cfs_rq->min_vruntime < entry->vruntime-cfs_rq->min_vruntime)

進一步化簡為

if (se->vruntime < entry->vruntime)

即整個過程等價於比較兩個調度實體vruntime值得大小

因此內核定義了函數entity_before來實現此功能, 函數定義在linux+v2.6.32/kernel/sched_fair.c, line 269, 在我們新的linux-4.6內核中定義在kernel/sched/fair.c, line 452

static inline int entity_before(struct sched_entity *a,
                                struct sched_entity *b)
{
    return (s64)(a->vruntime - b->vruntime) < 0;
}

5 延遲跟蹤(調度延遲)與虛擬時間在調度實體內部的再分配

5.1 調度延遲與其控制欄位

內核有一個固定的概念, 稱之為良好的調度延遲, 即保證每個可運行的進程都應該至少運行一次的某個時間間隔. 它在sysctl_sched_latency給出, 可通過/proc/sys/kernel/sched_latency_ns控制, 預設值為20000000納秒, 即20毫秒.

__sched_period確定延遲周期的長度, 通常就是sysctl_sched_latency, 但如果有更多的進程在運行, 其值有可能按比例線性擴展. 在這種情況下, 周期長度是

__sched_period = sysctl_sched_latency * nr_running / sched_nr_latency

5.2 虛擬時間在調度實體內的分配

調度實體是內核進行調度的基本實體單位, 其可能包含一個或者多個進程, 那麼調度實體分配到的虛擬運行時間, 需要在內部對各個進程進行再次分配.

通過考慮各個進程的相對權重, 將一個延遲周期的時間在活動進程之前進行分配. 對於由某個調度實體標識的給定進程, 分配到的時間通過sched_slice函數來分配, 其實現在kernel/sched/fair.c, line 626, 計算方式如下

/*
 * We calculate the wall-time slice from the period by taking a part
 * proportional to the weight.
 *
 * s = p*P[w/rw]
 */
static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
        u64 slice = __sched_period(cfs_rq->nr_running + !se->on_rq);

        for_each_sched_entity(se) {
                struct load_weight *load;
                struct load_weight lw;

                cfs_rq = cfs_rq_of(se);
                load = &cfs_rq->load;

                if (unlikely(!se->on_rq)) {
                        lw = cfs_rq->load;

                        update_load_add(&lw, se->load.weight);
                        load = &lw;
                }
                slice = __calc_delta(slice, se->load.weight, load);
        }
        return slice;
}

回想一下子, 就緒隊列的負荷權重是隊列是那個所有活動進程負荷權重的總和, 結果時間段是按實際時間給出的, 但內核有時候也需要知道等價的虛擬時間, 該功能通過sched_vslice函數來實現, 其定義在kernel/sched/fair.c, line 626

/*
 * We calculate the vruntime slice of a to-be-inserted task.
 *
 * vs = s/w
 */
static u64 sched_vslice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
        return calc_delta_fair(sched_slice(cfs_rq, se), se);
}

相對於權重weight的進程來說, 其實際時間段time相對應的虛擬時間長度為

time * NICE_0_LOAD / weight

該公式通過calc_delta_fair函數計算, 在sched_vslice函數中也被用來轉換分配到的延遲時間間隔.

6 總結

CFS調度演算法的思想

理想狀態下每個進程都能獲得相同的時間片,並且同時運行在CPU上,但實際上一個CPU同一時刻運行的進程只能有一個。也就是說,當一個進程占用CPU時,其他進程就必須等待。CFS為了實現公平,必須懲罰當前正在運行的進程,以使那些正在等待的進程下次被調度.

虛擬時鐘是紅黑樹排序的依據

具體實現時,CFS通過每個進程的虛擬運行時間(vruntime)來衡量哪個進程最值得被調度. CFS中的就緒隊列是一棵以vruntime為鍵值的紅黑樹,虛擬時間越小的進程越靠近整個紅黑樹的最左端。因此,調度器每次選擇位於紅黑樹最左端的那個進程,該進程的vruntime最小.

優先順序計算負荷權重, 負荷權重和當前時間計算出虛擬運行時間

虛擬運行時間是通過進程的實際運行時間和進程的權重(weight)計算出來的。在CFS調度器中,將進程優先順序這個概念弱化,而是強調進程的權重。一個進程的權重越大,則說明這個進程更需要運行,因此它的虛擬運行時間就越小,這樣被調度的機會就越大。而,CFS調度器中的權重在內核是對用戶態進程的優先順序nice值, 通過prio_to_weight數組進行nice值和權重的轉換而計算出來的

虛擬時鐘相關公式

linux內核採用了計算公式:

屬性 公式 描述
ideal_time sum_runtime *se.weight/cfs_rq.weight 每個進程應該運行的時間
sum_exec_runtime 運行隊列中所有任務運行完一遍的時間
se.weight 當前進程的權重
cfs.weight 整個cfs_rq的總權重

這裡se.weight和cfs.weight根據上面講解我們可以算出, sum_runtime是怎們計算的呢,linux內核中這是個經驗值,其經驗公式是

條件 公式
進程數 > sched_nr_latency sum_runtime=sysctl_sched_min_granularity *nr_running
進程數 <=sched_nr_latency sum_runtime=sysctl_sched_latency = 20ms

註:sysctl_sched_min_granularity =4ms

sched_nr_latency是內核在一個延遲周期中處理的最大活動進程數目

linux內核代碼中是通過一個叫vruntime的變數來實現上面的原理的,即:

每一個進程擁有一個vruntime,每次需要調度的時候就選運行隊列中擁有最小vruntime的那個進程來運行,vruntime在時鐘中斷裡面被維護,每次時鐘中斷都要更新當前進程的vruntime,即vruntime以如下公式逐漸增長:

條件 公式
curr.nice!=NICE_0_LOAD vruntime += delta * NICE_0_LOAD/se.weight;
curr.nice=NICE_0_LOAD vruntime += delta;

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

-Advertisement-
Play Games
更多相關文章
  • 硬鏈接:在linux文件系統中多個文件名指向同一個索引節點(Inode)是正常允許的。硬鏈接文件就相當於文件的另一個入口。 硬鏈接的作用:允許一個文件擁有多個有效路徑(多個入口),這樣用戶就可以建立硬鏈接到重要文件,以防止“誤刪”源數據 (很多硬體存儲,如netapp存儲中的快照功能就應用了這個原理 ...
  • DEVICE=eth0 #物理設備名,網卡名稱 WADDR=00:0c:29:7a:6b:24 #網卡的MAC地址,48位(配置克隆機時需把這一段刪除)TYPE=Ethernet #以外網UUID=c35b4084-7211-4da2-a64f-d078470ebac2 #網卡的唯一標識(配置克隆機 ...
  • Exchange主要是針對內部網或者企業網用戶進行搭建的郵件伺服器軟體,利用它能夠很快地搭建安全性較高的內部網郵件系統。 本次搭建在個人環境中實踐,純屬愛好折騰,分四步驟,1.搭建windows 2008 server域環境 2.安裝Exchange 2010郵件伺服器 3.配置Exchange 2 ...
  • [toc] Linux Namespace Linux Namespace 是kernel的一個功能,用於隔離系統資源。比如說PID,User ID 等。(可以想象一下變成語言變數的命名空間)這樣我們就可以將 進程,網路介面,掛載點以及用戶做一個隔離。 比如說我們在同一臺主機上,需要將資源分給多個類 ...
  • 個人總結,請尊重版權,轉載請在顯眼地方註明出處:https://www.cnblogs.com/sunshine5683/p/9948259.html day(01)基本命令學習 今天開始自學linux,在學習的過程中將踩過的坑和學習經歷總結出來,在提升自己的過程中,幫助有需要的人,一起學習,希望在 ...
  • Ubuntu的圖形界面使用起來非常方便,但是要想對Linux熟練掌握,就必須學會它的操作命令。雖然可能會花費一些時間,不過從長遠的角度來說,這的確是一件事半功倍的事情,它會讓我們更瞭解Linux,更靈活地去使用Linux。Ubuntu提供了很多命令,不過在本章中不做全部介紹,只是先介紹一些常用命令, ...
  • 這個寫的比較簡單,先做以下記錄 centos虛擬機安裝到別的電腦上,因為linux中的程式需要向外有網路互通,所以需要重新設置ip 通過 ifconfig eth4 192.168.0.20 broadcast 192.168.0.1 :broadcast 參數可以不填,這樣就設置好了ip 但是一個 ...
  • 1. CFS進程入隊和出隊 完全公平調度器CFS中有兩個函數可用來增刪隊列的成員: 和`dequeue_task_fair`分別用來向CFS就緒隊列中添加或者刪除進程 2 enqueue_task_fair入隊操作 2.1 enque_task_fair函數 向就緒隊列中放置新進程的工作由函數 函數 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...