2017年10月30日 星期一

[ThreadX, 3] thread

Thread State


TCB(Thread control block):
  • context switch用來保存thead狀態
  • 可在記憶體任何地方(user自行分配)
  • read only
  • Debug(tx_api.h)
tx_thread_run_count
Thread's run counter
tx_thread_state
Thread's execution state
TX_READY
TX_COMPLETED
TX_TERMINATED
TX_SUSPENDED
TX_SLEEP
TX_QUEUE_SUSP
TX_SEMAPHORE_SUSP
TX_EVENT_FLAG
TX_BLOCK_MEMORY
TX_BYTE_MEMORY
TX_MUTEX_SUSP

Note:
  1. 沒有executestate當正在執行時或剛createthread ready
    1. 若設了TX_DONT_START則為SUSPEND
  1. task return 狀態為 TX_COMPLETED


Thread Services
Thread service
 Description
tx_thread_create
 Create an application thread

Note: return is TX_THREAD (TCB)
tx_thread_delete
Delete an application thread

只在TX_COMPLETED/TX_TERMINATED狀態下操作
tx_thread_entry_exit_notify
 Notify application upon thread entry and exit
tx_thread_identify
Retrieves pointer to currently executing thread
tx_thread_info_get
Retrieve information about a thread

資訊與TCB相似
tx_thread_performance_info_get
 Get thread performance information
tx_thread_performance_system_info_get
 Get thread system performance information
tx_thread_preemption_change
Change preemption-threshold of application
thread
tx_thread_priority_change
 Change priority of an application thread

會將thread priority thresholddisable
tx_thread_relinquish
Relinquish control to other application threads

cooperative multi-tread(a round-robin scheduling)
tx_thread_reset
 Reset thread to original status
tx_thread_resume
 Resume suspended application thread
tx_thread_sleep
 Suspend current thread for specified time( unit: ticks)
tx_thread_stack_error_notify
Register thread stack error notification callback
tx_thread_suspend
 Suspend an application thread
tx_thread_terminate
 Terminates an application thread
tx_thread_time_slice_change
 Changes time-slice of application thread
tx_thread_wait_abort
 Abort suspension of specified thread

無法恢復被強制supspendthread(tx_thread_suspend)




Tips
  • how to fine tune stack size ?
    • 宣告一足夠大的stack並填上特定值(0xEFEF)
    • create thread and run
    • 完整測試
    • 查看有多少stack沒被用到
  • Reentrant
    • thread其中一個特點是,同一個function可能會有多個thread來使用,此時就需要保證為reeentrant,其中:
      • 不能使用靜態變數(global, static)

[ThreadX, 2] conception and terms

Priority
  • 有靜態與動態,簡單的說就是執行的過程中是否修改priority
  • 0~31: 0最高

Ready & suspend
  • ready: 隨時可以執行(但其他更高優先全的thread占著CPU)
  • suspend: 由於某種原因(等待資原被高優先權thread搶占other OS service)被掛起
  • 相關流程:thread -> put it into suspend list
 -> remove it from suspend list and put it into ready list
 -> remove it from ready list(FIFO) and run it(假設大家優先全都一樣等最久的先執行)

scheduling
  • preemptive: 高優先權thread可以中段並掛起當前正在執行的低優先權thread
  • round-robin: 相同優先權的thread該如何分享CPU有兩種方式:
    • 每個threa都執行Ntick(time slice)
    • 正在執行的thread放棄執行(cooperative multithreading),使其他相同或更高優先權的可拿到CPU並將自己放在ready list(FIFO)


context switch
  • contextthread當前的執行狀態(PC, SP, registers)
  • context switch是指thread交換(包含ISR)時,contextsaverestore

starvation
  • CPU被高優先權的thread占去大多的時間導致低優先權的沒機會執行。

