精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從CPU資源大戰,看懂內核搶占機制

系統 其他OS
內核搶占,簡單來說,就是在系統內核運行的過程中,允許高優先級的任務中斷當前正在執行的低優先級任務,從而獲取 CPU 資源并得以執行 。

當你同時打開瀏覽器刷視頻、IDE 編譯代碼、后臺還在同步云文檔時,有沒有突然遇到過鼠標卡頓半秒的情況?這不是硬件故障,而是一場無聲的 CPU 資源爭奪戰 —— 低優先級任務正霸占著處理器,高優先級任務卻在 “排隊等待”。在 Linux 系統中,每秒要處理成百上千個進程 / 線程,CPU 就像唯一的檢票口,如何避免 “慢任務堵路”、讓緊急任務快速通過?

為了解決 CPU 資源分配這一關鍵問題,操作系統引入了內核搶占機制。內核搶占機制就像是一位高效的交通警察,能夠根據各個程序的 “緊急程度” 和 “重要性”,動態地分配 CPU 資源,確保系統能夠高效、穩定地運行。接下來,就讓我們深入探索內核搶占機制,看看它是如何在這場 CPU 資源的爭奪戰中發揮關鍵作用的。

一、內核搶占機制是什么?

1.1內核搶占概述

內核搶占,簡單來說,就是在系統內核運行的過程中,允許高優先級的任務中斷當前正在執行的低優先級任務,從而獲取 CPU 資源并得以執行 。這就好比一場正在進行的接力比賽,原本低優先級任務是正在奔跑的運動員,但當高優先級任務這個 “更厲害的運動員” 出現時,就可以強行接過接力棒,繼續向前沖刺。

在操作系統中,任務(也可以稱為進程或線程)有著不同的優先級劃分。比如,系統中負責處理硬件中斷的任務,像硬盤數據讀取完成后的中斷處理任務,往往需要極高的優先級,因為它要及時處理硬件傳來的數據,確保數據的完整性和系統的穩定運行。而一些后臺運行的任務,比如自動備份文件的任務,雖然也重要,但在即時性上要求就沒有那么高,優先級相對較低。當一個低優先級的任務正在內核態運行時,如果此時有一個高優先級的任務被觸發,內核搶占機制就會發揮作用,暫停低優先級任務,將 CPU 資源分配給高優先級任務。這樣做的目的是為了確保系統能夠對緊急事件做出快速響應,提高整個系統的性能和穩定性 。

1.2內核搶占與用戶搶占的區別

內核搶占和用戶搶占雖然都是為了實現 CPU 資源的合理分配,但它們在觸發時機、條件和實現方式上有著明顯的區別。從觸發時機來看,用戶搶占通常發生在從系統調用返回用戶空間,或者從中斷(異常)處理程序返回用戶空間的時候。當內核即將把執行權交回給用戶空間時,會檢查一個名為need_resched的標志位。如果這個標志位被設置了,就意味著有其他更需要 CPU 資源的任務在等待,于是內核會調用調度程序schedule(),進行任務的重新調度,從而發生用戶搶占 。打個比方,就好像一個學生在課間休息(用戶空間)時,老師(內核)檢查到有更緊急的任務(need_resched標志位被設置)需要安排給某個同學,于是就會重新調整同學們的任務安排(用戶搶占)。

而內核搶占的觸發時機則更加多樣化。當中斷處理程序返回內核空間時,如果此時滿足搶占條件,就可能發生內核搶占;當內核代碼執行完畢,再次變得可搶占時,也可能觸發內核搶占;另外,如果內核中的任務顯式地調用了schedule()函數,或者任務因為某些原因阻塞了,同樣會引發內核搶占 。這就如同在一場課堂討論(內核空間運行)中,老師(內核)隨時可能因為有更重要的事情(高優先級任務出現等情況),而打斷當前同學(低優先級任務)的發言,讓更合適的同學(高優先級任務)發言。

在觸發條件方面,用戶搶占主要依賴于need_resched標志位,只要這個標志位被設置,并且在合適的返回用戶空間的時機,就可能發生用戶搶占。而內核搶占則依賴于一個名為preempt_count的計數器 。preempt_count用于表示當前內核上下文是否允許被搶占。每次調用preempt_disable()函數(或者持有鎖等操作)會使preempt_count增加;調用preempt_enable()函數時則會使其減少。當preempt_count等于 0 時,表示沒有阻止搶占的條件,內核允許被搶占;否則,搶占就會被延遲 。這就好像一個倉庫,preempt_count就像是倉庫的 “占用計數器”,當計數器為 0 時,說明倉庫可以被新的物品(高優先級任務)占用,也就是可以發生內核搶占;當計數器不為 0 時,說明倉庫被占用(有阻止搶占的條件),就不能發生內核搶占。

從實現方式上看,用戶搶占相對簡單,主要通過檢查need_resched標志位并在合適時機調用schedule()函數來實現。而內核搶占的實現則較為復雜,它需要在內核的關鍵位置,如中斷返回處、preempt_enable()函數調用處等,檢查preempt_count和need_resched等條件,以決定是否進行搶占 。這就好比管理一個圖書館,用戶搶占就像是在特定的時間點(如閉館前)檢查是否有讀者需要特殊安排(need_resched標志位),然后進行相應調整;而內核搶占則像是在圖書館的各個關鍵區域(如入口、借閱處等)都設置了檢查點,隨時檢查是否可以讓更重要的讀者(高優先級任務)優先使用資源(發生內核搶占)。

內核搶占在系統資源分配中有著獨特的作用。它能夠更及時地響應高優先級任務,尤其是在處理一些對時間要求苛刻的任務時,如實時數據處理、緊急的硬件中斷處理等,內核搶占可以確保這些任務能夠迅速得到 CPU 資源,從而保證系統的實時性和穩定性 。而用戶搶占更多地是從用戶空間任務的整體調度角度出發,優化用戶空間任務的執行效率。

1.3為什么需要內核搶占

當一個以優先級為主的調度器中,當一個新的進程(下圖中的task2)進入到可執行(running)的狀態,核心的調度器會檢查它的優先級,若該進程的優先權比目前正在執行的進程(下圖中的task1)還高,核心調度器便會觸發搶占(preempt),使得正在執行的進程被打斷,而擁有更高優先級的進程會開始執行。

圖片圖片

在不支持內核搶占模型中,搶占點比較少,對于內核搶占,如右圖會在系統中添加很多搶占點,同時會導致執行時間會比左圖多一點,可搶占會導致每隔一定時間去檢查是否需要搶占,這樣也會影響cache,pipeline,這樣就會犧牲吞吐量。從上面圖可以看出,操作系統演進過程中,不是新的就一定比舊的好,需要考量場景選擇合適的方案。從這張圖我們可以看出,內核搶占主要解決以下問題:

  • 提高系統響應實時性和用戶體驗:在不支持內核搶占式內核中,低優先級任務可能會長時間占用CPU,導致高優先級任務無法及時得到處理,主要解決的是latency問題。這種情況會顯著影響系統的響應速度,特別是在實時應用中,可能導致嚴重的性能問題。對于手機場景中,當用戶在使用應用程序時,內核搶占可以確保用戶界面關鍵線程得到足夠的CPU時間,避免界面卡頓和延遲。
  • 避免優先級翻轉:內核搶占結合優先級繼承(Priority Inheritance)等機制,可以有效緩解優先級翻轉問題。當低優先級任務持有高優先級任務需要的資源時,內核搶占機制可以提高低優先級任務的優先級,使其盡快釋放資源,從而減少高優先級任務的等待時間。在Linux中,從2.6開始,rtmutex支持優先級繼承,解決優先級翻轉的問題。

所以需要內核搶占的根本原因就是系統在吞吐量和及時響應之間進行權衡的結果,對于Linux作為一個通用的操作系統,其最初設計就是為了throughput而非確定性時延而設計。但是越來越多的場景對及時響應的要求越來越高,讓更高優先級的任務和關鍵的任務及時得到調度,特別對于我們手機這種交互式的場景中。

二、內核搶占機制的工作原理

