日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

FreeRTOS学习记录

發(fā)布時(shí)間:2024/1/1 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FreeRTOS学习记录 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

FreeRTOS學(xué)習(xí)記錄

  • 前言
  • FreeRTOS學(xué)習(xí)記錄
  • 在STM32CubeMX中配置FreeRTOS

前言

本人小白,最近學(xué)習(xí)了FreeRTOS操作系統(tǒng),打算做一點(diǎn)記錄。
學(xué)習(xí)的過(guò)程中雖然做了點(diǎn)練習(xí),不過(guò)都是跟著例程來(lái)的,大部分沒(méi)什么記錄必要,本文更多的是零零散散地記錄我在學(xué)習(xí)過(guò)程中的一些疑惑與個(gè)人見(jiàn)解,可能會(huì)有錯(cuò)誤還請(qǐng)指出。
這只是一個(gè)初學(xué)者的簡(jiǎn)單學(xué)習(xí)記錄,并沒(méi)有全面系統(tǒng)的知識(shí)介紹,學(xué)習(xí)的話還是要看對(duì)應(yīng)的教程啦。

使用到的工具及版本:
FreeRTOS:V9.0.0

STM32CubeMX版本:6.3.0
HAL庫(kù):STM32CubeF4 Firmware Package V1.26.2
MDK-ARM:V5.32.0.0
開(kāi)發(fā)板:野火的霸天虎開(kāi)發(fā)板V2(主控芯片是STM32F407ZGT6)

參考的資料:
野火的《FreeRTOS內(nèi)核實(shí)現(xiàn)與應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》
FreeRTOS入門(mén)手冊(cè)_中文
FreeRTOS官網(wǎng)提供的介紹。

FreeRTOS學(xué)習(xí)記錄

本文對(duì)port部分(移植層面)源碼的探究查看的是FreeRTOSv9.0.0\FreeRTOS\Source\portable\RVDS\ARM_CM4_MPU,這是我所使用的開(kāi)發(fā)工具和硬件決定的。其他工具或者硬件上的實(shí)現(xiàn)可能不一樣。

任務(wù)會(huì)在什么情況下切換?
1、 我們自己編寫(xiě)的程序所引起的任務(wù)切換。比如我們自己調(diào)用taskYIELD(),或者我們調(diào)用的API函數(shù)里面包含了任務(wù)切換(這種時(shí)候往往是當(dāng)前任務(wù)進(jìn)入阻塞/掛起,又或者是有更高優(yōu)先級(jí)的任務(wù)加入了就緒列表,API函數(shù)里面會(huì)在需要的時(shí)候去調(diào)用任務(wù)切換)。
2、 tick中斷里面會(huì)觸發(fā)任務(wù)切換。tick由內(nèi)核的Systick產(chǎn)生,屬于內(nèi)核的定時(shí)器/計(jì)數(shù)器(其設(shè)計(jì)的本意可能就是供操作系統(tǒng)使用的,為操作系統(tǒng)提供時(shí)間度量,我猜的)。
注意: 搶占式調(diào)度上面兩種情況都存在。協(xié)作式調(diào)度不存在第2種情況的切換(tick中斷的切換),協(xié)作式調(diào)度中所有的切換都是“顯式的”。
顯而易見(jiàn)協(xié)作式調(diào)度比搶占式調(diào)度有著更高的CPU效率,畢竟不需要每個(gè)tick中斷就跑去看看是不是要切換任務(wù)。但是它存在著高優(yōu)先級(jí)中斷可能得不到快速響應(yīng)的風(fēng)險(xiǎn),這取決于我們開(kāi)發(fā)者的程序設(shè)計(jì),得看我們是不是在合適的時(shí)機(jī)調(diào)用了任務(wù)切換,如果程序結(jié)構(gòu)復(fù)雜的話我們可能得時(shí)時(shí)刻刻考慮是不是該在什么地方進(jìn)行一次任務(wù)切換,這可太麻煩了,有的時(shí)候甚至不現(xiàn)實(shí)。
通過(guò)FreeRTOSConfig.h里面的configUSE_PREEMPTION(0是協(xié)作式調(diào)度,非0是搶占式調(diào)度)決定調(diào)度類型。
tick中斷源碼與任務(wù)切換源碼
系統(tǒng)tick依賴于Cotex-M4的SysTick硬件中斷資源,任務(wù)切換依賴于PendSV硬件中斷資源(注:下圖的SysTick及之前的中斷是屬于內(nèi)核的,后面的是芯片廠商的)

