久久久国产精品视频袁燕,99re久久精品国产,亚洲欧美日韩国产综合v,天天躁夜夜躁狠狠久久,激情五月婷婷激情五月婷婷

  • 回復
  • 收藏
  • 點贊
  • 分享
  • 發(fā)新帖

【RT-thread學習記】:Thread線程機制及應用

下面開講一下rtt(rtthread的簡稱,全稱打著實在太長了)的干貨,

線程是rtt的最基本的單位,管理的核心,像是信號量,事件,消息,郵箱,還有內存管理其它都是以線程為單位展開的,你可以類比在裸奔年代的函數,函數的出現就是為了以其為單位對于軟件需要實現的功能而存在的,其實線程的本質也是就是函數,但是她加入很多你以前可能沒考慮過的因素,因為實時性的要求:

比如,你在裸奔時代如何實現搶占一個函數的處理,最終返回該函數呢,答案很簡單:中斷嘛,那再進一步,如何搶斷中斷的任務呢,答案也可以脫口而出:嵌套中斷嘛,那我就剩下最后一個問題,那你能把所有函數都放在中斷中,并按照嵌套中斷管理排好順序讓他們執(zhí)行嗎?

我想針對這個問題還是有些困難的,因為中斷機制有其特殊性,在于她的中斷方式,還有中斷嵌套的管理問題,你可以無限制進行嵌套嗎?這肯定也是不行,就算是處理器允許,你想想你得要個多大棧才進行壓棧處理啊。太不現實。

但是線程的出現就是為了解決這個實時性的問題,她可以實現函數級的搶占,不存在嵌套的問題。因為每個線程都有自己的棧,所以你也不要擔心嵌套問題帶來的棧溢出。

如何理解一個線程的內部運行機制:其實你可以考慮,假如合成你如何實現函數級的搶占功能。等你實現了也就理解了。


全部回復(6)
正序查看
倒序查看
2018-10-31 12:45
mark          
0
回復
2018-11-11 16:33

接下來看一下一個線程應該擁有哪些組成部分,才能實現線程的功能,首先內核負責線程的切換管理,但是這不意味著你定義函數就OK了,但是線程的基本功能還是要靠一個線程入口函數的函數來實現,基本功能都在這里面寫,和你之前寫的功能函數有相同又有一些不同:

/*線程入口*/
static void thread_entry(void* parameter)
{
rt_uint32_t count = 0;
rt_uint32_t no = (rt_uint32_t) parameter; /*獲得線程的入口參數*/

while(1)
{
/*打印線程計數值輸出*/
rt_kprintf("thread%d count: %d\n",no,count++);

/*休眠10個os tick*/
rt_thread_delay(10);
}
}

賦值粘貼的時候因為不帶格式,所以看起來有點詭異,不管了,先來看的本體,是一個函數無疑 static 代表是個靜態(tài)函數,其它文件中不能直接調用,void返回值類型為空,void*類型的參數指針,全地圖兼容參數類型,最大的亮點在于體內有個while(1)看著是不是有點神奇,這一下還能執(zhí)行別的東西了嗎?實際是可以的,假如在沒有自身和主動掛起和搶占的時候,她是一直執(zhí)行的,但是她有個rt_thread_delay(10)會讓她主動讓出處理器的控制權。


0
回復
2018-11-11 16:51
@程序小白
接下來看一下一個線程應該擁有哪些組成部分,才能實現線程的功能,首先內核負責線程的切換管理,但是這不意味著你定義函數就OK了,但是線程的基本功能還是要靠一個線程入口函數的函數來實現,基本功能都在這里面寫,和你之前寫的功能函數有相同又有一些不同:/*線程入口*/staticvoidthread_entry(void*parameter){rt_uint32_tcount=0;rt_uint32_tno=(rt_uint32_t)parameter;/*獲得線程的入口參數*/while(1){/*打印線程計數值輸出*/rt_kprintf("thread%dcount:%d\n",no,count++);/*休眠10個ostick*/rt_thread_delay(10);}}賦值粘貼的時候因為不帶格式,所以看起來有點詭異,不管了,先來看的本體,是一個函數無疑static代表是個靜態(tài)函數,其它文件中不能直接調用,void返回值類型為空,void*類型的參數指針,全地圖兼容參數類型,最大的亮點在于體內有個while(1)看著是不是有點神奇,這一下還能執(zhí)行別的東西了嗎?實際是可以的,假如在沒有自身和主動掛起和搶占的時候,她是一直執(zhí)行的,但是她有個rt_thread_delay(10)會讓她主動讓出處理器的控制權。