2.1搶占標志位 TIF_NEED_RESCHED

TIF_NEED_RESCHED 是內核中一個非常關鍵的標志位,它位于線程的thread_info結構體的flags字段中 。這個標志位就像是一個信號燈,用于向內核表明 “有更緊急的任務需要調度,該進行任務切換啦” 。當系統中發生一些特定事件時,TIF_NEED_RESCHED 標志位就會被設置。比如,當一個正在運行的任務時間片耗盡時,就像一個運動員的比賽時間到了,內核會設置這個標志位,提醒系統需要重新分配 CPU 資源,讓其他任務有機會執行 。又比如,當一個高優先級的任務被喚醒時,好比一個更重要的客人突然到訪,內核也會設置該標志位,以便盡快讓高優先級任務獲取 CPU 資源,優先執行 。

設置 TIF_NEED_RESCHED 標志位后,并不會立即觸發任務調度,而是在內核執行到一些特定的 “安全點” 時,才會檢查這個標志位 。這些安全點就像是道路上的檢查關卡,內核會在這些地方停下來檢查是否有緊急任務需要處理。例如,當中斷處理程序返回內核空間時,或者內核代碼執行完畢再次變得可搶占時,內核就會檢查 TIF_NEED_RESCHED 標志位 。如果標志位被設置了,并且此時內核上下文允許被搶占(這就涉及到后面要講的搶占計數preempt_count ),那么內核就會調用調度程序schedule(),進行任務的重新調度 。這就好比在關卡處,警察檢查到有緊急任務(TIF_NEED_RESCHED 標志位被設置),并且當前道路條件允許(內核上下文允許被搶占),就會重新安排車輛(任務)的通行順序 。

2.2搶占計數 preempt_count

preempt_count 是一個用于記錄內核上下文是否允許被搶占的計數器 ,它的實現與體系結構相關 。在 x86 架構中,它被實現為一個percpu變量;在 ARM 架構中,它是當前進程描述符thread_info中的一個變量 。preempt_count 就像是一個 “門鎖計數器”,用來控制內核是否可以被搶占 。

每次調用preempt_disable()函數時,就相當于給門鎖上加了一把鎖,preempt_count 會增加 。這通常發生在內核執行一些不希望被打斷的關鍵代碼時,比如訪問共享數據結構時,為了防止其他任務在這個過程中搶占 CPU,導致數據不一致,就會調用preempt_disable()函數 。相反,每次調用preempt_enable()函數時,就像是打開了一把鎖,preempt_count 會減少 。當 preempt_count 的值等于 0 時,意味著所有的鎖都被打開了,即沒有阻止搶占的條件,內核允許被搶占 。

preempt_count 與 TIF_NEED_RESCHED 標志位緊密配合,共同實現內核搶占 。當 TIF_NEED_RESCHED 標志位被設置,表示有更緊急的任務需要調度 。但只有當 preempt_count 等于 0,即內核上下文允許被搶占時,才會真正觸發內核搶占 。例如,當一個中斷處理程序返回內核空間時,如果 TIF_NEED_RESCHED 標志位被設置,并且 preempt_count 為 0,那么內核就會立即進行任務調度,將 CPU 資源分配給更緊急的任務 。這就好比一個緊急通知(TIF_NEED_RESCHED 標志位)來了,但只有當房間的門(preempt_count 為 0,允許進入)打開時,緊急任務才能進入房間(獲取 CPU 資源) 。

2.3內核搶占的時機

內核搶占會在多種情況下發生,下面我們詳細分析幾種常見的情況,并配合示意圖進行說明 。

(1)中斷返回內核空間時:當中斷發生時,內核會暫停當前任務的執行,去處理中斷 。當中斷處理程序執行完畢返回內核空間時,如果此時 TIF_NEED_RESCHED 標志位被設置,并且 preempt_count 為 0,就會發生內核搶占 。假設現在有一個低優先級任務 A 正在內核態運行,突然發生了一個硬件中斷,比如硬盤數據讀取完成的中斷 。內核會立即保存任務 A 的現場(包括寄存器的值等),然后跳轉到中斷處理程序執行 。在中斷處理程序中,可能會喚醒一個高優先級任務 B 。當中斷處理程序執行完畢返回內核空間時,內核檢查到 TIF_NEED_RESCHED 標志位被設置(因為高優先級任務 B 被喚醒),并且 preempt_count 為 0(沒有阻止搶占的條件),那么內核就會搶占任務 A 的執行,將 CPU 資源分配給高優先級任務 B 。

內核中斷處理和搶占機制的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>

// 模擬任務結構
typedef struct {
    const char* name;
    int priority;       // 優先級:數值越大優先級越高
    volatile bool running;  // 任務是否運行中
    pthread_t tid;      // 線程ID
} Task;

// 模擬內核狀態
volatile int preempt_count = 0;          // 搶占計數
volatile bool tif_need_resched = false;  // 重調度標志
volatile Task* current_task = NULL;      // 當前運行任務

// 任務列表
Task taskA = {"TaskA", 1, false, 0};  // 低優先級任務
Task taskB = {"TaskB", 3, false, 0};  // 高優先級任務

// 模擬內核調度器:選擇最高優先級的就緒任務
Task* scheduler() {
    if (taskB.running) return &taskB;
    if (taskA.running) return &taskA;
    return NULL;
}

// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
    if (prev == next) return;

    printf("\n[調度器] 發生上下文切換: %s -> %s\n", 
           prev ? prev->name : "None", next->name);
    current_task = next;
}

// 模擬中斷處理程序
void interrupt_handler(int signum) {
    printf("\n[中斷處理] 收到硬盤數據讀取完成中斷!\n");
    preempt_count++;  // 進入中斷,禁止搶占

    // 在中斷處理中喚醒高優先級任務B
    if (!taskB.running) {
        printf("[中斷處理] 喚醒高優先級任務B\n");
        taskB.running = true;
        tif_need_resched = true;  // 設置重調度標志
    }

    preempt_count--;  // 離開中斷,恢復搶占計數
}

// 低優先級任務A的執行函數
void* taskA_func(void* arg) {
    taskA.running = true;
    current_task = &taskA;
    printf("[%s] 開始運行 (內核態)\n", taskA.name);

    // 模擬長時間運行的內核操作
    for (int i = 0; i < 5; i++) {
        printf("[%s] 正在執行內核操作... (%d/5)\n", taskA.name, i+1);
        sleep(1);  // 模擬耗時操作
    }

    taskA.running = false;
    printf("[%s] 完成內核操作\n", taskA.name);
    return NULL;
}

// 高優先級任務B的執行函數
void* taskB_func(void* arg) {
    // 等待被喚醒
    while (!taskB.running) {
        usleep(100);
    }

    current_task = &taskB;
    printf("[%s] 開始運行 (內核態)\n", taskB.name);

    // 模擬任務B的內核操作
    for (int i = 0; i < 3; i++) {
        printf("[%s] 正在執行內核操作... (%d/3)\n", taskB.name, i+1);
        sleep(1);
    }

    taskB.running = false;
    printf("[%s] 完成內核操作\n", taskB.name);
    return NULL;
}

// 模擬內核搶占檢查(在中斷返回時調用)
void check_preemption() {
    if (tif_need_resched && preempt_count == 0) {
        printf("\n[搶占檢查] 滿足搶占條件,觸發調度!\n");
        Task* next_task = scheduler();
        if (next_task && next_task != current_task) {
            context_switch(current_task, next_task);
        }
        tif_need_resched = false;  // 清除重調度標志
    }
}

// 模擬中斷返回過程
void interrupt_return() {
    printf("[中斷返回] 從中斷處理程序返回內核空間\n");
    check_preemption();  // 返回時檢查是否需要搶占
}

