使用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
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...