接下來來討論一下有關于棧的問題,在裸奔時代,當中斷觸發(fā)時,當前運行的函數被打斷,函數的執(zhí)行的相關環(huán)境參數需要保存在棧中,等中斷執(zhí)行完畢后,再將棧中的保存的參數寫入CPU寄存器中,恢復函數執(zhí)行環(huán)境,這個棧叫做系統(tǒng)棧,定義的具體位置如下:

你可以手動調整,200不夠就用400,但是你還是要在裸編程時候注意,這個函數嵌套的問題,過深的嵌套依然會導致你的棧溢出,因為棧不可能無限的大,所以需要你精簡一些不必要的函數,犧牲理解性,緩解棧的壓力,當你引入RTT后,你就需要手動在定義一個叫做任務棧的玩意兒,她是用任務被搶占和切換時候存儲線程入口函數的儲存,這樣對于線程搶占和切換你就不用擔心棧溢出了,因為一個線程一個棧,雖然物理開銷多了,但是他可以讓你走的更遠。

如下:定義,可手動更改大小

#define THREAD_STACK_SIZE       512     //線程棧的大小 512

static rt_uint8_t thread1_stack[THREAD_STACK_SIZE];

0
回復
2018-11-11 16:59
@程序小白
接下來來討論一下有關于棧的問題,在裸奔時代,當中斷觸發(fā)時,當前運行的函數被打斷,函數的執(zhí)行的相關環(huán)境參數需要保存在棧中,等中斷執(zhí)行完畢后,再將棧中的保存的參數寫入CPU寄存器中,恢復函數執(zhí)行環(huán)境,這個棧叫做系統(tǒng)棧,定義的具體位置如下:[圖片]你可以手動調整,200不夠就用400,但是你還是要在裸編程時候注意,這個函數嵌套的問題,過深的嵌套依然會導致你的棧溢出,因為棧不可能無限的大,所以需要你精簡一些不必要的函數,犧牲理解性,緩解棧的壓力,當你引入RTT后,你就需要手動在定義一個叫做任務棧的玩意兒,她是用任務被搶占和切換時候存儲線程入口函數的儲存,這樣對于線程搶占和切換你就不用擔心棧溢出了,因為一個線程一個棧,雖然物理開銷多了,但是他可以讓你走的更遠。如下:定義,可手動更改大小#defineTHREAD_STACK_SIZE    512   //線程棧的大小512staticrt_uint8_tthread1_stack[THREAD_STACK_SIZE];

有了線程棧和線程入口函數,還需要線程控制塊的輔佐,加上rtt的內核部分就可以實現線程的搶占和切換了,下面我們看看線程控制塊的內部具體構造:其實相對比較復雜,主要是為了輔佐rtt的任務調度完成對于線程的控制,所以內部的所有信息看不懂也不要緊,你知道當你定義一個線程時必須要定義一個線程控制塊就足夠了:

/*
*線程控制塊
*/
struct rt_thread
{
/*RT-Thread根對象定義*/
char name[RT_NAME_MAX];  /*對象的名稱*/
rt_uint8_t type;         /*對象的類型*/
rt_uint8_t flags;        /*對象的參數*/
#ifdef RT_USING_MODULE
void *module_id;         /*線程所在的模塊ID*/
#endif
rt_list_t list;          /*對象鏈表*/

rt_list_t tlist;         /*線程鏈表*/

/*棧指針及入口*/
void* sp;                /*線程的棧指針*/
void* entry;             /*線程入口*/
void* parameter;         /*線程入口參數*/
void* stack_addr;        /*線程棧地址*/
rt_uint16_t stack_size;  /*線程棧大小*/

rt_err_t error;          /*線程錯誤號*/

rt_uint8_t stat;         /*線程狀態(tài)*/

/*優(yōu)先級相關域*/
rt_uint8_t current_priority;  /*當前優(yōu)先級*/
rt_uint8_t init_priority;     /*初試線程優(yōu)先級*/

#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;

#if defined(RT_USING_EVENT)
/*時間相關域*/
rt_uint32_t event_set;
rt_uint8_t  event_info;
#endif

rt_ubase_t init_tick;       /*線程初始tick*/
rt_ubase_t remaining_tick;    /*線程當次運行剩余tick*/