int main() {
    // 注冊信號處理函數模擬中斷
    signal(SIGUSR1, interrupt_handler);

    // 創建任務線程
    pthread_create(&taskA.tid, NULL, taskA_func, NULL);
    pthread_create(&taskB.tid, NULL, taskB_func, NULL);

    // 等待任務A運行一段時間后發送中斷
    sleep(2);
    printf("\n[主程序] 觸發硬件中斷\n");
    pthread_kill(taskA.tid, SIGUSR1);  // 向任務A發送信號模擬中斷

    // 模擬中斷返回過程(實際內核中由硬件自動完成)
    interrupt_return();

    // 等待所有任務完成
    pthread_join(taskA.tid, NULL);
    pthread_join(taskB.tid, NULL);

    return 0;
}

程序運行后會出現以下過程:

  1. 低優先級任務 A 開始運行
  2. 2 秒后觸發硬件中斷
  3. 中斷處理程序喚醒高優先級任務 B
  4. 中斷返回時檢查到搶占條件,觸發任務切換
  5. 高優先級任務 B 搶占 CPU 并執行
  6. 任務 B 完成后,任務 A 繼續執行剩余操作

這個示例模擬了內核中 "中斷觸發→喚醒高優先級任務→中斷返回時搶占" 的完整過程,幫助理解內核搶占機制的基本原理。實際內核中的實現會更復雜,涉及真實的上下文切換、寄存器保存等操作。

(2)顯式調用 preempt_enable () 函數時:當內核代碼執行完一些關鍵的不可搶占部分,調用preempt_enable()函數使 preempt_count 減 1 。如果此時 preempt_count 變為 0,并且 TIF_NEED_RESCHED 標志位被設置,就會觸發內核搶占 。例如,內核在訪問共享數據結構時,先調用preempt_disable()函數禁止搶占,以保證數據訪問的一致性 。當數據訪問完成后,調用preempt_enable()函數 。如果在這期間有高優先級任務被喚醒,導致 TIF_NEED_RESCHED 標志位被設置,那么當preempt_enable()函數執行后,preempt_count 變為 0,就會發生內核搶占 。內核中preempt_enable()觸發搶占機制的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>

// 任務結構定義
typedef struct {
    const char* name;
    int priority;               // 優先級(數值越大優先級越高)
    volatile bool running;      // 任務運行狀態
    pthread_t tid;              // 線程ID
} Task;

// 內核狀態模擬
volatile int preempt_count = 0;          // 搶占計數器
volatile bool tif_need_resched = false;  // 重調度標志位
volatile Task* current_task = NULL;      // 當前運行任務
volatile bool shared_data_busy = false;  // 共享數據鎖定標志

// 定義兩個任務:低優先級A和高優先級B
Task taskA = {"TaskA", 1, false, 0};
Task taskB = {"TaskB", 3, false, 0};

// 模擬內核調度器:選擇最高優先級的就緒任務
Task* scheduler() {
    if (taskB.running) return &taskB;
    if (taskA.running) return &taskA;
    return NULL;
}

// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
    if (prev == next) return;

    printf("\n[調度器] 發生上下文切換: %s -> %s\n",
           prev ? prev->name : "None", next->name);
    current_task = next;
}

// 模擬搶占啟用函數
void preempt_enable() {
    preempt_count--;
    printf("[preempt_enable] 搶占計數變為: %d\n", preempt_count);

    // 檢查是否滿足搶占條件
    if (preempt_count == 0 && tif_need_resched) {
        printf("[preempt_enable] 滿足搶占條件,觸發調度!\n");
        Task* next_task = scheduler();
        if (next_task && next_task != current_task) {
            context_switch(current_task, next_task);
        }
        tif_need_resched = false;  // 清除重調度標志
    }
}

// 模擬搶占禁用函數
void preempt_disable() {
    preempt_count++;
    printf("[preempt_disable] 搶占計數變為: %d\n", preempt_count);
}

// 模擬訪問共享數據結構的函數
void access_shared_data() {
    printf("[%s] 準備訪問共享數據,禁止搶占\n", current_task->name);
    preempt_disable();          // 進入不可搶占區域
    shared_data_busy = true;

    // 模擬共享數據操作
    printf("[%s] 正在操作共享數據...\n", current_task->name);
    sleep(2);  // 模擬耗時操作

    shared_data_busy = false;
    printf("[%s] 共享數據操作完成,允許搶占\n", current_task->name);
    preempt_enable();           // 離開不可搶占區域,可能觸發搶占
}

// 高優先級任務B的執行函數
void* taskB_func(void* arg) {
    while (1) {
        if (!taskB.running) {
            usleep(100);  // 等待被喚醒
            continue;
        }

        current_task = &taskB;
        printf("\n[%s] 開始執行 (高優先級任務)\n", taskB.name);

        // 執行任務B的工作
        for (int i = 0; i < 3; i++) {
            printf("[%s] 執行核心工作... (%d/3)\n", taskB.name, i+1);
            sleep(1);
        }

        taskB.running = false;
        printf("[%s] 任務執行完畢\n", taskB.name);
        break;
    }
    return NULL;
}

// 低優先級任務A的執行函數
void* taskA_func(void* arg) {
    taskA.running = true;
    current_task = &taskA;
    printf("[%s] 開始執行 (低優先級任務)\n", taskA.name);

    // 執行一些前期工作
    printf("[%s] 執行前期準備工作...\n", taskA.name);
    sleep(1);

    // 訪問共享數據(不可搶占區域)
    access_shared_data();

    // 繼續執行剩余工作
    printf("[%s] 繼續執行剩余工作...\n", taskA.name);
    sleep(2);

    taskA.running = false;
    printf("[%s] 任務執行完畢\n", taskA.name);
    return NULL;
}

// 模擬喚醒高優先級任務的函數(可在中斷或其他任務中調用)
void wake_high_priority_task() {
    if (!taskB.running) {
        printf("\n[系統] 喚醒高優先級任務B\n");
        taskB.running = true;
        tif_need_resched = true;  // 設置重調度標志
        printf("[系統] 設置TIF_NEED_RESCHED標志\n");
    }
}

int main() {
    // 創建任務線程
    pthread_create(&taskA.tid, NULL, taskA_func, NULL);
    pthread_create(&taskB.tid, NULL, taskB_func, NULL);

    // 等待任務A進入不可搶占區域后喚醒任務B
    sleep(2);  // 確保taskA已進入共享數據操作
    wake_high_priority_task();

    // 等待所有任務完成
    pthread_join(taskA.tid, NULL);
    pthread_join(taskB.tid, NULL);

    return 0;
}

(3)內核代碼執行完畢,再次變得可搶占時:有些內核代碼在執行過程中會暫時禁止搶占,當這些代碼執行完畢,再次變得可搶占時,如果滿足 TIF_NEED_RESCHED 標志位被設置且 preempt_count 為 0 的條件,也會發生內核搶占 。比如,在一些內核模塊的初始化函數中,可能會禁止搶占,以確保初始化過程的順利進行 。當初始化完成后,內核恢復可搶占狀態,如果此時有高優先級任務等待調度,就會觸發內核搶占 。

內核代碼在禁止搶占后恢復可搶占狀態時觸發內核搶占的示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>

// 任務結構定義
typedef struct {
    const char* name;
    int priority;               // 優先級:數值越大優先級越高
    volatile bool running;      // 任務運行狀態
    pthread_t tid;              // 線程ID
} Task;

// 內核狀態模擬
volatile int preempt_count = 0;          // 搶占計數器
volatile bool tif_need_resched = false;  // 重調度標志位
volatile Task* current_task = NULL;      // 當前運行任務

// 定義任務:低優先級初始化任務和高優先級等待任務
Task init_task = {"InitTask", 1, false, 0};  // 模擬內核模塊初始化任務
Task high_prio_task = {"HighPrioTask", 5, false, 0};  // 高優先級任務

// 調度器:選擇最高優先級的就緒任務
Task* scheduler() {
    if (high_prio_task.running) return &high_prio_task;
    if (init_task.running) return &init_task;
    return NULL;
}

// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
    if (prev == next) return;
    printf("\n[調度器] 上下文切換: %s -> %s\n", 
           prev ? prev->name : "None", next->name);
    current_task = next;
}

// 禁止搶占(preempt_disable模擬)
void preempt_disable() {
    preempt_count++;
    printf("[內核] 禁止搶占,preempt_count = %d\n", preempt_count);
}