來(lái)看看SysTick_Handler(systick中斷函數(shù))。

void SysTick_Handler(void) { #if (INCLUDE_xTaskGetSchedulerState == 1 )if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED){#endif /* INCLUDE_xTaskGetSchedulerState */ xPortSysTickHandler();#if (INCLUDE_xTaskGetSchedulerState == 1 )}#endif /* INCLUDE_xTaskGetSchedulerState */ }

如果調(diào)度器此時(shí)壓根還沒(méi)開(kāi)啟,或者調(diào)用了vTaskEndScheduler( void ),那么這里的xPortSysTickHandler()并不會(huì)執(zhí)行,系統(tǒng)節(jié)拍則不會(huì)增加(當(dāng)然前提是宏INCLUDE_xTaskGetSchedulerState定義為1)。調(diào)度器如果掛起,系統(tǒng)時(shí)間依然會(huì)增加。
接下來(lái)看看xPortSysTickHandler()里面是啥。

void xPortSysTickHandler( void ) {/* The SysTick runs at the lowest interrupt priority, so when this interruptexecutes all interrupts must be unmasked. There is therefore no need tosave and then restore the interrupt mask value as its value is alreadyknown - therefore the slightly faster vPortRaiseBASEPRI() function is usedin place of portSET_INTERRUPT_MASK_FROM_ISR(). */vPortRaiseBASEPRI();{/* Increment the RTOS tick. */if( xTaskIncrementTick() != pdFALSE ){/* A context switch is required. Context switching is performed inthe PendSV interrupt. Pend the PendSV interrupt. */portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;}}vPortClearBASEPRIFromISR(); }

首先調(diào)用了vPortRaiseBASEPRI()把中斷屏蔽,它的實(shí)現(xiàn)利用了Cotex-M4內(nèi)核的BASEPRI register(這張圖是從《STM32F4xx-Cortex_-M4內(nèi)核參考手冊(cè)》截的)。

然后調(diào)用的xTaskIncrementTick() 則是實(shí)現(xiàn)了:
1、系統(tǒng)tick加1(無(wú)論調(diào)度器是否掛起)
2、如果tick溢出,把延時(shí)列表和延時(shí)溢出列表交換
3、處理延時(shí)列表的事宜
4、如果是搶占式調(diào)度,會(huì)返回pdTRUE,這樣接下來(lái)就會(huì)把PendSV中斷標(biāo)志位置位,進(jìn)入PendSV中斷服務(wù)完成任務(wù)切換。如果是協(xié)作式調(diào)度則返回pdFALSE,不會(huì)置位PendSV中斷標(biāo)志。

接下來(lái)看看PendSV_Handler(PendSV中斷函數(shù),用于任務(wù)切換)。


有關(guān)R14與R15(PC)寄存器的描述(我在網(wǎng)上找的)

R14稱為子程序鏈接寄存器LR(Link Register),當(dāng)執(zhí)行子程序調(diào)用指令(BL)時(shí),R14可得到R15(程序計(jì)數(shù)器PC)的備份。在每一種運(yùn)行模式下,都可用R14保存子程序的返回地址,當(dāng)用BL或BLX指令調(diào)用子程序時(shí),將PC的當(dāng)前值復(fù)制給R14,執(zhí)行完子程序后,又將R14的值復(fù)制回PC,即可完成子程序的調(diào)用返回。

寄存器R15用作程序計(jì)數(shù)器(PC),在ARM狀態(tài)下,位[1:0]為0,位[31:2]用于保存PC,在Thumb狀態(tài)下,位[0]為0,位[31:1]用于保存PC。由于ARM體系結(jié)構(gòu)采用了多級(jí)流水線技術(shù),對(duì)于ARM指令集而言,PC總是指向當(dāng)前指令的下兩條指令的地址,即PC的值為當(dāng)前指令的地址值加8個(gè)字節(jié)程序狀態(tài)寄存器。

一開(kāi)始我只找了R14的描述,然后覺(jué)得很奇怪,上圖中的程序怎么會(huì)跳到R14(PC的內(nèi)容)所指的地址呢,不應(yīng)該是PC+1嗎(我只了解一點(diǎn)51內(nèi)核,cortex-M內(nèi)核還沒(méi)怎么了解,以后有時(shí)間得看看它的內(nèi)容),所以才查了PC寄存器的內(nèi)容。
閱讀這些匯編代碼需要對(duì)照ARM與Thumb指令集說(shuō)明(除非特殊需要,不然應(yīng)該沒(méi)什么人會(huì)去背這個(gè)吧)。可以在keil提供的“幫助”窗口查閱。


任務(wù)優(yōu)先級(jí)與中斷優(yōu)先級(jí)
中斷優(yōu)先級(jí)用于硬件中斷管理,屬于微處理器內(nèi)核的內(nèi)容。cortex-M4的中斷優(yōu)先級(jí)數(shù)字越大優(yōu)先級(jí)越低。
任務(wù)優(yōu)先級(jí)用于調(diào)度器的任務(wù)管理,屬于操作系統(tǒng)的內(nèi)容。FreeRTOS中任務(wù)優(yōu)先級(jí)數(shù)字越大優(yōu)先級(jí)越高。
vTaskDelay相對(duì)延時(shí)與vTaskDelayUntil絕對(duì)延時(shí)
我試著寫(xiě)了一段代碼來(lái)進(jìn)行說(shuō)明

void Task_1( void* parameter ) {/* 用于保存上次時(shí)間。調(diào)用后自動(dòng)更新 */static portTickType PreviousWakeTime;...PreviousWakeTime = xTaskGetTickCount(); for( ;; ){... /*假設(shè)此處可能切換成了更高優(yōu)先級(jí)的任務(wù)并去執(zhí)行,并假設(shè)執(zhí)行該任務(wù)要花費(fèi)5個(gè)tick才會(huì)把CPU釋放給Task_1*/vTaskDelay(50) or vTaskDelayUntil(&PreviousWakeTime,50) } }

我畫(huà)了個(gè)非常潦草的圖來(lái)說(shuō)明Task_1的運(yùn)行情況(這里假設(shè)Task_1處于運(yùn)行狀態(tài)的時(shí)間非常短,沒(méi)有畫(huà)出來(lái))

可以看到用相對(duì)延時(shí)的話任務(wù)每次被喚醒的時(shí)間間隔可能是55個(gè)tick,也可能是50個(gè)tick,絕對(duì)延時(shí)則會(huì)讓任務(wù)每次進(jìn)入就緒的時(shí)間是確定的。
關(guān)于消息隊(duì)列、信號(hào)量與互斥量
用于進(jìn)程間通信或同步的手段。信號(hào)量與互斥量的創(chuàng)建利用了消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。它們可以實(shí)現(xiàn)任務(wù)間的通信,本質(zhì)上就是函數(shù)利用全局變量來(lái)對(duì)其他函數(shù)產(chǎn)生影響(進(jìn)行通信)。它們可以讓等待它們的任務(wù)進(jìn)入阻塞狀態(tài)。
事件位
相比起消息隊(duì)列,它的結(jié)構(gòu)更加簡(jiǎn)單。不能傳遞值,只傳遞事件標(biāo)志。
任務(wù)通知
相比起上述的時(shí)間和消息隊(duì)列,它更更簡(jiǎn)單,不需要?jiǎng)?chuàng)建該對(duì)象,也就不需要額外花費(fèi)RAM空間,因?yàn)樗窃谌蝿?wù)控制塊(TCB)里定義了uint32_t類型的變量(用來(lái)傳遞值)和uint8_t類型的變量(用來(lái)傳遞狀態(tài))來(lái)實(shí)現(xiàn)的,它的功能函數(shù)的實(shí)現(xiàn)也更加簡(jiǎn)單。(FreeRTOS V8.2.0 才開(kāi)始支持的)
內(nèi)存管理方案
heap_1.c:最簡(jiǎn)單的,只能申請(qǐng)內(nèi)存,不能進(jìn)行內(nèi)存釋放,申請(qǐng)內(nèi)存的時(shí)間是一個(gè)常量。
heap_2.c:可刪除內(nèi)存。采用了一種最佳匹配算法,但是會(huì)產(chǎn)生內(nèi)存碎片。
heap_3.c:簡(jiǎn)單地封裝了標(biāo)準(zhǔn) C 庫(kù)中的 malloc()和 free()函數(shù)。此時(shí)FreeRTOSConfig.h中的configTOTAL_HEAP_SIZE 宏定義不起作用,使用啟動(dòng)文件里劃分的堆空間。
heap_4.c:在heap_2.c的基礎(chǔ)上多了合并相鄰內(nèi)存空間的功能。
heap_5.c:在heap_4.c的基礎(chǔ)上把外擴(kuò)RAM也納入分配空間。
關(guān)于“stdint.readme”文件
在C語(yǔ)言的發(fā)展過(guò)程中會(huì)添加新的標(biāo)準(zhǔn),所以會(huì)有C89、C99、C11標(biāo)準(zhǔn)。不同的編譯器對(duì)標(biāo)準(zhǔn)的支持程度不一樣,有的會(huì)支持新出的標(biāo)準(zhǔn)而有的則不會(huì)。FreeRTOS希望在不同的編譯器下都可以編譯,所以它沒(méi)有用C99及C99之后的內(nèi)容,除了C99引進(jìn)的stdint.h。這個(gè)文件定義了一些整數(shù)類型和宏。
如果編譯器不提供stdint.h的話,就把FreeRTOS/Source/include路徑下的stdint.readme改成stdint.h并放到工程指定的頭文件路徑下吧。
關(guān)于協(xié)程
和任務(wù)(task)相似的東西,但是對(duì)RAM的要求很低,是為存儲(chǔ)非常小的情況設(shè)計(jì)的,但是使用起來(lái)有更多的限制,現(xiàn)在用的很少,FreeRTOS官方雖然沒(méi)有刪除協(xié)程,但并不再打算更新這部分內(nèi)容。
關(guān)于互斥量的優(yōu)先級(jí)繼承機(jī)制
一個(gè)用來(lái)減輕優(yōu)先級(jí)反轉(zhuǎn)危害的措施。當(dāng)更高優(yōu)先級(jí)的任務(wù)(A)要獲取一個(gè)被低優(yōu)先級(jí)任務(wù)(B)占用的互斥量時(shí),A不可避免的要等待B,如果B的優(yōu)先級(jí)臨時(shí)提高到與A一樣,可以避免優(yōu)先級(jí)介于兩者的任務(wù)這時(shí)候摻一腳(假設(shè)有任務(wù)C,優(yōu)先級(jí)順序A>C>B,沒(méi)有優(yōu)先級(jí)繼承的話C可能會(huì)搶占B,導(dǎo)致A又要等B又要等C執(zhí)行)。如果不想出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn),要硬實(shí)時(shí)之類的,應(yīng)該是在一開(kāi)始的設(shè)計(jì)階段就要考慮。

