哈哈哈哈哈操欧洲电影,久草网在线,亚洲久久熟女熟妇视频,麻豆精品色,久久福利在线视频,日韩中文字幕的,淫乱毛视频一区,亚洲成人一二三,中文人妻日韩精品电影

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

RTOS內(nèi)功修煉記(一)— 任務(wù)到底應(yīng)該怎么寫?

冬至子 ? 來源:Mculover666 ? 作者:Mculover666 ? 2023-12-01 16:36 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本篇文章講述了任務(wù)的三大元素:任務(wù)控制塊、任務(wù)棧、任務(wù)入口函數(shù),并講述了編寫RTOS任務(wù)入口函數(shù)時三個重要的注意點(diǎn)。

1. 知識點(diǎn)回顧

在正式開始講解內(nèi)容之前,我會先回顧一下基礎(chǔ)知識點(diǎn),請確保你已經(jīng)了解并掌握。

1.1. 任務(wù)的創(chuàng)建方法

在用戶層調(diào)用API創(chuàng)建一個任務(wù),通常的流程如下:

① 創(chuàng)建一個數(shù)組作為任務(wù)棧:

#define TASK1_STACK_SIZE    512  
k_stack_t task1_stack[TASK1_STACK_SIZE];

② 創(chuàng)建一個任務(wù)控制塊:

k_task_t    task1;

③ 編寫任務(wù)入口函數(shù):

void task1_entry(void *arg)  
{  
    while(1)  
    {  
        printf("task1 is runningrn");  
        tos_task_delay(1000);  
    }  
}

④ 調(diào)用系統(tǒng)API創(chuàng)建任務(wù):

ret = tos_task_create(&task1,  
                      "task1",  
                      task1_entry,  
                      NULL,  
                      TASK1_PRO,  
                      task1_stack,  
                      TASK1_STACK_SIZE,  
                      10);

創(chuàng)建之后任務(wù)為就緒態(tài)(處于系統(tǒng)就緒隊列中),等待系統(tǒng)調(diào)度器調(diào)度執(zhí)行。

1.2. STM32內(nèi)存分布

閱讀之后,你應(yīng)該要知道,STM32(Cortex-M3)中Flash和SRAM的內(nèi)存空間如下:

image.png

其中Flash存儲空間中又分為文本段、只讀數(shù)據(jù)段、復(fù)制數(shù)據(jù)段:

image.png

其中SRAM存儲空間中又分為data數(shù)據(jù)段、bss數(shù)據(jù)段、堆空間、棧空間:

image.png

并且還要知道不同的變量類型,它對應(yīng)的存儲位置在哪里,如果沒有,一定要閱讀上文之后再回來看,這是理解之后內(nèi)容的基礎(chǔ)。

1.3. Cortex-M3/4系列內(nèi)核

CrortexM3/4系列內(nèi)核中的寄存器組都有16個寄存器,如圖所示,寄存器組通常都是CPU用于數(shù)據(jù)處理和運(yùn)行控制的,希望你可以大概知道每個寄存器的作用:

image.png

① R0-R12:通用寄存器,用于數(shù)據(jù)操作;

② R13:棧頂指針,有兩個互斥的指針MSP和PSP,在任一時刻只能使用其中一個;

③ R14:連接寄存器,調(diào)用子程序時存放返回地址;

④ R15:程序計數(shù)器,PC指針指向哪里,CPU就執(zhí)行哪里的代碼;

在RTOS內(nèi)核中,這16個寄存器組的值稱之為 「上下文環(huán)境」 ,即當(dāng)前任務(wù)運(yùn)行時這16個寄存器中的值稱為上文環(huán)境,下一個任務(wù)運(yùn)行時這16個寄存器的值稱為下文環(huán)境, 「上下文切換」 就是指將這16寄存器組的值修改為下一個任務(wù)的值。

1.4. 棧

棧是一種 「只能在一端插入或者刪除元素」 的數(shù)據(jù)結(jié)構(gòu),規(guī)則為: 「先入后出」 (FILO)。

image.png

C語言程序運(yùn)行的時候,棧是非常非常非常重要的,在裸機(jī)程序中,棧頂指針由寄存器R13給出。

棧的作用,一方面是局部變量的存儲,局部變量的定義會被匯編為PUSH 指令,將局部變量中的內(nèi)容壓入棧中,在函數(shù)執(zhí)行完畢之后出棧,該局部變量被銷毀;另一方面是函數(shù)調(diào)用時的參數(shù)傳遞,也會被壓入棧中,在函數(shù)執(zhí)行完畢后出棧。

2. 任務(wù)控制塊長啥樣