// 允許搶占(preempt_enable模擬)
void preempt_enable() {
    preempt_count--;
    printf("[內核] 允許搶占,preempt_count = %d\n", preempt_count);

    // 檢查搶占條件:計數為0且需要重調度
    if (preempt_count == 0 && tif_need_resched) {
        printf("[內核] 滿足搶占條件,觸發調度\n");
        Task* next = scheduler();
        if (next && next != current_task) {
            context_switch(current_task, next);
        }
        tif_need_resched = false;  // 清除重調度標志
    }
}

// 模擬內核模塊初始化函數(需要禁止搶占的關鍵代碼)
void kernel_module_init() {
    printf("\n[InitTask] 開始內核模塊初始化(禁止搶占以保證原子性)\n");
    preempt_disable();  // 進入不可搶占區域

    // 模擬初始化過程(耗時操作)
    for (int i = 0; i < 3; i++) {
        printf("[InitTask] 執行初始化步驟 %d/3\n", i+1);
        sleep(1);
    }

    printf("[InitTask] 初始化完成,恢復搶占狀態\n");
    preempt_enable();   // 離開不可搶占區域,可能觸發搶占
}

// 高優先級任務執行函數
void* high_prio_task_func(void* arg) {
    while (1) {
        if (!high_prio_task.running) {
            usleep(100);  // 等待被喚醒
            continue;
        }

        current_task = &high_prio_task;
        printf("\n[%s] 獲得CPU,開始執行高優先級任務\n", high_prio_task.name);

        // 執行高優先級任務工作
        for (int i = 0; i < 2; i++) {
            printf("[%s] 處理緊急任務 %d/2\n", high_prio_task.name, i+1);
            sleep(1);
        }

        high_prio_task.running = false;
        printf("[%s] 高優先級任務執行完畢\n", high_prio_task.name);
        break;
    }
    return NULL;
}

// 初始化任務執行函數
void* init_task_func(void* arg) {
    init_task.running = true;
    current_task = &init_task;
    printf("[%s] 啟動內核模塊初始化任務\n", init_task.name);

    // 執行初始化(包含不可搶占區域)
    kernel_module_init();

    // 初始化后的后續工作
    printf("\n[%s] 繼續執行初始化后的收尾工作\n", init_task.name);
    sleep(1);

    init_task.running = false;
    printf("[%s] 所有工作完成\n", init_task.name);
    return NULL;
}

// 喚醒高優先級任務的函數
void wake_high_prio_task() {
    if (!high_prio_task.running) {
        printf("\n[系統] 高優先級任務等待調度,設置TIF_NEED_RESCHED\n");
        high_prio_task.running = true;
        tif_need_resched = true;
    }
}

int main() {
    // 創建任務線程
    pthread_create(&init_task.tid, NULL, init_task_func, NULL);
    pthread_create(&high_prio_task.tid, NULL, high_prio_task_func, NULL);

    // 等待初始化任務進入不可搶占區域后喚醒高優先級任務
    sleep(2);  // 確保init_task已進入禁止搶占狀態
    wake_high_prio_task();

    // 等待所有任務完成
    pthread_join(init_task.tid, NULL);
    pthread_join(high_prio_task.tid, NULL);

    return 0;
}

(4)任務顯式調用 schedule () 函數時:當任務在執行過程中,主動調用schedule()函數時,無論 TIF_NEED_RESCHED 標志位和 preempt_count 的狀態如何,都會觸發任務調度 。這就好比一個運動員主動放棄比賽,把機會讓給其他運動員 。例如,當一個任務在等待某個資源時,它會調用schedule()函數,主動讓出 CPU 資源,以便其他可運行的任務能夠執行 。任務主動調用schedule()函數主動讓出 CPU 的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>

// 任務結構定義
typedef struct {
    const char* name;
    int priority;               // 優先級:數值越大優先級越高
    volatile bool running;      // 任務運行狀態
    pthread_t tid;              // 線程ID
} Task;

// 內核狀態模擬
volatile int preempt_count = 0;          // 搶占計數器
volatile bool tif_need_resched = false;  // 重調度標志位
volatile Task* current_task = NULL;      // 當前運行任務
volatile bool resource_available = false; // 共享資源可用性

// 定義任務:等待資源的任務A和就緒任務B
Task taskA = {"TaskA", 2, false, 0};  // 會主動調用schedule()的任務
Task taskB = {"TaskB", 2, false, 0};  // 可運行的其他任務

// 調度器:選擇就緒的任務(此處簡化為輪詢)
Task* scheduler() {
    if (taskB.running) return &taskB;
    if (taskA.running) return &taskA;
    return NULL;
}

// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
    if (prev == next) return;
    printf("\n[調度器] 上下文切換: %s -> %s\n", 
           prev ? prev->name : "None", next->name);
    current_task = next;
}

// 模擬主動調度函數(無論狀態如何都觸發調度)
void schedule() {
    printf("\n[%s] 主動調用schedule(),讓出CPU\n", current_task->name);
    Task* next_task = scheduler();
    if (next_task && next_task != current_task) {
        context_switch(current_task, next_task);
    }
}

// 模擬資源等待函數(任務會在此處主動調度)
void wait_for_resource() {
    printf("[%s] 嘗試獲取資源...\n", current_task->name);
    while (!resource_available) {
        // 資源未就緒,主動讓出CPU
        schedule();
        // 調度回來后再次檢查資源
        usleep(100000); // 短暫延遲后重試
    }
    printf("[%s] 成功獲取資源!\n", current_task->name);
}

// 任務A的執行函數(會等待資源并主動調度)
void* taskA_func(void* arg) {
    taskA.running = true;
    current_task = &taskA;
    printf("[%s] 開始執行,需要等待資源\n", taskA.name);

    // 等待資源(會主動調用schedule())
    wait_for_resource();

    // 獲得資源后執行后續操作
    printf("[%s] 使用資源完成工作...\n", taskA.name);
    sleep(1);

    taskA.running = false;
    printf("[%s] 任務執行完畢\n", taskA.name);
    return NULL;
}

// 任務B的執行函數(可被調度的任務)
void* taskB_func(void* arg) {
    taskB.running = true;
    // 等待被調度
    while (current_task != &taskB) {
        usleep(100000);
    }

    printf("\n[%s] 獲得CPU,開始執行任務\n", taskB.name);

    // 執行任務B的工作
    for (int i = 0; i < 3; i++) {
        printf("[%s] 執行任務步驟 %d/3\n", taskB.name, i+1);
        sleep(1);
    }

    // 任務B完成后釋放資源
    resource_available = true;
    printf("[%s] 任務完成,釋放資源\n", taskB.name);

    taskB.running = false;
    return NULL;
}

int main() {
    // 創建任務線程
    pthread_create(&taskA.tid, NULL, taskA_func, NULL);
    pthread_create(&taskB.tid, NULL, taskB_func, NULL);

    // 等待所有任務完成
    pthread_join(taskA.tid, NULL);
    pthread_join(taskB.tid, NULL);

    return 0;
}

(5)任務因為某些原因阻塞時:當任務因為等待某個事件(如等待信號量、等待 I/O 操作完成等)而進入阻塞狀態時,會觸發內核搶占 。因為此時該任務暫時無法繼續執行,內核會將 CPU 資源分配給其他可運行的任務 。比如,一個任務在等待從網絡接收數據,由于數據還未到達,它會進入阻塞狀態 。這時,內核會檢查是否有其他可運行的任務,如果有,就會將 CPU 資源分配給這些任務,發生內核搶占 。任務因等待事件進入阻塞狀態而觸發內核搶占的代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <semaphore.h>

// 任務結構定義
typedef struct {
    const char* name;
    int priority;               // 優先級:數值越大優先級越高
    volatile bool running;      // 任務運行狀態
    volatile bool blocked;      // 是否阻塞狀態
    pthread_t tid;              // 線程ID
} Task;

