RTT筆記-線程

繼續(xù)按照程序流程前進蚂维,前面提到的時鐘初始化癌刽,堆初始化都還未跳出板級初始化rt_hw_board_init函數(shù)

然后看看rt_hw_board_init函數(shù)中剩下的代碼

    /* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
    rt_hw_pin_init(); 
#endif

    /* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
    rt_hw_usart_init();
#endif

    /* Set the shell console output device */
#ifdef RT_USING_CONSOLE
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

    /* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

也就是分別對引腳、串口步绸、shell進行了初始化掺逼,最后rt_components_board_init是對執(zhí)行添加進自啟動的函數(shù),自啟動相關(guān)前篇文章描述瓤介,這里也就不談了吕喘。
這樣又回到了componts.c的rtthread_startup函數(shù)中,板級初始化之后需要執(zhí)行三個函數(shù)

    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

其中rt_show_version是打印一些系統(tǒng)信息刑桑,rt_system_timer_init則是基于系統(tǒng)時鐘的定時器初始化氯质。最后就是操作系統(tǒng)的重頭線程調(diào)度初始化了rt_system_scheduler_init,但這里先不談?wù){(diào)度器祠斧,而是看看本文主題--線程闻察,調(diào)度器相關(guān)下篇再分析。


先從線程結(jié)構(gòu)體入手琢锋,便能了解到每個線程所包含的信息辕漂,該定義在文件rtdef.h中

/**
 * Thread structure
 */
struct rt_thread
{
    /* rt object */
    char        name[RT_NAME_MAX];                      /**< the name of thread */
    rt_uint8_t  type;                                   /**< type of object 對象類型*/
    rt_uint8_t  flags;                                  /**< thread's flags */

#ifdef RT_USING_MODULE
    void       *module_id;                              /**< id of application module */
#endif

    rt_list_t   list;                                   /**< the object list */
    rt_list_t   tlist;                                  /**< the thread list */

    /* stack point and entry */
    void       *sp;                                     /**< stack point */
    void       *entry;                                  /**< entry */
    void       *parameter;                              /**< parameter */
    void       *stack_addr;                             /**< stack address */
    rt_uint32_t stack_size;                             /**< stack size */

    /* error code */
    rt_err_t    error;                                  /**< error code */

    rt_uint8_t  stat;                                   /**< thread status */

#ifdef RT_USING_SMP
    rt_uint8_t  bind_cpu;                               /**< thread is bind to cpu */
    rt_uint8_t  oncpu;                                  /**< process on cpu` */

    rt_uint16_t scheduler_lock_nest;                    /**< scheduler lock count */
    rt_uint16_t cpus_lock_nest;                         /**< cpus lock count */
#endif /*RT_USING_SMP*/

    /* priority */
    rt_uint8_t  current_priority;                       /**< current priority 當(dāng)前優(yōu)先級*/
    rt_uint8_t  init_priority;                          /**< initialized 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)
    /* thread event */
    rt_uint32_t event_set; 
    rt_uint8_t  event_info;
#endif

#if defined(RT_USING_SIGNALS)
    rt_sigset_t     sig_pending;                        /**< the pending signals 等待信號*/
    rt_sigset_t     sig_mask;                           /**< the mask bits of signal 掩碼*/

#ifndef RT_USING_SMP
    void            *sig_ret;                           /**< the return stack pointer from signal */
#endif
    rt_sighandler_t *sig_vectors;                       /**< vectors of signal handler */
    void            *si_list;                           /**< the signal infor list */
#endif

    rt_ubase_t  init_tick;                              /**< thread's initialized tick能占用的tick */
    rt_ubase_t  remaining_tick;                         /**< remaining tick 剩余可運行的tick*/

    struct rt_timer thread_timer;                       /**< built-in thread timer */

    void (*cleanup)(struct rt_thread *tid);             /**< cleanup function when thread exit */

    /* light weight process if present */
#ifdef RT_USING_LWP
    void        *lwp;
#endif