任務(wù)控制塊是一個任務(wù)的核心,廣義的講: 「內(nèi)核所有對任務(wù)的操作,其實都是在操作任務(wù)控制塊」 。

任務(wù)控制塊類型k_task_t是一個結(jié)構(gòu)體類型:

typedef struct k_task_st    k_task_t;

當(dāng)定義了一個任務(wù)控制塊時,該結(jié)構(gòu)體變量沒有初始值,所以 「存儲位置在STM32內(nèi)部SRAM中的bss段內(nèi)」 。

任務(wù)控制塊的結(jié)構(gòu)體類型定義如下:

/**  
 * task control block  
 */  
struct k_task_st {  
    k_stack_t          *sp;                     /**< task stack pointer. This lady always comes first, we count on her in port_s.S for context switch. */  
  
    knl_obj_t           knl_obj;                /**< just for verification, test whether current object is really a task. */  
  
    char                name[K_TASK_NAME_MAX];  /**< task name */  
    k_task_entry_t      entry;                  /**< task entry */  
    void               *arg;                    /**< argument for task entry */  
    k_task_state_t      state;                  /**< just state */  
    k_prio_t            prio;                   /**< just priority */  
  
    k_stack_t          *stk_base;               /**< task stack base address */  
    size_t              stk_size;               /**< stack size of the task */  
  
  
  
    k_list_t            stat_list;              /**< list for hooking us to the k_stat_list */  
  
    k_tick_t            tick_expires;           /**< if we are in k_tick_list, how much time will we wait for? */  
  
    k_list_t            tick_list;              /**< list for hooking us to the k_tick_list */  
    k_list_t            pend_list;              /**< when we are ready, our pend_list is in readyqueue; when pend, in a certain pend object's list. */  
      
  
    pend_obj_t         *pending_obj;            /**< if we are pending, which pend object's list we are in? */  
    pend_state_t        pend_state;             /**< why we wakeup from a pend */  
};

此處引用的源碼 「不完整」 ,方便閱讀起見,所有使用宏開關(guān)配置的定義全部省略。

任務(wù)控制塊中的內(nèi)容主要分為三部分:

① 任務(wù)棧棧頂指針sp:接下來會重點(diǎn)講解;

② 任務(wù)的全部信息:任務(wù)名稱、任務(wù)狀態(tài)、任務(wù)優(yōu)先級、任務(wù)入口函數(shù)及參數(shù)、任務(wù)棧地址和大??;

③ 任務(wù)的鏈表:后續(xù)文章中重點(diǎn)講解。

3. 任務(wù)棧

3.1. 任務(wù)棧是什么

任務(wù)棧類型 k_stack_t 是一個 uint8_t 類型:

typedef uint8_t             k_stack_t;

當(dāng)定義了一個任務(wù)棧數(shù)組時:

#define TASK1_STACK_SIZE    512  
k_stack_t task1_stack[TASK1_STACK_SIZE];

本質(zhì)上還是一個uint8_t類型的全局變量數(shù)組,該全局變量數(shù)組沒有初始值,所以 「存儲位置仍在STM32內(nèi)部SRAM中的bss段內(nèi)」 。

在使用該數(shù)組的時候,只通過指針sp訪問,假裝它是一個棧,在使用上和棧的使用方式一模一樣,所以稱之為任務(wù)棧。

3.2. 任務(wù)棧中有什么(作用)

在創(chuàng)建任務(wù)的API中,有這樣一句代碼來初始化任務(wù)棧,并且返回任務(wù)棧的棧頂指針sp:

task- >sp = cpu_task_stk_init((void *)entry, arg, (void *)task_exit, stk_base, stk_size);

查看cpu_task_stk_init函數(shù)的定義,會發(fā)現(xiàn) 「不同的CPU結(jié)構(gòu),該函數(shù)的實現(xiàn)不同」 。

image.png

為什么不同的CPU結(jié)構(gòu),會導(dǎo)致任務(wù)棧的初始化代碼實現(xiàn)不同呢?

不急,讓我們先來看看如何來初始化任務(wù)棧, 「Cortex-M系列芯片的內(nèi)核對應(yīng)的都是ARM v7m架構(gòu)」 ,選取此架構(gòu)中的 cpu_task_stk_init 函數(shù)實現(xiàn)來探索問題的答案。

① 獲取任務(wù)棧棧頂指針的地址并對齊:

cpu_data_t *sp;  
  
sp = (cpu_data_t *)&stk_base[stk_size];  
sp = (cpu_data_t *)((cpu_addr_t)sp & 0xFFFFFFF8);