  struct rt_timer thread_timer; /*線程定時器*/
  
  /*當線程退出時,需要執(zhí)行的清理函數*/
  void(*cleanup)(struct rt_thread *tid);
  rt_uint32_t user_data;        /*用戶數據*/

};

0
回復
2018-11-11 17:14
@程序小白
接下來來討論一下有關于棧的問題,在裸奔時代,當中斷觸發(fā)時,當前運行的函數被打斷,函數的執(zhí)行的相關環(huán)境參數需要保存在棧中,等中斷執(zhí)行完畢后,再將棧中的保存的參數寫入CPU寄存器中,恢復函數執(zhí)行環(huán)境,這個棧叫做系統(tǒng)棧,定義的具體位置如下:[圖片]你可以手動調整,200不夠就用400,但是你還是要在裸編程時候注意,這個函數嵌套的問題,過深的嵌套依然會導致你的棧溢出,因為棧不可能無限的大,所以需要你精簡一些不必要的函數,犧牲理解性,緩解棧的壓力,當你引入RTT后,你就需要手動在定義一個叫做任務棧的玩意兒,她是用任務被搶占和切換時候存儲線程入口函數的儲存,這樣對于線程搶占和切換你就不用擔心棧溢出了,因為一個線程一個棧,雖然物理開銷多了,但是他可以讓你走的更遠。如下:定義,可手動更改大小#defineTHREAD_STACK_SIZE    512   //線程棧的大小512staticrt_uint8_tthread1_stack[THREAD_STACK_SIZE];

總結一下,線程三大要素:線程控制塊、線程棧、線程入口,當你需要使用線程時候,這仨部分在定義的時候是必須的,但是當你僅僅定義了這三個部分,還不能讓線程正常的工作,因為線程的真正的調度工作是在RTT的內核中實現的,內核的實現說實話我也是只知一不知其二,展開講以我的水準真的講不明白,前期只考慮應用,所以干脆不講了,但是你知道她是主導地位就可以了。

你定義了線程的三大要素,只是完成了線程的實體,要賦予其靈魂,讓其真的開始工作,你還需要把它與rtt關聯(lián)的在一起,你不需要rtt的內核是如何工作的,rtt給你開放了API函數作為接口,你可以把你定義的線程與RTT建立聯(lián)系,就可以了。接下來將RTT有關線程的API。

0
回復
2018-12-25 17:46
@程序小白
總結一下,線程三大要素:線程控制塊、線程棧、線程入口,當你需要使用線程時候,這仨部分在定義的時候是必須的,但是當你僅僅定義了這三個部分,還不能讓線程正常的工作,因為線程的真正的調度工作是在RTT的內核中實現的,內核的實現說實話我也是只知一不知其二,展開講以我的水準真的講不明白,前期只考慮應用,所以干脆不講了,但是你知道她是主導地位就可以了。你定義了線程的三大要素,只是完成了線程的實體,要賦予其靈魂,讓其真的開始工作,你還需要把它與rtt關聯(lián)的在一起,你不需要rtt的內核是如何工作的,rtt給你開放了API函數作為接口,你可以把你定義的線程與RTT建立聯(lián)系,就可以了。接下來將RTT有關線程的API。
RT-thread學習系列更多精彩內容】PS:點擊可直接跳轉閱讀

               本帖內容】RT-thread學習之Thread線程機制及應用

                RT-thread學習之一直走下去

                RT-thread學習之RTT 的與眾不同
                RT-thread學習之object對象管理機制
                RT-thread學習之線程間的同步
                RT-thread學習之設備層框架設計淺析
0
回復
發(fā)