使用C語言構建一個獨立棧協程和共用棧協程的任務調度系統

来源:https://www.cnblogs.com/const-zpc/p/18007643
-Advertisement-
Play Games

使用了標準庫頭文件 <setjmp.h>中的 setjmp 和 longjmp兩個函數,構建了一個簡單的查詢式協作多任務系統,支持獨立棧和共用棧兩種任務。 其中涉及到獲取和設置棧的地址操作,因此還需要根據不同平臺提供獲取和設置棧的地址操作(一般是彙編語言,因為涉及到寄存器) 該調度系統僅運行在一個實 ...


使用了標準庫頭文件 <setjmp.h>中的 setjmplongjmp兩個函數,構建了一個簡單的查詢式協作多任務系統,支持獨立棧共用棧兩種任務。

  1. 其中涉及到獲取和設置棧的地址操作,因此還需要根據不同平臺提供獲取和設置棧的地址操作(一般是彙編語言,因為涉及到寄存器)
  2. 該調度系統僅運行在一個實際的線程中,因此本質上屬於協程
  3. 獨立棧任務都有自己獨立的運行棧空間,互不幹擾;共用棧任務共用一個運行棧空間。

特點

  • 無任務優先順序搶占的功能。

  • 任務切換的時機完全取決於正在運行的任務,體現協作

  • 支持獨立棧共用棧兩種任務,根據不同的應用場景決定。

  • 查詢式的調度方式,當前任務切換時,查詢下個任務是否需要執行。

  • 移植性強,只需要修改設置棧和獲取當前棧地址的巨集即可。

  • 相對於時間片論法的任務調度來說,查詢式協作多任務系統有以下特點:

    • 無需使用定時器做為任務調度
    • 每個任務都可以使用while迴圈,用於執行任務並保持程式的運行,程式結構清晰
    • 每個任務都可以隨時阻塞等待,甚至可以在嵌套的子函數中阻塞等待
    • 通過阻塞等待,無需使用狀態機等較為複雜的方式來優化縮減每個任務的執行時長
  • 相對於RTOS操作系統來說,查詢式協作多任務系統有以下特點:

    • 沒有任務優先順序搶占式的功能,因此臨界資源(中斷除外)和優先順序反轉的問題也不存在
    • 允許用戶或應用程式根據需要自由地切換到下一個就緒任務
    • 通過自主調度和管理任務,查詢式協作多任務系統可以提高工作效率
    • 沒有操作系統的複雜

功能設計

運行棧空間:程式運行中發生函數調用等情況需要使用的棧記憶體空間

獨立棧任務(有棧任務)

每個獨立棧任務都擁有自己獨立的運行棧空間,可以隨時隨地阻塞等待,保存上下文後切換到下一個任務執行

獨立棧任務在切換下一個任務時,不會操作運行棧,只對上下文切換

共用棧任務(無棧任務)

每個共用棧任務都沒有自己獨立的運行棧空間,雖然也能阻塞等待,但是僅限於在任務入口函數中使用,禁止在任務的子函數(嵌套函數)中阻塞等待;並且在該任務入口函數中不建議定義相關變數。

  • 每個任務有自己的獨立備份棧(用來備份運行棧的棧頂部分數據);運行棧通常比備份棧要大很多,否則任務函數無法正常運行多級嵌套的函數
  • 共用棧任務在切換下一個任務時會將當前運行棧(共用棧)提前設置好的備份棧大小(巨集配置)拷貝到記憶體備份起來,等下次即將執行時再從記憶體中拷貝到運行棧(共用棧)進行恢復
  • 通過修改加大備份棧大小(巨集配置)的值,可以在共用棧任務入口函數定義變數,這樣可以避免這些變數的值沒有備份導致丟失,或者通過 static 定義局部變數
  • 該類型任務適合於輕量的任務處理,一般都是調用封裝好的函數即可

註:這裡的共用棧任務和常規的實現有一些差異,常規的實現是使用堆申請記憶體保存棧的數據,用多少申請多少進行保存,而這裡的實現僅僅保存了一部分數據。