// 內核狀態模擬
volatile Task* current_task = NULL;  // 當前運行任務
sem_t network_data_sem;              // 模擬網絡數據到達的信號量
volatile bool network_data_ready = false;  // 網絡數據是否就緒

// 定義任務:等待網絡數據的任務A和就緒任務B
Task taskA = {"TaskA", 2, false, false, 0};  // 會阻塞等待數據的任務
Task taskB = {"TaskB", 2, false, false, 0};  // 可運行的其他任務

// 調度器:選擇最高優先級的非阻塞任務
Task* scheduler() {
    // 優先選擇非阻塞的任務
    if (taskB.running && !taskB.blocked) return &taskB;
    if (taskA.running && !taskA.blocked) return &taskA;
    return NULL;
}

// 模擬上下文切換
void context_switch(Task* prev, Task* next) {
    if (prev == next) return;
    printf("\n[調度器] 發生搶占:%s (阻塞) -> %s\n", 
           prev->name, next->name);
    current_task = next;
}

// 模擬等待網絡數據(會進入阻塞狀態)
void wait_for_network_data() {
    printf("[%s] 等待網絡數據到達...\n", current_task->name);

    // 進入阻塞狀態
    current_task->blocked = true;
    printf("[%s] 數據未就緒,進入阻塞狀態\n", current_task->name);

    // 觸發調度(因為當前任務已阻塞,必須切換)
    Task* next_task = scheduler();
    if (next_task) {
        context_switch(current_task, next_task);
    }

    // 被喚醒后繼續執行(數據已就緒)
    printf("[%s] 收到網絡數據,退出阻塞狀態\n", current_task->name);
    current_task->blocked = false;
}

// 模擬網絡數據到達(喚醒阻塞任務)
void network_data_arrived() {
    printf("\n[系統] 網絡數據到達!喚醒等待任務\n");
    network_data_ready = true;
    // 將任務A標記為非阻塞
    taskA.blocked = false;
}

// 任務A的執行函數(等待網絡數據)
void* taskA_func(void* arg) {
    taskA.running = true;
    current_task = &taskA;
    printf("[%s] 開始執行,需要接收網絡數據\n", taskA.name);

    // 等待網絡數據(會進入阻塞)
    wait_for_network_data();

    // 處理收到的數據
    printf("[%s] 開始處理網絡數據...\n", taskA.name);
    sleep(1);

    taskA.running = false;
    printf("[%s] 任務執行完畢\n", taskA.name);
    return NULL;
}

// 任務B的執行函數(可被調度的任務)
void* taskB_func(void* arg) {
    taskB.running = true;
    // 等待被調度
    while (current_task != &taskB) {
        usleep(100000);
    }

    printf("[%s] 獲得CPU,開始執行任務\n", taskB.name);

    // 執行任務B的工作
    for (int i = 0; i < 3; i++) {
        printf("[%s] 執行處理步驟 %d/3\n", taskB.name, i+1);
        sleep(1);
    }

    // 任務B完成后,模擬網絡數據到達
    network_data_arrived();

    // 觸發調度,讓任務A繼續執行
    Task* next_task = scheduler();
    if (next_task) {
        context_switch(current_task, next_task);
    }

    taskB.running = false;
    printf("[%s] 任務執行完畢\n", taskB.name);
    return NULL;
}

int main() {
    // 初始化信號量(模擬網絡數據同步)
    sem_init(&network_data_sem, 0, 0);

    // 創建任務線程
    pthread_create(&taskA.tid, NULL, taskA_func, NULL);
    pthread_create(&taskB.tid, NULL, taskB_func, NULL);

    // 等待所有任務完成
    pthread_join(taskA.tid, NULL);
    pthread_join(taskB.tid, NULL);

    sem_destroy(&network_data_sem);
    return 0;
}

通過以上對內核搶占時機的分析,我們可以看到內核搶占機制能夠在各種情況下,根據任務的優先級和系統的狀態,靈活地進行 CPU 資源的分配,確保系統的高效運行 。

三、內核搶占機制的實現方式

3.1自旋鎖與內核搶占

自旋鎖是一種用于多線程同步的鎖機制,在 Linux 內核中被廣泛應用,主要用于保護臨界區免受并發訪問的干擾 。自旋鎖的核心原理基于一個原子操作的標志變量,該變量通常用 0 表示鎖可用,1 表示鎖已被占用 。當一個線程嘗試獲取鎖時,會通過原子操作檢查鎖的狀態 。如果鎖可用(標志變量為 0),則線程將標志變量設置為 1,成功獲取鎖并進入臨界區;如果鎖已被占用(標志變量為 1),線程不會進入睡眠狀態,而是在一個循環中不斷檢查鎖的狀態,即 “自旋”,直到鎖變為可用 。這種機制在多處理器系統中非常有效,因為它避免了線程進入睡眠和被喚醒的開銷,減少了上下文切換 。例如,在多核處理器中,當一個 CPU 上的線程持有自旋鎖時,其他 CPU 上的線程如果想要訪問相同的臨界區,只需在原地自旋等待,而無需進行復雜的上下文切換操作 。

自旋鎖在防止并發訪問臨界區方面起著關鍵作用 。在多線程環境中,臨界區是指同一時間內不允許有超過一個線程進入執行的代碼區域,例如對共享數據結構的訪問代碼 。自旋鎖通過保證在任何時刻只有一個線程能夠持有鎖,從而確保了同一時間只有一個線程可以進入臨界區,避免了多個線程同時訪問臨界區導致的數據競爭和不一致問題 。比如,在多個線程同時訪問共享鏈表時,如果沒有自旋鎖保護,可能會出現一個線程正在插入節點,而另一個線程同時刪除節點的情況,這會導致鏈表結構被破壞 。有了自旋鎖,當一個線程獲取到鎖并進入臨界區操作鏈表時,其他線程只能自旋等待,直到該線程完成操作并釋放鎖 。

然而,自旋鎖的使用對內核搶占有著重要影響 。在持有自旋鎖期間,禁止內核搶占 。這是因為自旋鎖的設計目的是為了在短時間內保護臨界區,假設在持有自旋鎖時允許內核搶占,當一個低優先級任務持有自旋鎖進入臨界區,此時如果高優先級任務搶占了 CPU,而高優先級任務又試圖獲取同一個自旋鎖,就會發生死鎖 。因為低優先級任務被搶占后無法繼續執行以釋放自旋鎖,高優先級任務則會一直自旋等待該鎖,導致兩個任務都無法繼續執行 。例如,在一個多核系統中,任務 A 在 CPU1 上持有自旋鎖訪問共享資源,此時任務 B(高優先級)在 CPU2 上被喚醒并試圖獲取相同的自旋鎖,如果允許內核搶占,任務 B 搶占了 CPU1,而任務 A 因為被搶占無法釋放自旋鎖,任務 B 就會陷入自旋等待,從而造成死鎖 。

在單核系統和多核系統中,自旋鎖與內核搶占的關系有所不同 。在單核系統中,自旋鎖的實現實際上是通過關閉內核搶占來防止其他進程進入臨界區 。因為在單核系統中,同一時間只有一個進程能夠運行,不存在多個 CPU 同時訪問臨界區的問題,但如果系統開啟了搶占,一個進程進入臨界區后可能會被其他進程搶占,導致新進程再次進入臨界區,從而破壞數據結構 。所以,在單核系統中,自旋鎖通過關閉搶占來保證臨界區的獨占訪問 。例如,當一個進程在單核系統中獲取自旋鎖進入臨界區時,其他進程無法搶占 CPU,也就無法進入該臨界區 。而在多核系統中,自旋鎖不僅要防止同一 CPU 上的并發訪問,還要防止不同 CPU 上的并發訪問 。自旋鎖通過讓其他 CPU 上的線程自旋等待,確保了在任何時刻只有一個 CPU 上的線程能夠進入臨界區,同時配合禁止內核搶占,避免了因搶占導致的死鎖問題 。

3.2調度器與內核搶占