② PendSV異常發(fā)生時自動保存的寄存器:

/* auto-saved on exception(pendSV) by hardware */  
*--sp = (cpu_data_t)0x01000000u;    /* xPSR     */  
*--sp = (cpu_data_t)entry;          /* entry    */  
*--sp = (cpu_data_t)exit;           /* R14 (LR) */  
*--sp = (cpu_data_t)0x12121212u;    /* R12      */  
*--sp = (cpu_data_t)0x03030303u;    /* R3       */  
*--sp = (cpu_data_t)0x02020202u;    /* R2       */  
*--sp = (cpu_data_t)0x01010101u;    /* R1       */  
*--sp = (cpu_data_t)arg;            /* R0: arg  */

③ 手動保存/加載的寄存器:

*--sp = (cpu_data_t)0x11111111u;    /* R11      */  
*--sp = (cpu_data_t)0x10101010u;    /* R10      */  
*--sp = (cpu_data_t)0x09090909u;    /* R9       */  
*--sp = (cpu_data_t)0x08080808u;    /* R8       */  
*--sp = (cpu_data_t)0x07070707u;    /* R7       */  
*--sp = (cpu_data_t)0x06060606u;    /* R6       */  
*--sp = (cpu_data_t)0x05050505u;    /* R5       */  
*--sp = (cpu_data_t)0x04040404u;    /* R4       */

④ 返回當(dāng)前棧頂指針:

return (k_stack_t *)sp;

初始化后任務(wù)棧中的內(nèi)容如下:

image.png

任務(wù)切換的大致流程是觸發(fā)PendSV異常,在異常處理函數(shù)中使用匯編語言實現(xiàn)任務(wù)切換,也就是 「上下文切換」 ,在接下來的文章中會專門講述任務(wù)切換。

當(dāng)該任務(wù)被調(diào)度執(zhí)行時,CPU會自動將任務(wù)棧中最前面的8個寄存器值加載到CPU寄存器中,完成 「下文環(huán)境切換」 ,此時:

  • 棧頂指針寄存器R13中的值是該任務(wù)的任務(wù)棧的sp指針;
  • 程序計數(shù)器指針PC指向的是該任務(wù)的入口函數(shù)entry;

接下來CPU中的環(huán)境就是該任務(wù)的環(huán)境,該任務(wù)開始運(yùn)行。

因為棧頂指針指向的是該任務(wù)的任務(wù)棧,所以此時若在任務(wù)的入口函數(shù)中傳遞參數(shù),調(diào)用函數(shù),創(chuàng)建局部變量, 「所有數(shù)據(jù)都被壓入到該任務(wù)的任務(wù)棧中」 ,與STM32內(nèi)部的棧空間毫無關(guān)系。

同理,當(dāng)任務(wù)執(zhí)行完畢時(不一定是程序結(jié)束,而是調(diào)度器需要去調(diào)度執(zhí)行別的任務(wù)了),因為棧具有 「后入先出」 的規(guī)則,CPU再將當(dāng)前寄存器組的值壓入到棧中,完成 「上文環(huán)境保存」 ,下次再需要被加載時,這些寄存器組的值將首先出棧。

最后揭曉問題答案,因為 「不同的CPU架構(gòu),CPU寄存器組的數(shù)量、功能都不同」 ,所以需要針對每種CPU架構(gòu)都要有一個實現(xiàn)。

4. 任務(wù)到底應(yīng)該怎么寫

在學(xué)習(xí)RTOS的時候,我們的關(guān)注點(diǎn)都是“如何創(chuàng)建任務(wù)”,將重點(diǎn)放在了創(chuàng)建任務(wù)的API上,而忽略了一些最重要的問題。

重點(diǎn)①: 「任務(wù)入口函數(shù),并不是一個普通的函數(shù)」 。

任務(wù)入口函數(shù),通常它都偽裝成了一個普通函數(shù),不像main函數(shù)那樣鶴立雞群,所以很多時候我們覺得它就是一個普通函數(shù)調(diào)用,實則不然。

「每一個任務(wù)的entry,首先應(yīng)該是一個獨(dú)立的裸機(jī)程序?!?/strong>

為什么這么說?因為多任務(wù)操作系統(tǒng)的機(jī)制是搶占式調(diào)度和時間片輪轉(zhuǎn),無論再怎么牛逼,也無法改變CPU中只有一個CPU的事實,所以無論在任何一個時刻,系統(tǒng)中都只有唯一一個任務(wù)在運(yùn)行。

重點(diǎn)②: 「每寫一行代碼,都要思考任務(wù)棧是否足夠」 。