任務創建

  1. 在調度系統啟動前,至少要先創建一個任務,否則直接退出
  2. 可以在任務中創建新的任務,不管是獨立棧任務還是共用棧任務
    • 獨立棧任務中可以創建新的獨立棧任務和共用棧任務
    • 共用棧任務中同樣可以創建新的獨立棧任務和共用棧任務,而且在創建共用棧任務時可以使用同一個共用棧
  3. 獨立棧任務和共用棧任務一共可以創建最多32個任務(需要修改巨集配置)

任務銷毀

  • 沒有提供該功能介面函數,任務入口函數主動退出則自動將任務銷毀。
  • 可以通過等待任務退出介面函數在其他任務中等待該任務退出。

任務阻塞

當前任務阻塞提供兩種方式:

  • 時間阻塞:需要阻塞多長時間,等時間滿足後才會繼續執行
  • 事件阻塞:通過事件阻塞,只有事件觸發後才會繼續執行

使用說明

任務創建/退出

對於創建獨立棧任務還是共用棧任務的示例代碼:


uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];

uint8_t g_sharedStack[1024 * 2];

// 執行完成就退出的任務
void taskfunc3(int arg)
{
    ...
    cotOs_Wait(1000);
    ...
    cotOs_Wait(1000);
}

void taskfunc1(int arg)
{
   /* 不管taskfunc1是獨立棧任務還是共用棧任務,都支持創建子任務 */
   cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0);  // 創建獨立棧任務
   cotOs_CreatTask(taskfunc3, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0); // 創建共用棧任務

    while (1)
    {
        ...
        cotOs_Wait(1000);
    }
}

void taskfunc2(int arg)
{
    while (1)
    {
        ...
        cotOs_Wait(10);
    }
}

int main(void)
{
    cotOs_Init(GetTimerMs);
#if 0
    /* 創建獨立棧任務 */
    cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_UNIQUE_STACK, g_task2Stack, sizeof(g_task2Stack), 0);
#else
    /* 創建共用棧任務 */
    cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
#endif
    cotOs_Start();
}

任務限制

對於創建獨立棧任務還是共用棧任務,共用棧任務有限制要求,禁止在任務入口函數的嵌套函數中阻塞


uint8_t g_task1Stack[1024 * 2];
uint8_t g_sharedStack[1024 * 2];

void func1_1(void)
{
    ...
    cotOs_Wait(1000);
    ...
    cotOs_Wait(1000);
}

/* 獨立棧任務 */
void taskfunc1(int arg)
{
    int arr[10];   // 可以直接定義變數使用

    while (1)
    {
        func1_1();  // 可以在嵌套函數中使用阻塞等待
        ...
        cotOs_Wait(1000);
    }
}

void func2_1(void)
{
    ...
}

/* 共用棧任務 */
void taskfunc2(int arg)
{
    static int arr[10];  // 建議使用static定義任務內變數或者不定義變數

    while (1)
    {
        func2_1();  // 禁止在嵌套函數中使用阻塞等待
        ...
        cotOs_Wait(10);
    }
}

int main(void)
{
    cotOs_Init(GetTimerMs);

    /* 創建獨立棧任務 */
    cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);

    /* 創建共用棧任務 */
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);

    cotOs_Start();
}

任務阻塞/退出

通過時間和事件的方式阻塞


uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];

uint8_t g_sharedStack[1024 * 2];

CotOSCondition_t g_eventCv;

// 執行完成就退出的任務
void taskfunc3(int arg)
{
    ...
    cotOs_ConditionWait(&g_eventCv);
    ...
}

void taskfunc1(int arg)
{
   cotOsTask_t task = cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0);

    while (1)
    {
        ...
        cotOs_Wait(1000);

        if (...)
        {
            // 等待 taskfunc3 任務運行結束後才退出 taskfunc1
            cotOs_Join(task);
            break;
        }
    }
}

void taskfunc2(int arg)
{
    while (1)
    {
        ...
        cotOs_Wait(10);

        if (...)
        {
            cotOs_ConditionNotify(&g_eventCv);  // 通知 taskfunc3 繼續執行
        }
    }
}