    rt_uint32_t user_data;                             /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;

如果看了內(nèi)核對象管理那篇,前幾個成員看著肯定熟悉吧吴超,其實就和內(nèi)核對象結(jié)構(gòu)體struct rt_object成員是一樣钉嘹,最后多了一個線程鏈表rt_list_t tlist。里面其他成員單獨拿出來是沒啥說的鲸阻,在具體使用時就清楚了跋涣。

線程相關(guān)函數(shù)分析

靜態(tài)創(chuàng)建線程

rt_err_t rt_thread_init(struct rt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                        rt_uint32_t       tick)
{
    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(stack_start != RT_NULL);

    /* init thread object */
    rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);//將新創(chuàng)建的線程加入到管理器中

      //實際的初始化函數(shù)
    return _rt_thread_init(thread,
                           name,
                           entry,
                           parameter,
                           stack_start,
                           stack_size,
                           priority,
                           tick);
}
RTM_EXPORT(rt_thread_init);

英文單詞能直接表達其意思就不打備注了缨睡,其中RTM_EXPORT宏定義是將rt_thread_init函數(shù)公開給所以內(nèi)核代碼∑统保可以參考博文
接下里再看看實際的線程初始化函數(shù)_rt_thread_init宏蛉,其實線程初始化就是在給線程結(jié)構(gòu)體參數(shù)賦值,配合線程結(jié)構(gòu)體的注釋看就好了

static rt_err_t _rt_thread_init(struct rt_thread *thread,
                                const char       *name,
                                void (*entry)(void *parameter),
                                void             *parameter,
                                void             *stack_start,
                                rt_uint32_t       stack_size,
                                rt_uint8_t        priority,
                                rt_uint32_t       tick)
{
    /* init thread list */
    rt_list_init(&(thread->tlist));//清空鏈表

    thread->entry = (void *)entry;
    thread->parameter = parameter;

    /* stack init */
    thread->stack_addr = stack_start;
    thread->stack_size = stack_size;

    /* init thread stack 將被分配的內(nèi)存全部用#符號填充*/
    rt_memset(thread->stack_addr, '#', thread->stack_size);
      //給堆棧指針賦值
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (void *)((char *)thread->stack_addr),
                                          (void *)rt_thread_exit);
#else
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (void *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
                                          (void *)rt_thread_exit);
#endif

    /* priority init */
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;
    thread->current_priority = priority;

    thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number = 0;
    thread->high_mask = 0;
#endif

    /* tick init */
    thread->init_tick      = tick;//用于保存可用時間片大小
    thread->remaining_tick = tick;//用于保存還市灾茫可用時間片大小

    /* error and flags */
    thread->error = RT_EOK;
    thread->stat  = RT_THREAD_INIT;

#ifdef RT_USING_SMP//多核
    /* not bind on any cpu */
    thread->bind_cpu = RT_CPUS_NR;
    thread->oncpu = RT_CPU_DETACHED;

    /* lock init */
    thread->scheduler_lock_nest = 0;
    thread->cpus_lock_nest = 0;
#endif /*RT_USING_SMP*/

    /* initialize cleanup function and user data */
    thread->cleanup   = 0;//回調(diào)函數(shù)
    thread->user_data = 0;

    /* init thread timer */
    rt_timer_init(&(thread->thread_timer),
                  thread->name,
                  rt_thread_timeout,
                  thread,
                  0,
                  RT_TIMER_FLAG_ONE_SHOT);

    /* initialize signal */
#ifdef RT_USING_SIGNALS
    thread->sig_mask    = 0x00;
    thread->sig_pending = 0x00;

    thread->sig_ret     = RT_NULL;
    thread->sig_vectors = RT_NULL;
    thread->si_list     = RT_NULL;
#endif

#ifdef RT_USING_LWP
    thread->lwp = RT_NULL;
#endif

    RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));

    return RT_EOK;
}

其中每個線程都建立了一個定時器拾并,超時函數(shù)均指向了rt_thread_timeout,他實際上就是將對于的線程踢出掛起鏈表鹏浅,加入到就緒鏈表嗅义,并且手動執(zhí)行調(diào)度器切換,在看調(diào)度器部分的時候應(yīng)該會配合則這個函數(shù)說明隐砸,這里就不多言了之碗。

移除進程函數(shù)