在任務(wù)入口函數(shù)中創(chuàng)建的局部變量,函數(shù)調(diào)用,函數(shù)傳參,都使用的是該任務(wù)的任務(wù)棧,和STM32內(nèi)部??臻g沒有任何關(guān)系,所以在編寫的時候一定要時刻思考自己指定的任務(wù)棧大小是否足夠,特別是在開辟局部變量數(shù)組的時候,調(diào)用一些庫的API的時候。

而在任務(wù)入口函數(shù)中,如果定義的是static變量,則不會存放到任務(wù)棧中,存放位置在STM32內(nèi)部SRAM中的bss區(qū)域內(nèi)。

除此之外,其余代碼都屬于可執(zhí)行代碼,存放在Flash中Text區(qū)域中的Executable Code段,大可不必太在意。

重點(diǎn)③: 「盡量盡量要主動釋放CPU,切忌浪費(fèi)CPU」 。

在裸機(jī)程序中,如果你動不動喜歡寫個死循環(huán)延時,尚可原諒,但是在RTOS系統(tǒng)中,如果一個任務(wù)在死循環(huán)做無用功,而導(dǎo)致其它任務(wù)得不到調(diào)度執(zhí)行,將是不可饒恕的。

在編寫任務(wù)入口函數(shù)的時候,一定要遵循“不使用,就讓出”的原則,做一個高素質(zhì)的任務(wù),最普遍的做法是使用系統(tǒng)提供的delay函數(shù)來延時。