在STM32CubeMX中配置FreeRTOS

原本我是計(jì)劃學(xué)習(xí)μC/OS的,因?yàn)椴閷W(xué)習(xí)路徑的時(shí)候網(wǎng)上都推薦μC/OS,不過(guò)在使用STM32CubeMX的時(shí)候發(fā)現(xiàn)它提供了FreeRTOS中間件并可以快速配置,所以選擇了先學(xué)習(xí)FreeRTOS。
STM32CubeMX中FreeRTOS的配置界面。

CMSIS是什么
CMSIS是ARM和一眾廠商一起決定的 Cortex-M 處理器系列的通用接口,相當(dāng)于大家圍在一起制定了標(biāo)準(zhǔn)并提供了各種外設(shè)函數(shù)、實(shí)時(shí)操作系統(tǒng)和中間設(shè)備等在該標(biāo)準(zhǔn)下的通用接口的實(shí)現(xiàn),使用通用接口的話,應(yīng)用程序可以更簡(jiǎn)單地在不同廠商的Cortex-M處理器之間反復(fù)橫跳(移植更方便),V2表示版本2。
版本選擇不了,STM32CubeMX里面好像只能用新版本的FreeRTOS。
點(diǎn)擊選項(xiàng)可以看見(jiàn)參數(shù)說(shuō)明等。