該函數(shù)與靜態(tài)創(chuàng)建線程配對,其實靜態(tài)創(chuàng)建于動態(tài)創(chuàng)建區(qū)域就在于是在運行之前就分配好了內(nèi)存季希,還是在運行中使用malloc來分配褪那,其他部分都是一樣的,這里就不再分析動態(tài)創(chuàng)建刪除等相關(guān)的函數(shù)了式塌。

rt_err_t rt_thread_detach(rt_thread_t thread)
{
    rt_base_t lock;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
    RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread));

    if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) //線程被創(chuàng)建但未啟動則處于INIT狀態(tài)
    {
        /* remove from schedule */
        rt_schedule_remove_thread(thread);
    }

    /* release thread timer */
    rt_timer_detach(&(thread->thread_timer)); //刪掉線程匹配的定時器

    /* change stat */
    thread->stat = RT_THREAD_CLOSE;

    /* detach object */
    rt_object_detach((rt_object_t)thread); //將線程從管理器中移除

    if (thread->cleanup != RT_NULL)  //如果有綁定清除回調(diào)
    {
        /* disable interrupt */
        lock = rt_hw_interrupt_disable();

        /* insert to defunct thread list */
        rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));

        /* enable interrupt */
        rt_hw_interrupt_enable(lock);
    }

    return RT_EOK;
}
RTM_EXPORT(rt_thread_detach);

啟動線程

rt_thread_resume函數(shù)是將掛起線程切換成就緒狀態(tài)并且放入線程鏈表中去博敬,在線程啟動函數(shù)rt_thread_startup中被調(diào)用,所以先拿出來說說峰尝。
其實線程狀態(tài)實際就四種偏窝,初始、就緒武学、掛起祭往、關(guān)閉。初始就是線程剛剛被創(chuàng)建時候的狀態(tài)火窒,就緒表示該線程已經(jīng)準(zhǔn)備好被執(zhí)行硼补,就等調(diào)度器亂轉(zhuǎn)到它了,掛起表示該線程在獲得某種條件前不被執(zhí)行熏矿。關(guān)閉和初始對應(yīng)括勺,就是線程被刪除。

rt_err_t rt_thread_resume(rt_thread_t thread)
{
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume:  %s\n", thread->name));

      //當(dāng)線程狀態(tài)不是SUSPEND時曲掰,錯誤退出
    if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND)
    {
        RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
                                       thread->stat));

        return -RT_ERROR;
    }

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    /* remove from suspend list */
        //這里是將該線程鏈表指針清空
    rt_list_remove(&(thread->tlist));

    rt_timer_stop(&thread->thread_timer);

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    /* insert to schedule ready list */
        //這里是將狀態(tài)切換成READY,并且插入到線程鏈表中去
    rt_schedule_insert_thread(thread);

    RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));
    return RT_EOK;
}
RTM_EXPORT(rt_thread_resume);
rt_err_t rt_thread_startup(rt_thread_t thread)
{
    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    /* set current priority to init priority */
    thread->current_priority = thread->init_priority;

    /* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number      = thread->current_priority >> 3;            /* 5bit */
    thread->number_mask = 1L << thread->number;
    thread->high_mask   = 1L << (thread->current_priority & 0x07);  /* 3bit */
#else
    thread->number_mask = 1L << thread->current_priority;
#endif

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
                                   thread->name, thread->init_priority));
    /* change thread stat */
    thread->stat = RT_THREAD_SUSPEND; //狀態(tài)由INIT 變成了SUSPEND
    /* then resume it */
    rt_thread_resume(thread);//切換成就緒態(tài)奈辰,加入線程鏈表
    if (rt_thread_self() != RT_NULL)
    {
        /* do a scheduling */
        rt_schedule();//該函數(shù)是手動切換一次調(diào)度器輪轉(zhuǎn)栏妖,選擇一個最高優(yōu)先級的線程執(zhí)行
    }

    return RT_EOK;
}
RTM_EXPORT(rt_thread_startup);

線程掛起

與函數(shù)rt_thread_resume相對于的是rt_thread_suspend,前者是將掛起轉(zhuǎn)為就緒奖恰,后者是將就緒轉(zhuǎn)為掛起吊趾。