調度器在整個內核搶占機制中扮演著核心角色,它是操作系統中負責管理工作負載、決定哪些任務(進程或線程)在什么時候獲得 CPU 資源的關鍵組件 。調度器的基本職責是根據一定的調度策略和算法,從就緒隊列中選擇一個合適的任務,將 CPU 的控制權分配給它,使任務能夠在 CPU 上運行 。在 Linux 系統中,調度器的設計和實現對系統的性能有著決定性的影響,它直接關系到系統資源的合理利用、用戶體驗以及系統的響應速度 。

調度器主要根據任務優先級和搶占條件來實現任務的調度和切換 。在 Linux 內核中,任務被分為不同的優先級,實時進程的優先級通常高于普通進程 。調度器會優先調度高優先級的任務,以確保對時間要求苛刻的任務能夠及時得到處理 。例如,對于實時音頻播放任務,為了保證音頻播放的流暢性,調度器會給予其較高的優先級,使其能夠在 CPU 資源競爭中優先獲得執行機會 。當有高優先級任務進入就緒狀態時,如果當前運行的是低優先級任務,且滿足搶占條件,調度器就會執行任務切換,將 CPU 資源分配給高優先級任務 。

調度器實現任務調度和切換的過程涉及多個關鍵步驟 。當一個任務的時間片耗盡、被阻塞(如等待 I/O 操作完成、等待信號量等)或者有更高優先級的任務被喚醒時,調度器會被觸發 。調度器首先會檢查是否有更高優先級的任務在就緒隊列中等待 。如果有,調度器會根據調度算法選擇優先級最高的任務 。對于實時調度器,它會從最高優先級的隊列開始選擇任務;對于普通調度器(如完全公平調度器 CFS),它會根據任務的虛擬運行時間等因素來選擇下一個要運行的任務 。在選擇好任務后,調度器會執行上下文切換操作 。

上下文切換包括保存當前任務的上下文(如 CPU 寄存器的值、堆棧指針等),然后加載新任務的上下文,使新任務能夠在 CPU 上繼續執行 。例如,當一個任務因為等待 I/O 操作而被阻塞時,調度器會將其上下文保存起來,然后從就緒隊列中選擇一個可運行的任務,加載該任務的上下文,讓其在 CPU 上運行 。在上下文切換過程中,調度器還會處理一些與任務調度相關的操作,如更新任務的運行狀態、調整任務的優先級等 。

調度器與內核搶占密切配合,共同實現高效的 CPU 資源分配 。當內核搶占發生時,調度器會根據搶占條件和任務優先級,及時進行任務的調度和切換 。例如,當中斷處理程序返回內核空間時,如果滿足內核搶占條件(如 TIF_NEED_RESCHED 標志位被設置且 preempt_count 為 0),調度器會立即進行任務調度,將 CPU 資源分配給更緊急的任務 。在多核系統中,調度器還需要負責負載均衡,確保各個 CPU 上的任務負載相對均衡,避免出現某個 CPU 過于繁忙,而其他 CPU 閑置的情況 。通過合理的調度和負載均衡,調度器能夠充分利用 CPU 資源,提高系統的整體性能 。

四、案例分析:內核搶占機制的實際應用

在一個基于 Linux 系統的工業自動化控制系統中,負責實時數據采集和處理的線程被設置為高優先級,它需要及時采集傳感器數據并進行處理,以保證生產過程的精準控制。然而,在系統運行一段時間后,工程師們發現這個高優先級線程有時會長時間無法獲取 CPU 資源,導致數據采集和處理出現延遲,嚴重影響了生產的穩定性和產品質量。經過深入分析,發現問題出在一個低優先級的線程上。這個低優先級線程負責定期更新系統的配置信息,它在執行過程中需要訪問共享的配置文件。為了保證數據一致性,該線程在訪問配置文件時使用了自旋鎖。由于配置文件的更新操作較為復雜,涉及大量的數據讀取和解析,導致低優先級線程持有自旋鎖的時間過長 。

根據內核搶占機制的原理,當低優先級線程持有自旋鎖時,會禁止內核搶占。這就意味著,即使高優先級的實時數據采集線程被喚醒,由于低優先級線程一直持有自旋鎖,使得 preempt_count 不為 0,內核無法進行搶占,高優先級線程只能等待低優先級線程釋放自旋鎖后才能獲取 CPU 資源 。這就好比一條單行道,低優先級線程就像一輛行駛緩慢且占用道路時間很長的大貨車,它一直占據著道路(持有自旋鎖),導致后面著急趕路的高優先級車輛(高優先級線程)無法通行,只能無奈等待 。

為了解決這個問題,工程師們對低優先級線程的代碼進行了優化。他們將配置文件的更新操作進行了拆分,減少每次持有自旋鎖的時間 。同時,將一些非關鍵的數據解析操作放到低優先級線程釋放自旋鎖之后執行,這樣就大大縮短了低優先級線程持有自旋鎖的時長 。經過這樣的優化,高優先級的實時數據采集線程能夠及時獲取 CPU 資源,系統的數據采集和處理恢復正常,生產過程也恢復了穩定 。

代碼示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <sched.h>

// 系統狀態模擬
pthread_spinlock_t config_lock;       // 保護配置文件的自旋鎖
volatile bool system_running = true;  // 系統運行標志

// 高優先級實時數據采集線程函數
void* realtime_data_thread(void* arg) {
    // 設置線程為高優先級
    struct sched_param param = {.sched_priority = 90};
    pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);

    int count = 0;
    while (system_running) {
        // 嘗試獲取CPU執行數據采集
        printf("\n[高優先級線程] 嘗試采集傳感器數據...\n");

        // 模擬數據采集和處理(需要及時執行)
        printf("[高優先級線程] 成功采集并處理數據 #%d\n", ++count);

        // 正常情況下10ms采集一次
        usleep(10000);
    }
    return NULL;
}

// 未優化的低優先級配置更新線程函數(問題版本)
void* unoptimized_config_thread(void* arg) {
    // 設置線程為低優先級
    struct sched_param param = {.sched_priority = 10};
    pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);

    int update_count = 0;
    while (system_running) {
        // 定期更新配置(每2秒一次)
        sleep(2);
        printf("\n[低優先級線程] 開始更新配置 #%d\n", ++update_count);

        // 獲取自旋鎖(會禁用內核搶占)
        pthread_spin_lock(&config_lock);
        printf("[低優先級線程] 持有自旋鎖,開始處理配置文件...\n");

        // 模擬長時間持有鎖進行復雜處理(問題根源)
        // 實際系統中這可能是大量文件IO和數據解析
        printf("[低優先級線程] 正在進行復雜的配置解析(長時間占用鎖)...\n");
        sleep(3);  // 長時間持有鎖,導致高優先級線程無法搶占

        // 釋放自旋鎖
        pthread_spin_unlock(&config_lock);
        printf("[低優先級線程] 釋放自旋鎖,配置更新完成\n");
    }
    return NULL;
}

// 優化后的低優先級配置更新線程函數
void* optimized_config_thread(void* arg) {
    // 設置線程為低優先級
    struct sched_param param = {.sched_priority = 10};
    pthread_setschedparam(pthread_self(), SCHED_FIFO, ?m);

    int update_count = 0;
    while (system_running) {
        // 定期更新配置(每2秒一次)
        sleep(2);
        printf("\n[低優先級線程] 開始優化版配置更新 #%d\n", ++update_count);

        // 準備工作:在獲取鎖之前進行必要的準備
        printf("[低優先級線程] 準備配置數據(無鎖操作)...\n");
        usleep(500000);  // 0.5秒準備時間(不持有鎖)

        // 獲取自旋鎖(僅在必要時持有)
        pthread_spin_lock(&config_lock);
        printf("[低優先級線程] 持有自旋鎖,執行關鍵更新...\n");

        // 僅在鎖內執行必要的關鍵操作(大幅縮短持有時間)
        printf("[低優先級線程] 執行核心配置寫入(短時間占用鎖)...\n");
        usleep(500000);  // 0.5秒關鍵操作(大幅縮短)

        // 立即釋放自旋鎖
        pthread_spin_unlock(&config_lock);
        printf("[低優先級線程] 釋放自旋鎖\n");

        // 非關鍵操作放到鎖外執行
        printf("[低優先級線程] 執行后續解析和處理(無鎖操作)...\n");
        usleep(2000000);  // 2秒非關鍵操作(不影響搶占)
    }
    return NULL;
}