這樣做有非常多的優(yōu)點(diǎn),一方面是防止系統(tǒng)發(fā)生堵塞,導(dǎo)致其它任務(wù)得不到運(yùn)行;另一方面是使系統(tǒng)中的空閑任務(wù)可以在空閑的時候回收系統(tǒng)內(nèi)存資源,進(jìn)入低功耗模式等騷操作。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5616

    瀏覽量

    130347
  • STM32
    +關(guān)注

    關(guān)注

    2312

    文章

    11187

    瀏覽量

    374421
  • RTOS
    +關(guān)注

    關(guān)注

    25

    文章

    868

    瀏覽量

    123193
  • Cortex-M3
    +關(guān)注

    關(guān)注

    9

    文章

    276

    瀏覽量

    61779
  • SRAM控制器
    +關(guān)注

    關(guān)注

    0

    文章

    11

    瀏覽量

    6034
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    CTO到底應(yīng)不應(yīng)該代碼?聽聽硅谷大神們怎么說

    醫(yī)療社區(qū)丁香園的 CTO 馮大輝離職了,炸出了科技行業(yè)里的個大問題:CTO到底應(yīng)不應(yīng)該代碼?
    發(fā)表于 08-29 10:07 ?5617次閱讀

    電子工程師應(yīng)該修煉的九大內(nèi)功

    ,平均年齡在29歲,美國電子工程師的年齡平均在40歲左右。在這個朝氣蓬勃的年齡,電子工程師如何實現(xiàn)快速個人成長呢?本文作者結(jié)合網(wǎng)上的資源,整理了下就大內(nèi)功修養(yǎng)路徑,和電子工程師們分享。
    發(fā)表于 11-03 10:31 ?3797次閱讀

    RTOS信號量、隊列通信原理

    有深入理解RTOS原理,或閱讀過RTOS源碼的同學(xué)應(yīng)該知道:RTOS實現(xiàn)任務(wù)間通信通常是由系列
    發(fā)表于 08-16 10:07 ?2225次閱讀

    請問編譯純rtos到底是選擇Linux+rtos的sdk編譯only rtos還是直接使用rtos sdk?

    編譯純rtos到底是選擇Linux+rtos的sdk編譯only rtos還是直接使用rtos sdk?
    發(fā)表于 07-11 07:22

    嵌入式軟件工程師的內(nèi)功修煉

    嵌入式軟件的內(nèi)功,21ic家也曾經(jīng)多次強(qiáng)調(diào)。相信大家都看過武俠小說或電視,金老前輩的甚是出名,里面有“天下武功出少林”說,為什么呢?就是因為少林有本《易筋經(jīng)》,“掃地僧”也就是當(dāng)時江湖的大神,學(xué)了
    發(fā)表于 11-03 15:33

    RTOS任務(wù)性能分析實現(xiàn)經(jīng)驗分享

    1、如何利用公式評估RTOS任務(wù)的系統(tǒng)資源占用呢在實踐中,我們應(yīng)該如何利用上述公式評估 RTOS
    發(fā)表于 04-15 18:16

    到底該不該用RTOS?看完你就有答案了

    到底該不該用RTOS,看完你就有答案了
    的頭像 發(fā)表于 02-25 16:17 ?4263次閱讀

    LEDs狀態(tài)燈任務(wù)(線程)設(shè)計 (基于RTOS

    LEDs狀態(tài)燈任務(wù)(線程)設(shè)計(基于RTOS
    的頭像 發(fā)表于 03-12 11:30 ?3287次閱讀

    如何讓RTOS任務(wù)訪問同個UART?

    RTOS任務(wù)編程的時候,同個硬件(比如UART、I2C等)被多個任務(wù)訪問的情況比較多,如果不合理處理,就會導(dǎo)致“混亂”的局面。 處理“混亂”局面的方法比較多,下面基于FreeRT
    的頭像 發(fā)表于 03-12 17:18 ?3221次閱讀

    淺析OS中的線程、進(jìn)程和協(xié)程與RTOS任務(wù)屬于那種

    今天為大家講解講解OS中的線程、進(jìn)程和協(xié)程的這幾個概念,同時起看看RTOS中的任務(wù)到底屬于哪種。
    的頭像 發(fā)表于 04-19 10:06 ?3983次閱讀
    淺析OS中的線程、進(jìn)程和協(xié)程與<b class='flag-5'>RTOS</b><b class='flag-5'>任務(wù)</b>屬于那種

    RTOS任務(wù)的堆棧大小與代碼量有啥關(guān)系嗎?

    最近有小伙伴問了這樣個問題:我有個任務(wù)中的代碼量很多,是不是這個任務(wù)的堆棧需要分配很大才行? 下面就圍繞任務(wù)代碼量,以及堆棧進(jìn)行描述相關(guān)內(nèi)容。 1
    的頭像 發(fā)表于 05-26 09:34 ?2867次閱讀

    分析RTOS實現(xiàn)多任務(wù)調(diào)度的基本原理

    概念,相信大部分初學(xué)者都是懵的。 不太懂這些概念很正常,下子懂了,我倒反而覺得不正常。 1什么是多任務(wù) 這里大部分人應(yīng)該都是從裸機(jī)階段過來的,裸機(jī)系統(tǒng)般也稱之為單
    的頭像 發(fā)表于 07-27 15:59 ?6297次閱讀
    分析<b class='flag-5'>RTOS</b>實現(xiàn)多<b class='flag-5'>任務(wù)</b>調(diào)度的基本原理

    RTOS中的任務(wù)是線程?進(jìn)程?還是協(xié)程?

    今天為大家講解講解OS中的線程、進(jìn)程和協(xié)程的這幾個概念,同時起看看RTOS中的任務(wù)到底屬于哪種。
    的頭像 發(fā)表于 06-04 17:19 ?2446次閱讀
    <b class='flag-5'>RTOS</b>中的<b class='flag-5'>任務(wù)</b>是線程?進(jìn)程?還是協(xié)程?

    使用任務(wù)通知提高RTOS應(yīng)用的效率

    在實時嵌入式系統(tǒng)中,性能和資源效率是決定設(shè)計成敗的關(guān)鍵因素。傳統(tǒng)的實時操作系統(tǒng)(RTOS)提供了如隊列、信號量和事件組機(jī)制,實現(xiàn)任務(wù)之間的同步和通信。FreeRTOS/SAFERTOS還提供種方法可以使這些過程更快、更輕量化,
    的頭像 發(fā)表于 12-27 14:54 ?1387次閱讀

    RTOS 必學(xué)概念:任務(wù)、信號量、隊列次搞懂

    如果你剛接觸RTOS(實時操作系統(tǒng)),很可能會有這樣的困惑:“RTOS和裸機(jī)程序到底有什么區(qū)別?”“任務(wù)是線程嗎?為什么要分任務(wù)?”“信號量
    的頭像 發(fā)表于 11-17 10:53 ?642次閱讀
    <b class='flag-5'>RTOS</b> 必學(xué)概念:<b class='flag-5'>任務(wù)</b>、信號量、隊列<b class='flag-5'>一</b>次搞懂
    正安县| 平顶山市| 五寨县| 文成县| 集安市| 荃湾区| 犍为县| 霍邱县| 舟曲县| 内黄县| 二连浩特市| 石城县| 双江| 义马市| 梅河口市| 晋州市| 原阳县| 兴海县| 淮阳县| 托里县| 邓州市| 民勤县| 桦南县| 陆川县| 微博| 灌阳县| 维西| 调兵山市| 兴义市| 文化| 卫辉市| 修文县| 吴桥县| 灌南县| 河源市| 昭苏县| 永和县| 普兰店市| 英山县| 贡嘎县| 肃南|