rt_err_t rt_thread_suspend(rt_thread_t thread)
{
    register rt_base_t stat;
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend:  %s\n", thread->name));

    stat = thread->stat & RT_THREAD_STAT_MASK;
    if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING))
    {
        RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",
                                       thread->stat));
        return -RT_ERROR;
    }

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();
    if (stat == RT_THREAD_RUNNING)
    {
        /* not suspend running status thread on other core */
             //當(dāng)執(zhí)行掛起正在運行的程序則會報錯宛裕,正在運行其實意思就是自己,也就是自己不能掛起自己
        RT_ASSERT(thread == rt_thread_self());
    }

    /* change thread stat */
        //踢出鏈表论泛,狀態(tài)切換為suspend
    rt_schedule_remove_thread(thread);
    thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);

    /* stop thread timer anyway */
    rt_timer_stop(&(thread->thread_timer));

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));
    return RT_EOK;
}
RTM_EXPORT(rt_thread_suspend);

rt_thread_sleep函數(shù)實際上是基于rt_thread_suspend和定時器做了一個功能性封裝揩尸,先將對于線程掛起,然后開啟定時器屁奏,由于定時器超時函數(shù)被設(shè)定成了將對于線程加入就緒鏈表中去岩榆,就達到了類似讓線程掛起特定時間的目的。

rt_err_t rt_thread_sleep(rt_tick_t tick)
{
    register rt_base_t temp;
    struct rt_thread *thread;

    /* set to current thread */
    thread = rt_thread_self();
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    /* suspend thread */
    rt_thread_suspend(thread);

    /* reset the timeout of thread timer and start it */
      //使用每個線程配備的定時器來設(shè)定掛起時間坟瓢,超時將被切換回就緒狀態(tài)
    rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
    rt_timer_start(&(thread->thread_timer));

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    rt_schedule();

    /* clear error number of this thread to RT_EOK */
    if (thread->error == -RT_ETIMEOUT)
        thread->error = RT_EOK;

    return RT_EOK;
}

其他函數(shù)便沒什么說的了勇边,接下來便是調(diào)度器部分。這里補充說明一下關(guān)于線程棧初始化函數(shù)rt_hw_stack_init折联,它實際上是根據(jù)不同內(nèi)核進行的棧信息填充粒褒,我們分配給線程的棧在這里被切成了兩部分,第一部分是CPU寄存器诚镰,主要是用來保存上下文奕坟,第二部分才是線程可使用的棧空間清笨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月杉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子函筋,更是在濱河造成了極大的恐慌沙合,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跌帐,死亡現(xiàn)場離奇詭異首懈,居然都是意外死亡,警方通過查閱死者的電腦和手機谨敛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門究履,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脸狸,你說我怎么就攤上這事最仑。” “怎么了炊甲?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵泥彤,是天一觀的道長。 經(jīng)常有香客問我卿啡,道長吟吝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任颈娜,我火速辦了婚禮剑逃,結(jié)果婚禮上浙宜,老公的妹妹穿的比我還像新娘。我一直安慰自己蛹磺,他們只是感情好粟瞬,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著萤捆,像睡著了一般裙品。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鳖轰,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天清酥,我揣著相機與錄音,去河邊找鬼蕴侣。 笑死焰轻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昆雀。 我是一名探鬼主播辱志,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼狞膘!你這毒婦竟也來了揩懒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挽封,失蹤者是張志新(化名)和其女友劉穎已球,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辅愿,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡智亮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了点待。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阔蛉。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖癞埠,靈堂內(nèi)的尸體忽然破棺而出状原,到底是詐尸還是另有隱情,我是刑警寧澤苗踪,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布颠区,位于F島的核電站,受9級特大地震影響通铲,放射性物質(zhì)發(fā)生泄漏瓦呼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望央串。 院中可真熱鬧,春花似錦碗啄、人聲如沸质和。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饲宿。三九已至,卻和暖如春胆描,著一層夾襖步出監(jiān)牢的瞬間瘫想,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工昌讲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留国夜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓短绸,卻偏偏與公主長得像车吹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醋闭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容