創(chuàng)建任務(wù)
任務(wù)、隊(duì)列等直接在STM32CubeMX里面就可以創(chuàng)建好,不必在編輯器里面寫(xiě)。
STM32CubeMX默認(rèn)創(chuàng)建了一個(gè)defaultTask,這是一個(gè)啥都不做的任務(wù),每次執(zhí)行就阻塞1個(gè)tick。
我創(chuàng)建了兩個(gè)任務(wù)


關(guān)于優(yōu)先級(jí)
單從配置的名字看不出具體的優(yōu)先級(jí)數(shù)字,只能看出優(yōu)先級(jí)先后。我在cmsis_os2.h里面找到它的定義。

/// Priority values. typedef enum {osPriorityNone = 0, ///< No priority (not initialized).osPriorityIdle = 1, ///< Reserved for Idle thread.osPriorityLow = 8, ///< Priority: lowosPriorityLow1 = 8+1, ///< Priority: low + 1osPriorityLow2 = 8+2, ///< Priority: low + 2osPriorityLow3 = 8+3, ///< Priority: low + 3osPriorityLow4 = 8+4, ///< Priority: low + 4osPriorityLow5 = 8+5, ///< Priority: low + 5osPriorityLow6 = 8+6, ///< Priority: low + 6osPriorityLow7 = 8+7, ///< Priority: low + 7osPriorityBelowNormal = 16, ///< Priority: below normalosPriorityBelowNormal1 = 16+1, ///< Priority: below normal + 1osPriorityBelowNormal2 = 16+2, ///< Priority: below normal + 2osPriorityBelowNormal3 = 16+3, ///< Priority: below normal + 3osPriorityBelowNormal4 = 16+4, ///< Priority: below normal + 4osPriorityBelowNormal5 = 16+5, ///< Priority: below normal + 5osPriorityBelowNormal6 = 16+6, ///< Priority: below normal + 6osPriorityBelowNormal7 = 16+7, ///< Priority: below normal + 7osPriorityNormal = 24, ///< Priority: normalosPriorityNormal1 = 24+1, ///< Priority: normal + 1osPriorityNormal2 = 24+2, ///< Priority: normal + 2osPriorityNormal3 = 24+3, ///< Priority: normal + 3osPriorityNormal4 = 24+4, ///< Priority: normal + 4osPriorityNormal5 = 24+5, ///< Priority: normal + 5osPriorityNormal6 = 24+6, ///< Priority: normal + 6osPriorityNormal7 = 24+7, ///< Priority: normal + 7osPriorityAboveNormal = 32, ///< Priority: above normalosPriorityAboveNormal1 = 32+1, ///< Priority: above normal + 1osPriorityAboveNormal2 = 32+2, ///< Priority: above normal + 2osPriorityAboveNormal3 = 32+3, ///< Priority: above normal + 3osPriorityAboveNormal4 = 32+4, ///< Priority: above normal + 4osPriorityAboveNormal5 = 32+5, ///< Priority: above normal + 5osPriorityAboveNormal6 = 32+6, ///< Priority: above normal + 6osPriorityAboveNormal7 = 32+7, ///< Priority: above normal + 7osPriorityHigh = 40, ///< Priority: highosPriorityHigh1 = 40+1, ///< Priority: high + 1osPriorityHigh2 = 40+2, ///< Priority: high + 2osPriorityHigh3 = 40+3, ///< Priority: high + 3osPriorityHigh4 = 40+4, ///< Priority: high + 4osPriorityHigh5 = 40+5, ///< Priority: high + 5osPriorityHigh6 = 40+6, ///< Priority: high + 6osPriorityHigh7 = 40+7, ///< Priority: high + 7osPriorityRealtime = 48, ///< Priority: realtimeosPriorityRealtime1 = 48+1, ///< Priority: realtime + 1osPriorityRealtime2 = 48+2, ///< Priority: realtime + 2osPriorityRealtime3 = 48+3, ///< Priority: realtime + 3osPriorityRealtime4 = 48+4, ///< Priority: realtime + 4osPriorityRealtime5 = 48+5, ///< Priority: realtime + 5osPriorityRealtime6 = 48+6, ///< Priority: realtime + 6osPriorityRealtime7 = 48+7, ///< Priority: realtime + 7osPriorityISR = 56, ///< Reserved for ISR deferred thread.osPriorityError = -1, ///< System cannot determine priority or illegal priority.osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. } osPriority_t;