int main(int argc, char* argv[]) {
    // 初始化自旋鎖
    pthread_spin_init(&config_lock, PTHREAD_PROCESS_PRIVATE);

    pthread_t realtime_tid, config_tid;

    // 創建高優先級實時線程
    pthread_create(&realtime_tid, NULL, realtime_data_thread, NULL);

    // 根據參數選擇運行未優化或優化版本
    if (argc > 1 && argv[1][0] == '1') {
        printf("=== 運行未優化版本(會出現優先級反轉問題) ===\n");
        pthread_create(&config_tid, NULL, unoptimized_config_thread, NULL);
    } else {
        printf("=== 運行優化版本(解決優先級反轉問題) ===\n");
        pthread_create(&config_tid, NULL, optimized_config_thread, NULL);
    }

    // 運行5秒后結束
    sleep(5);
    system_running = false;

    // 等待線程結束
    pthread_join(realtime_tid, NULL);
    pthread_join(config_tid, NULL);

    // 銷毀自旋鎖
    pthread_spin_destroy(&config_lock);
    return 0;
}

在系統運行過程中,高優先級的 realtime_data_thread 需要每 10ms 采集一次傳感器數據以滿足實時控制需求,而低優先級的 unoptimized_config_thread 使用自旋鎖更新配置文件并長時間持有該鎖(約3秒)。由于自旋鎖持有期間會禁用內核搶占(preempt_count不為0),導致高優先級線程在此期間無法搶占執行,從而影響實時性能。

在優化方案中,我們將配置更新操作拆分為三個關鍵階段:首先,在無鎖準備階段完成所有數據預處理工作,確保高優先級線程不被阻塞;其次,通過重構代碼將必須受保護的關鍵操作集中在0.5秒內完成,大幅縮短自旋鎖持有時間;最后,在釋放鎖后執行非關鍵的配置解析等后續處理。這種設計既保證了數據一致性,又顯著降低了實時線程被阻塞的風險。

在編譯與運行階段,用戶可通過執行未優化版本程序(使用命令./program 1)來模擬原始配置更新導致的實時線程阻塞問題,而通過運行優化版本(直接執行./program)則可驗證拆分三階段操作后縮短鎖持有時間的解決方案效果。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2017-08-16 16:20:01

Linux內核態搶占用戶態搶占

2021-05-19 07:56:26

Linux內核搶占

2019-03-27 09:14:38

CPU內核應用程序

2020-01-16 09:55:28

STGW流量內核

2025-10-11 04:11:00

2025-11-03 04:00:00

2025-09-08 02:00:00

2021-04-08 09:32:17

鴻蒙HarmonyOS應用

2010-10-15 14:58:36

AMD

2016-09-20 15:21:35

LinuxInnoDBMysql

2010-12-22 13:09:23

Linux性能監測CPU

2014-07-28 16:47:41

linux性能

2022-03-22 08:52:40

KubernetesCPU內存資源

2021-05-11 10:40:29

JUCAQSJava

2022-04-26 13:41:16

區塊鏈比特幣數據庫

2021-05-12 15:16:17

JUCAQSJava

2010-04-27 18:24:56

AIX CPU

2009-10-29 09:41:01

Linux內核DeviceMappe

2016-08-10 07:41:30

5G網絡5G毫米波FCC

2009-04-08 08:39:59

IphoneSymbian移動OS
點贊
收藏

51CTO技術棧公眾號

