Zephys OS nano 內核篇:fiber 服務

Zephyr OS 所有的學習筆記已託管到 Github,CSDN 博客裏的內容只是 Github 裏內容的拷貝,因此鏈接會有錯誤,請諒解。

最新的學習筆記請移步 GitHub:https://github.com/tidyjiang8/zephyr-inside

本文講解 Zephyr 中的 fiber 相關的 API:
- _nano_fiber_ready()
- _fiber_start()
- _fiber_start()
- _nano_fiber_swap()
- fiber_abort()
- fiber_delayed_start()
- fiber_delayed_start_cancel()

_nano_fiber_ready

void _nano_fiber_ready(struct tcs *tcs)
{   
    // 獲取就緒線程鏈表的表頭數據
    struct tcs *pQ = (struct tcs *)&_nanokernel.fiber;

    // 從表頭開始逐漸向後查找鏈表中的線程,直到所查找線程的優先級比
    // 待插入的線程的優先級低(或者相同)爲止
    while (pQ->link && (tcs->prio >= pQ->link->prio)) {
        pQ = pQ->link;
    }

    // 在查找到的線程前插入待插入的線程
    tcs->link = pQ->link;
    pQ->link = tcs;
}

內核大總管 _nanokernel 維護了一張由就緒線程形成的就緒鏈表,這張鏈表中的線程按優先級由高到低的順序排列(數字越低,優先級越高)。_nano_fiber_ready() 的作用就是將線程 tcs 按照其優先級插入到這張鏈表中的對應位置。

嚴格地說,隊列指的是只能在隊尾加入數據,隊首取出數據的一種數據結構。但是爲了與後面的等待隊列進行類比,我們將這個鏈表叫做就緒隊列。

_fiber_start

ano_thread_id_t _fiber_start(char *pStack,
        unsigned stackSize, /* stack size in bytes */
        nano_fiber_entry_t pEntry,
        int parameter1,
        int parameter2,
        unsigned priority,
        unsigned options)
{
    struct tcs *tcs;
    unsigned int imask;

    // 由於_new_thread()將在線程棧的低地址處存儲線程的控制結構,
    // 所以此處直接將 tcs 指向線程棧的低地址處。
    tcs = (struct tcs *) pStack;
    // 新建一個線程
    _new_thread(pStack,
            stackSize,
            NULL,
            (_thread_entry_t)pEntry,
            (void *)parameter1,
            (void *)parameter2,
            (void *)0,
            priority,
            options);

    imask = irq_lock();

    // 將該線程加入到就緒線程鏈表中
    _nano_fiber_ready(tcs);

    if ((_nanokernel.current->flags & TASK) == TASK) {
        // 如果當前正在運行的上下文是 task,則進行上下文切換,然後由
        // 調度器選擇執行優先級最高的線程
        _Swap(imask);
    } else {
        // 如果當前正在運行的上下文是 fiber,不進行上下文切換,
        // 因爲 fiber 服務一旦運行,就必須運行完,它是不能被搶佔的
        irq_unlock(imask);
    }

    return tcs;
}

該函數用於啓動一個新的 fiber 服務,即創建一個新的線程。

irq_lock() 和 irq_unlock() 用於屏蔽中斷和使能中斷,具體信息請參考《Zephyr OS nano 內核篇:中斷的使能和屏蔽》。

_Swap()用於進行上下文切換。當調用_Swap()函數後,調度器會保持當前正在執行的上下文的現場信息,然後調用就緒中優先級最高的線程去執行。具體信息請參考《Zephyr OS nano 內核篇:上下文切換》。

內核還爲該函數 _fiber_start() 提供了兩個別名:

FUNC_ALIAS(_fiber_start, fiber_fiber_start, nano_thread_id_t);
FUNC_ALIAS(_fiber_start, task_fiber_start, nano_thread_id_t);
FUNC_ALIAS(_fiber_start, fiber_start, nano_thread_id_t);

FUNC_ALIAS 是系統預定義的一個宏,用來爲函數取別名,第一個參數是函數本來的名字,第二個參數是函數的別名,第三個參數是函數的返回值。