priority inversion
  • Bounded priority inversion
    • 高優先權的process/thread等待進入critical section,該critical section目前由低優先權的process/thread佔用中。
    • 因此只要低優先權的process/thread離開該critical section後高優先權的process/thread便可繼續執行
  • Unbounded priority inversion
    • 高優先權的process/thread等待進入critical section,該critical section目前由低優先權的process/thread佔用中
    • 不幸的是,當低優先權process/thread還在critical section執行的時候,被切換到中優先權的process/thread由於高優先權的process/threadblock,而低優先權的process/thread一定會被中優先權的process/thread搶走執行權。resource 也就一直被拿著但也不處理,最壞的狀況就是之後就只剩中優先權的process/thread被執行
  • 解法:
    • mutex's priority inheritance option.
    • priority threshold to avoid preempted by thread2.




priority inheritance (threadX only)
  • mutex的ㄧ個選項,允許poiority低的thread暫時以等待其釋放資源的高優先權threadpriority執行,來避免priority inversion
Priority inheritance allows a lower-priority thread to temporarily assume the priority of a
higher-priority thread that is waiting for a mutex owned by the lower-priority thread.
This capability helps the application to avoid nondeterministic priority inversion by
eliminating preemption of intermediate thread priorities. The mutex is the only ThreadX
resource that supports priority inheritance.


Preemption-threshold(threadX only)
  • 創建thread時設閥值,當其他thread要強佔時,priority需高於(不包含)此閥值。
  • disable的話則與priority設一樣的值即可。
  • 注意會使threadtime-slice屬性無效。
  • priority有做調整時此屬性會被設disable

semaphore && mutex
  • 比較表:
    • 相對於 mutexsemaphre沒有所有權的概念,也就是說可以由其他的threadput
    • mutex才有優先權繼承的service
  • mutex較健壯如果,對互斥很重是可以選擇此;但如果不是,semaphore稍快。 同步的處理中常會用到,Wait forever選項,但在即時的函式中只能改為no waitISR, Timer

同步的OS servicemutex/semaphore/event/queeu,其有些共同特色:
  • FIFO: 被掛起的thread,當條件滿足時,先被resume的不是priority最高的(除非操作特定API 將最高prioritythread 放到FIFO )而是等最久的;Event是個例外,當TX_OR set時是所有被掛起的thread都檢查一次。
  • delete: 做刪除動作時,所有被suspendthreadresume並返回TX_DELETE

  • OS service suggestion:

Questions:
  • Guess the purpose of following code
sp5kOsMutexGet(&GPSGSVmutex, TX_WAIT_FOREVER);
sp5kOsMutexPut
(&GPSGSVmutex);

Ans:
  1. 框一個critcial section保護resouce(全域變數,  HW resource, Function…)