天堂成人在线观看| 亚洲最大的黄色网址| 欧美电影免费观看| 国产精品福利av | 一区二区三区四区精品视频| 五月激情综合色| 亚洲欧美日韩国产成人综合一二三区 | 国产精品嫩草影院久久久| 登山的目的在线| 日韩欧美黄色| 欧美一级免费大片| 久久午夜夜伦鲁鲁一区二区| 欧美xxxx视频| 国产精品美日韩| 麻豆精品视频| 亚洲国产一二三区| 九色|91porny| 国产成人精品在线视频| 久久黄色小视频| 国产高清一区| 亚洲午夜av久久乱码| 白丝校花扒腿让我c| 黄色成人小视频| 色综合久久精品| 人妻av中文系列| 最近中文字幕免费mv2018在线| 亚洲国产精品精华液2区45| av日韩免费电影| 一区二区美女视频| 久久永久免费| 欧美最近摘花xxxx摘花| 亚洲国产精一区二区三区性色| 亚洲精品成人无限看| 一区二区三区在线播放欧美| 国产在线观看无码免费视频| 2021年精品国产福利在线| 在线电影院国产精品| 乌克兰美女av| 精品欧美日韩精品| 色婷婷久久一区二区三区麻豆| 麻豆tv在线播放| 美女精品导航| 亚洲成年人影院| 日本福利视频一区| 欧美高清另类hdvideosexjaⅴ| 成人欧美一区二区三区白人| 樱花www成人免费视频| 国产在线观看免费| 国产欧美日韩视频在线观看| 日产精品高清视频免费| 国产对白叫床清晰在线播放| 国产女主播一区| 性欧美videosex高清少妇| 久久精品国产亚洲a∨麻豆| 久久久久久久久久久99999| 久久视频在线观看中文字幕| 外国精品视频在线观看 | 成人在线激情视频| 97人妻精品一区二区三区视频| 麻豆精品在线观看| 91精品视频免费看| 国产不卡av在线播放| 国产成人免费网站| 国产尤物91| 黄色av免费在线观看| 国产日韩在线不卡| 黄频视频在线观看| 另类视频在线| 疯狂做受xxxx欧美肥白少妇| 成人在线观看a| 久久亚洲精品中文字幕| 日韩一区二区三区在线观看| 香蕉久久久久久av成人| 特黄特色欧美大片| 中文字幕日韩精品在线| 午夜国产福利一区二区| 亚洲激情视频| 国产精品九九九| 国产免费av电影| 成人v精品蜜桃久久一区| 鲁片一区二区三区| 欧美成人二区| 亚洲成人av电影| 亚洲欧洲日本精品| 91精品国产乱码久久久竹菊| 日韩av影片在线观看| 中文字幕第二区| 欧美视频二区| 日韩av电影在线网| 国产精品自产拍| 97久久久精品综合88久久| 视频一区国产精品| 欧美bbbxxxxx| 在线观看日韩国产| 激情小说欧美色图| 国内精品伊人久久久| 欧美成人一区二区三区电影| 欧美精品一二三四区| 国产一区二区免费视频| 噜噜噜噜噜久久久久久91| 国产精品实拍| 日本丰满少妇一区二区三区| 免费在线观看日韩av| 欧美日韩在线二区| 97国产精品视频| 国产又粗又黄又爽| 久久久久久久久蜜桃| 久久在线中文字幕| 91成人福利社区| 亚洲深夜福利在线| 日本三级片在线观看| 国产一区二区中文字幕| 日韩理论片在线观看| heyzo高清在线| 欧美一区二区视频网站| 国产真实乱人偷精品人妻| 雨宫琴音一区二区在线| 成人女保姆的销魂服务| 成人在线免费观看| 欧美日韩在线视频一区| 秘密基地免费观看完整版中文| 日韩欧美一区二区三区免费看| 欧美诱惑福利视频| 亚洲日本中文字幕在线| 一区二区高清免费观看影视大全 | 欧美大陆国产| 国产亚洲欧洲高清| 黑人一级大毛片| 成人精品高清在线| 99er在线视频| 日韩欧美另类中文字幕| 久久影院资源网| 亚洲综合网av| 国产精品久久久久久福利一牛影视| 国产男女在线观看| 天海翼亚洲一区二区三区| 77777亚洲午夜久久多人| 亚洲av无码乱码国产精品久久 | 国产亚洲欧美一区| 日韩黄色片网站| 91丨九色丨国产丨porny| 亚洲色欲久久久综合网东京热| 久久久久久爱| 欧美大奶子在线| 国产高清免费观看| 夜夜亚洲天天久久| 美女久久久久久久久| 伊人蜜桃色噜噜激情综合| 成人看片视频| 欧美aa在线| 亚洲欧美另类在线观看| www.久久精品视频| 国产精品区一区二区三| 日本高清一区二区视频| 综合一区二区三区| 国产精品推荐精品| 久久青草伊人| 亚洲人午夜精品| 91精品国产综合久| 亚洲精品乱码久久久久久日本蜜臀| 国产xxxxhd| 伊人久久综合| 任我爽在线视频精品一| 99欧美精品| 久久99精品国产99久久6尤物| 亚洲精品综合网| 欧美丝袜一区二区三区| 美国黄色特级片| 国产精品一色哟哟哟| 人妻少妇精品无码专区二区| 精品免费一区二区| 91久久精品www人人做人人爽| 免费影视亚洲| 亚洲图片在线综合| 国产女人高潮的av毛片| 婷婷六月综合亚洲| 啪啪一区二区三区| 粉嫩aⅴ一区二区三区四区| 欧美韩国日本在线| 亚洲精品午夜av福利久久蜜桃| 国产精品久久久一区二区三区| 在线观看爽视频| 日韩天堂在线视频| 欧美特黄一级视频| 欧美色图片你懂的| 久久久久久久九九九九| 国产日韩欧美高清| 最新国产精品自拍| 蜜乳av一区二区三区| 男的插女的下面视频| 日韩国产在线| 久精品国产欧美| 大胆国模一区二区三区| 91高潮精品免费porn| 在线观看a级片| 亚洲一区999| 天天干视频在线观看| 欧美日韩亚州综合| 中文字幕视频网| 亚洲理论在线观看| 9.1片黄在线观看| 99久久精品国产一区| 特级黄色片视频| 秋霞国产午夜精品免费视频| 六月婷婷在线视频| 午夜精品婷婷| 亚洲一区二区自拍偷拍| 要久久电视剧全集免费| 俄罗斯精品一区二区| 97色婷婷成人综合在线观看| 国产成人亚洲综合91精品| 丁香高清在线观看完整电影视频 | 国偷自产一区二区免费视频| 欧美精品videos另类日本| 又爽又大又黄a级毛片在线视频| 亚洲黄色av女优在线观看| 99精品视频在线播放免费| 欧美午夜精品电影| 黄色大全在线观看| 日韩欧美国产中文字幕| 日本少妇做爰全过程毛片| 亚洲欧美电影院| 国产性生活大片| 中文字幕日韩一区| 亚洲一二三精品| 国产午夜三级一区二区三| 成年人在线观看av| 91亚洲精品一区二区乱码| 欧美大喷水吹潮合集在线观看| 国产精品99久| 丰满饥渴老女人hd| 国产资源在线一区| 国产男女无遮挡猛进猛出| 狠狠色丁香婷综合久久| 热久久久久久久久| 韩国女主播成人在线| 在线免费观看av网| 国产一区二区三区蝌蚪| 色综合五月婷婷| 国产精品系列在线播放| 日批视频免费看| 成人久久视频在线观看| 动漫av在线免费观看| 成人免费视频播放| 中文在线永久免费观看| 91网站在线观看视频| 精品国产无码在线观看| 国产欧美日韩一区二区三区在线观看| 色欲狠狠躁天天躁无码中文字幕 | 欧美国产日韩综合| 亚洲一区二区三区爽爽爽爽爽 | 欧洲一区在线观看| 中文永久免费观看| 在线成人免费观看| 精品国产av鲁一鲁一区| 亚洲精品在线电影| 青青草免费观看免费视频在线| 亚洲人在线视频| 永久免费在线观看视频| 九九热99久久久国产盗摄| 牛牛精品在线视频| 日韩免费在线观看视频| 欧美日韩卡一| 99精品99久久久久久宅男| 精品亚洲自拍| 翔田千里亚洲一二三区| 亚洲精品二区三区| 黄色一级视频片| 蜜臀av性久久久久蜜臀aⅴ四虎 | 国产精品美女久久久久久久| 国产精品 欧美激情| 亚洲成人在线观看视频| 成人av网站在线播放| 91精品国产综合久久福利| 全国男人的天堂网| 在线色欧美三级视频| 秋霞在线午夜| 国产成人精品免高潮费视频| 国产美女亚洲精品7777| 国内不卡一区二区三区| 全球成人免费直播| www.av91| 蜜桃久久久久久| 亚洲精品激情视频| 无码一区二区精品| 国产精品久久久久影院色老大| 久久精品国产亚洲AV无码男同| 欧美小视频在线观看| 国产特黄一级片| 亚洲欧美第一页| 高h视频在线播放| 国产精品色婷婷视频| 欧美18xxxx| 男同互操gay射视频在线看| 久久精品九九| 精品无码av一区二区三区| 中文字幕av资源一区| 国产无遮挡又黄又爽在线观看| 欧美性受极品xxxx喷水| 天天射天天色天天干| 久久久久北条麻妃免费看| 韩国成人漫画| 国产呦系列欧美呦日韩呦| 亚洲欧美日韩高清在线| www日韩在线观看| 99久久精品国产导航| 男人与禽猛交狂配| 欧美视频三区在线播放| 香蕉国产在线视频| 欧美激情videoshd| 成人精品在线| 日韩欧美亚洲日产国| 国产精品美女久久久浪潮软件| 日本黄色一级网站| 中文字幕在线一区| 最近中文字幕在线观看| 亚洲精品日韩丝袜精品| 24小时免费看片在线观看| 亚洲在线观看视频| 小小影院久久| 九九热免费在线观看| 国产精品另类一区| jizz国产在线| 一区二区三区天堂av| 欧美天堂视频| 欧美韩国日本精品一区二区三区| 亚洲激情网址| 老熟妇精品一区二区三区| 一区二区三区日韩精品视频| 国产视频一区二区三| 久久精品中文字幕| 99精品在线免费观看| 中文字幕中文字幕一区三区| 久久国产精品色婷婷| 男人天堂资源网| 538在线一区二区精品国产| 久久精品视频免费看| 91日韩在线视频| 亚洲乱码精品| 黑人性生活视频| 亚洲大片在线观看| 午夜av免费在线观看| 欧美一级视频免费在线观看| 亚洲第一二三区| 国产视频在线视频| 国产精品丝袜久久久久久app| 少妇一级淫片日本| www.xxxx精品| 韩国三级成人在线| 精品一二三四五区| av资源站一区| 欧美人一级淫片a免费播放| 中文字幕日韩欧美| 麻豆精品久久| 日韩网站在线免费观看| 91丨porny丨最新| 国产九色91回来了| 久久精品亚洲热| www.国产精品一区| 东京热加勒比无码少妇| 伊人国产精品| 亚洲欧美综合另类中字| 在线免费av资源| 午夜精品一区二区三区在线观看 | 男女av在线| 国产精品视频一区国模私拍| 99re久久最新地址获取| 无人码人妻一区二区三区免费| 亚洲二区在线观看| 韩国免费在线视频| 成人福利网站在线观看| 亚洲电影在线| 色屁屁草草影院ccyy.com| 日韩一区二区三区在线观看| 中文字幕色婷婷在线视频| 亚洲免费视频一区| 国产精品66部| 亚洲婷婷综合网| 久久视频在线视频| 欧美亚视频在线中文字幕免费| 亚洲国产精品毛片av不卡在线| 18欧美亚洲精品| 四虎影视在线播放| 91精品久久久久久久久久久| 亚洲黄色影片| 精品视频第一页| 欧美精品一区二区久久婷婷| 素人一区二区三区| 欧美 日韩 亚洲 一区| 国产精品视频第一区| 免费的黄色av| 成人在线免费观看视视频| 欧美在线综合| 久久丫精品久久丫| 中文字幕欧美专区| 私拍精品福利视频在线一区| 97人人模人人爽人人澡| 日本乱码高清不卡字幕| 俄罗斯一级**毛片在线播放| 一区二区在线观|