優(yōu)先級(jí)個(gè)數(shù)超過(guò)32個(gè),可見(jiàn)沒(méi)有使用硬件優(yōu)化任務(wù)查找(Cortex-M處理器提供了一個(gè)計(jì)算前導(dǎo)零的指令‘CLZ’可以優(yōu)化優(yōu)先級(jí)查找)。

關(guān)于Timebase Source
Timebase Source如果使用SysTick,那么在生成代碼前會(huì)有這樣的警告。

原因是使用HAL庫(kù)的一些函數(shù)(比如延時(shí))會(huì)依賴一個(gè)時(shí)鐘源提供周期的節(jié)拍,這和操作系統(tǒng)的tick(心跳)是一樣的道理,這里的Timebase Source是設(shè)定HA庫(kù)函數(shù)L所依賴的時(shí)鐘源,如果把時(shí)鐘源設(shè)為內(nèi)核的SysTick,相當(dāng)于HAL庫(kù)和操作系統(tǒng)共用一個(gè)“心臟”,如果HAL庫(kù)的某個(gè)功能把“心臟”給停了那么操作系統(tǒng)會(huì)受到影響。
給HAL庫(kù)換一個(gè)時(shí)鐘源就好了,我這里用的TIM1。

順便一提,HAL庫(kù)用來(lái)記錄節(jié)拍數(shù)的全局變量叫uwTick,FreeRTOS用來(lái)記錄節(jié)拍數(shù)的全局變量叫xTickCount。
編寫(xiě)任務(wù)函數(shù)
創(chuàng)建好任務(wù)后產(chǎn)生代碼,接下來(lái)寫(xiě)任務(wù)功能就好了,我這里就是簡(jiǎn)單的點(diǎn)燈,把freetros.c里的任務(wù)函數(shù)補(bǔ)充好。

/* USER CODE BEGIN Header_Task_LED_Red */ /*** @brief Function implementing the TASK_LED_RED thread.* @param argument: Not used* @retval None*/ /* USER CODE END Header_Task_LED_Red */ void Task_LED_Red(void *argument) {/* USER CODE BEGIN Task_LED_Red *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED_RED_GPIO_Port,LED_RED_Pin);osDelay(3000);}/* USER CODE END Task_LED_Red */ }/* USER CODE BEGIN Header_Task_LED_Green */ /** * @brief Function implementing the TASK_LED_GREEN thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_Task_LED_Green */ void Task_LED_Green(void *argument) {/* USER CODE BEGIN Task_LED_Green *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin);osDelay(1000);}/* USER CODE END Task_LED_Green */ }

現(xiàn)象
燈光顏色周期性地變化。

總結(jié)

以上是生活随笔為你收集整理的FreeRTOS学习记录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。