使用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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...