使用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
  • 在C#中使用SQL Server實現事務的ACID(原子性、一致性、隔離性、持久性)屬性和使用資料庫鎖(悲觀鎖和樂觀鎖)時,你可以通過ADO.NET的SqlConnection和SqlTransaction類來實現。下麵是一些示例和概念說明。 實現ACID事務 ACID屬性是事務處理的四個基本特征, ...
  • 我們在《SqlSugar開發框架》中,Winform界面開發部分往往也用到了自定義的用戶控制項,對應一些特殊的界面或者常用到的一些局部界面內容,我們可以使用自定義的用戶控制項來提高界面的統一性,同時也增強了使用的便利性。如我們Winform界面中用到的分頁控制項、附件顯示內容、以及一些公司、部門、菜單的下... ...
  • 在本篇教程中,我們學習瞭如何在 Taurus.MVC WebMVC 中進行數據綁定操作。我們還學習瞭如何使用 ${屬性名稱} CMS 語法來綁定頁面上的元素與 Model 中的屬性。通過這些步驟,我們成功實現了一個簡單的數據綁定示例。 ...
  • 是在MVVM中用來傳遞消息的一種方式。它是在MVVMLight框架中提供的一個實現了IMessenger介面的類,可以用來在ViewModel之間、ViewModel和View之間傳遞消息。 Send 接受一個泛型參數,表示要發送的消息內容。 Register 方法用於註冊某個對象接收消息。 pub ...
  • 概述:在WPF中,通過EventHandler可實現基礎和高級的UI更新方式。基礎用法涉及在類中定義事件,併在UI中訂閱以執行更新操作。高級用法藉助Dispatcher類,確保在非UI線程上執行操作後,通過UI線程更新界面。這兩種方法提供了靈活而可靠的UI更新機制。 在WPF(Windows Pre ...
  • 概述:本文介紹了在C#程式開發中如何利用自定義擴展方法測量代碼執行時間。通過使用簡單的Action委托,開發者可以輕鬆獲取代碼塊的執行時間,幫助優化性能、驗證演算法效率以及監控系統性能。這種通用方法提供了一種便捷而有效的方式,有助於提高開發效率和代碼質量。 在軟體開發中,瞭解代碼執行時間是優化程式性能 ...
  • 概述:Cron表達式是一種強大的定時任務調度工具,通過配置不同欄位實現靈活的時間規定。在.NET中,Quartz庫提供了簡便的方式配置Cron表達式,實現精準的定時任務調度。這種靈活性和可擴展性使得開發者能夠根據需求輕鬆地制定和管理定時任務,例如每天備份系統日誌或其他重要操作。 Cron表達式詳解 ...
  • 概述:.NET提供多種定時器,如System.Windows.Forms.Timer適用於UI,System.Web.UI.Timer用於Web,System.Diagnostics.Timer用於性能監控,System.Threading.Timer和System.Timers.Timer用於一般 ...
  • 問題背景 有同事聯繫我說,在生產環境上,訪問不了我負責的common服務,然後我去檢查common服務的health endpoint, 沒問題,然後我問了下異常,timeout導致的System.OperationCanceledException。那大概率是客戶端的問題,會不會是埠耗盡,用ne ...
  • 前言: 在本篇 Taurus.MVC WebMVC 入門開發教程的第四篇文章中, 我們將學習如何實現數據列表的綁定,通過使用 List<Model> 來展示多個數據項。 我們將繼續使用 Taurus.Mvc 命名空間,同時探討如何在視圖中綁定並顯示一個 Model 列表。 步驟1:創建 Model ...