該區間只允許一個thread 進入。
    2.     兩個放在一起。拿來與其他thread sync(只是個同步點?),其他的Threadput(mutex初始直為0),此Thread才能往下走。

  • while ( ros_semaphore_put(mediaCb.sigDisp) == SUCCESS ) {
ros_semaphore_get(mediaCb.sigDisp, ROS_WAIT_FOREVER);
  • semaphore 會根據掛起的先後順序(FIFO)來決定誰去get到下一個,所以這段code可以用來允許被abort( abort/resume => get/put於其他task())因為PUT完下面那一行不一定會GET

=>可用於暫停功能,例如host呼叫暫停(get semaphore),則此迴圈跑完就會因為拿不到,semaphore而被暫停。


  • Replacing following mutex with critical section makes it better or worse? Why?
sp5kOsMutexGet(&vidurgmut, TX_WAIT_FOREVER);
urgentCbCnt
--;
sp5kOsMutexPut
(&vidurgmut);
Ans: Depend on CPU(single or multi)//雖然此cretical section夠短
       1. MUTEX 需要額外消耗資源如MCB( mutex control block)context switchcritical section(disable interrupt)較低階(設個暫存器而已)_所花的時間與資源相對少很多
       2. 但是~~~假設CPU不只一個,那麼就無法用取消中斷(Enter Critical)的方式來達成,因為若要達成同樣目的,要取消所有CPU的中斷,效能不好(要發disable interrupt給所有cpu, 等所有cpu回覆我已中斷,才能執行),用MUTEX就不會有此問題。

  • ISR(非同步事件)
    • ISR發生時,原本的task就會被搶占(且做contex switch),當ISR完成後,就會察看ready list換誰了。

[ThreadX, 1] entry of OS

_maint//程式入口
->tx_kernel_enter()//Enter thread kenel
->tx_application_define() //起始的tread, memory, other OS service定義
->tx_thread_schedule() //開始執行

//不要放程式

2017年10月27日 星期五

[C]實用巨集範例

除錯:
將描述式給一起印出

case1:
#define DPRINTF(expr) printf("%s=%d\n",#expr, expr)
or
#define DPRINTF(expr) printf(#expr"=%d",expr)

case2:
#define WARN_IF(EXP) \
     do { if (
EXP) \
             fprintf (stderr, "Warning: "
#EXP "\n"); } \
     while (0)

Note:
  1. "#":字串話運算子可將當前內容轉完字串
  1. #expr本質就為一個字串的巨集


case1:
DPRINTF(x); //if x = 0xaa
=>x=0xaa;

case2:
int main() {
    int x = 0;
    WARN_IF(x == 0);
}
=>Warning: x == 0

除錯巨集
#define DBG_PRINTF(fmt, args...)\
printf("[%s,%d]" fmt, __FUNCTION__, __LINE__,##args)
int main()
{
DBG_PRINTF("Hello\n");
}

Note:
  1. \結尾,表示巨集的換行
  2. fmt:為上例的"Hello\n"
  3. 不定變數沒用到參考(巨集與不定變數)
=>[main,7]Hello
除錯巨集的分級審查

#if DBG_LEVEL==0
#define PRINTF(fmt,...)
#else
#define PRINTF(fmt,...) printf(fmt,##__VA_ARGS__)
#endif


包裝:
利用參數來選擇呼叫的函式
#define test(x) test##x
test1(){
printf("%s\n",__FUNCTION__);
}
test2(){
printf("%s\n",__FUNCTION__);
}

main(){
test(1)();
}

Note:
  1. ##: 為連結運算子,CPP會將字元串作連結

=>test2
程式碼封裝(do while巨集定義)

#define HELLO(str) do{\
printf("Hello:%s\n",str);\
}while(0)

Note:
  1. do while封裝的意義在於,可以使巨集像函式一般使用而不會遇到一些狀況的編輯錯誤,如下:
#define HELLO(str) printf("Hello:%s\n",str);

main(){
if(condition)
printf();;//多一個分號會視為if結構結束,
     //else被視為新的程式(沒有if)
else
printf()
}
test.c:9:2: error: ‘else’ without a previous ‘if’


將表格(cmd table)簡單化:

#define COMMAND(name) {#name, name ## _command}
struct command {
    char *name;
    void (*f)(void);
} cmds[] = {
   
COMMAND(quit),
   
COMMAND(help),
};
Note: #name: 為字串 "xxx"

展開=>
 struct command {
     char *name;
     void (*f)(void);
 } cmds[] = {
     {"quit", quit_command},
     {"help", help_command},

 };

功能:

buffer中得到字串的大小

#define _STRING_LEN_GET(expr) sizeof #expr//stringize the expanded value("test.c")
#define STRING_LEN_GET(expr) STRING_LEN_GET(expr) //展開為 main.c

int main()
{
char buf[128] = __FILE__;
printf("%d\n",sizeof(buf));

printf("%d\n",STRING_LEN_GET(buf));
}

=>
128
4
round up/down

#define ROUND_DOWN_TO_DIVISIBLE(num,div) \
( (UINT32)(num) & -(UINT32)(div) )
#define ROUND_UP_TO_DIVISIBLE(num,div) \
ROUND_DOWN_TO_DIVISIBLE( (UINT32)(num) + (div) - 1, div )