int main(void)
{
    cotOs_Init(GetTimerMs);
    cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);

    cotOs_Start();
}

不同棧類型任務應用場景

  • 獨立棧任務(有棧任務)

    • 重量級任務: 提供更多的控制,適用於需要更精確地管理任務狀態的情況和執行計算密集型任務的場景
    • 更可預測的記憶體使用: 在創建時分配棧空間,可以更好地控制記憶體使用,適用於需要更可預測記憶體行為的場景
    • 遞歸調用: 更容易處理遞歸調用,因為每個任務都有獨立的棧空間
  • 共用棧任務(無棧任務)

    • 輕量級任務: 通常更輕量,適用於大量小任務的場景。
    • 記憶體效率: 適用於記憶體受限的環境,因為不需要為每個任務分配各自的棧空間(備份棧除外)。

代碼鏈接

cot_os

本文來自博客園,作者:大橙子瘋,轉載請註明原文鏈接:https://www.cnblogs.com/const-zpc/p/18007643


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

-Advertisement-
Play Games
更多相關文章
  • 目錄一、LVM概述二、基本術語三、PE、PV、VG、LV之間的關係四、LVM的工作原理五、LVM的使用1、部署邏輯捲第一步:還原快照,併在虛擬機添加兩塊新硬碟設備,開機第二步:讓新添加的兩塊硬碟設備支持LVM技術第三步:把兩塊硬碟設備加入到storage捲組中,查看捲組狀態第四步:切割出一個148M ...
  • docker-compose-nacos.yml services: mysql-nacos: restart: always image: mysql:5.7.18 container_name: mysql-lable volumes: - /Users/docker/mysql/source/ ...
  • 一:配置預設文檔 輸入ip打開哪個頁面是由預設文檔設定的 1.打開IIS配置頁面,點擊網站。我們的預設站點已經啟動,可以看到綁定的ip和網頁的路徑 2.選中Default Web Site,可以看到有個預設文檔 3.打開預設文檔發現已經有五個條目,右鍵添加我們想要的預設文檔名 如果想要預設文檔名為f ...
  • 實驗介紹:IIS的作用 IIS是web伺服器中常見的一種。當客戶端想訪問某個功能變數名稱時,向web伺服器發出請求。web伺服器返回網頁的代碼做出回應。客戶端解析代碼生成網頁。 一:安裝IIS 1.打開一臺windows伺服器,修改IP為192.168.1.208 2.打開伺服器管理器,安裝web伺服器(I ...
  • 故障如圖: 即所有原本顯示【桌面】的地方,變成英文Desktop了,雖說本體目錄名就是desktop,只是顯示出來的名稱做了本地化,但顯示故障也是故障。 解決辦法: 先看desktop目錄的屬性,正常的屬性是這樣的: 即只有一個R屬性,如果你的不是這樣,就要讓它變成這樣,我的情況是缺乏R屬性,所以要 ...
  • 哈嘍大家好,我是鹹魚。 接觸過 Linux 的小伙伴們都知道在 Linux (或者說類 Unix)中,有三種類型的用戶: 超級用戶(UID 為 0):即 root 用戶,擁有最高許可權。 系統用戶(UID 為 1~999):系統內建用戶,用於運行系統服務和守護進程。 普通用戶(UID 為 1000~6 ...
  • 本文介紹Git軟體與GitHub平臺的基本內容、使用方法與應用場景等。 目錄1 初步介紹2 使用方法2.1 GitHub配置2.2 Git配置2.3 代碼上傳至GitHub 1 初步介紹 首先,什麼是GitHub與Git?為什麼我們要運用這些工具? 首先從GitHub說起。如果大家參加過數學建模比賽 ...
  • ## 複製、粘貼及其他常規 Ctrl + X 剪切選定項。 Ctrl + C(或 Ctrl + Insert) 複製選定項。 Ctrl + V(或 Shift + Insert) 粘貼選定項。 Ctrl + Z 撤消操作。 Alt + Tab 在打開的應用­之間切換。 Alt + F4 關閉活動項, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...