這樣做的原因是爲了兼容性考慮,以防今後由於某些原語使 fiber_fiber_start() 和 task_fiber_start() 成爲兩個實現方法不同的函數。所以我們在調用這類使用了別名的函數時,應儘量根據使用環境選擇對應的別名函數。

fiber_yield

void fiber_yield(void)
{
    unsigned int imask = irq_lock();

    if ((_nanokernel.fiber != (struct tcs *)NULL) &&
        (_nanokernel.current->prio >= _nanokernel.fiber->prio)) {

        // 將本線程加入到就緒線程鏈表中,此時線程的狀態由執行態轉變爲就緒態
        _nano_fiber_ready(_nanokernel.current);
        _Swap(imask);
    } else {
        irq_unlock(imask);
    }
}

如果當前正在執行的線程由於某種原因想主動使用 CPU 的使用權,可以調用該函數。但是 CPU 的使用權釋放成功需要滿足兩個條件:
- 內核中存在就緒線程
- 該線程的優先級比內核中優先級最高的就緒線程的優先級低(或相等)

釋放完 CPU 後,該線程的狀態由執行態轉變爲了就緒態,並當該線程在就緒鏈表中的優先級最高時會被調度器再次調度並執行。

_nano_fiber_swap

FUNC_NORETURN void _nano_fiber_swap(void)
{
    unsigned int imask;

    imask = irq_lock();
    _Swap(imask);

    // 編譯器不知道 _Swap() 不會反悔,因此會發出一個警告,除非我
    // 們使用下面的宏明確地告訴編譯器
    CODE_UNREACHABLE;
}

_nano_fiber_swap() 也是主動釋放 CPU 的使用權,但是與 fiber_yield() 不同的是,該函數釋放完 CPU 後,不會被加入就緒隊列,因爲該線程將永遠不會被再執行。

fiber_abort

FUNC_NORETURN void fiber_abort(void)
{
    _thread_exit(_nanokernel.current);
    _nano_fiber_swap();
}

將當前線程從線程鏈表 _nanokernel.threads 中刪除,然後釋放 CPU。

fiber_delayed_start

nano_thread_id_t fiber_delayed_start(char *stack,
              unsigned int stack_size_in_bytes,
              nano_fiber_entry_t entry_point, int param1,
              int param2, unsigned int priority,
              unsigned int options, int32_t timeout_in_ticks)
{
    unsigned int key;
    struct tcs *tcs;

    tcs = (struct tcs *)stack;
    _new_thread(stack, stack_size_in_bytes, NULL, (_thread_entry_t)entry_point,
        (void *)param1, (void *)param2, (void *)0, priority, options);

    key = irq_lock();

    _nano_timeout_add(tcs, NULL, timeout_in_ticks);

    irq_unlock(key);
    return tcs;
}

該函數的作用與 _fiber_start() 類似,都會創建一個線程,其不同之處被創建的線程不是立即被加入到就緒線程鏈表中,而是被加入到一個超時鏈表中。當經過 timeout_in_ticks 個系統滴答後,該線程纔會被加入到就緒線程鏈表。

關於超時鏈表,請參考《Zephyr OS nano 內核篇: 超時服務 timeout》

該函數定義了兩個別名:

FUNC_ALIAS(fiber_delayed_start, fiber_fiber_delayed_start, nano_thread_id_t);
FUNC_ALIAS(fiber_delayed_start, task_fiber_delayed_start, nano_thread_id_t);

fiber_delayed_start_cancel

void fiber_delayed_start_cancel(nano_thread_id_t handle)
{
    struct tcs *cancelled_tcs = handle;
    int key = irq_lock();

    // 從超時鏈表中刪除該線程的節點
    _nano_timeout_abort(cancelled_tcs);
    // 從 _nanokernel.threads 的鏈表中刪除該節點
    _thread_exit(cancelled_tcs);

    irq_unlock(key);
}

該函數的作用是取消使用 fiber_delayed_start() 函數創建的線程,即將其從超時鏈表中刪除。

該函數也定義了兩個別名:

FUNC_ALIAS(fiber_delayed_start_cancel, fiber_fiber_delayed_start_cancel, void);
FUNC_ALIAS(fiber_delayed_start_cancel, task_fiber_delayed_start_cancel, void);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章