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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

baddy:核心函数入口

發(fā)布時間:2023/12/18 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 baddy:核心函数入口 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇<<baddy:初始化內(nèi)存域>>分析了內(nèi)存域的初始化過程,及擴展內(nèi)容cpu熱插拔函數(shù)的注冊及熱插拔線程的作用等等。UMA下只有一個pglist_data對象也就是對應一個內(nèi)存域,而NUMA下最多擁有5個內(nèi)存域(zone), ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE等等,內(nèi)存域類型根據(jù)具體硬件來決定。

baddy系統(tǒng)的入口(核心)函數(shù)為__alloc_pages,經(jīng)典調(diào)用可以追溯到slub分配器的kmalloc函數(shù),參考<<slub構(gòu)建過程>>。


__alloc_pages

? __alloc_pages函數(shù)實現(xiàn)在mm/page_alloc.c文件:

struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid,nodemask_t *nodemask) {struct page *page;unsigned int alloc_flags = ALLOC_WMARK_LOW; // 默認低水位線// #define ALLOC_WMARK_MIN WMARK_MIN // 最低水位線// #define ALLOC_WMARK_LOW WMARK_LOW // 低水位線// #define ALLOC_WMARK_HIGH WMARK_HIGH // 高水位線// #define ALLOC_NO_WATERMARKS 0x04 // 不檢查水位線// enum zone_watermarks {// WMARK_MIN, // 內(nèi)存區(qū)域的空閑頁面最低水位線,說明本區(qū)域的內(nèi)存嚴重不足// WMARK_LOW, // 內(nèi)存區(qū)域的空閑頁面低水位線,說明本區(qū)域的內(nèi)存輕微不足,默認值為WMARK_MIN的125%// WMARK_HIGH, // 內(nèi)存區(qū)域的空閑頁數(shù)高水位線,說明本區(qū)域內(nèi)存充足,默認值為WMARK_MAX的150%// WMARK_PROMO, // 內(nèi)存區(qū)域的空閑頁面大于高水位線;當內(nèi)存需要回收時,對快速內(nèi)存節(jié)點通過kswapd以回收頁面,至空閑頁面略高于WMARK_PROMO水位線// NR_WMARK // unsigned long _watermark[NR_WMARK]; 表示區(qū)域(zone)水平線,由WMARK_MIN... WMARK_PROMO索引// };gfp_t alloc_gfp; /* The gfp_t that was actually used for allocation */struct alloc_context ac = { };/** There are several places where we assume that the order value is sane* so bail out early if the request is out of bound.*/if (WARN_ON_ONCE_GFP(order >= MAX_ORDER, gfp)) // 調(diào)用dump_stack,打印堆棧信息,指定__GFP_NOWARN標志時,不輸出警告信息return NULL;gfp &= gfp_allowed_mask;// 在早期引導期間,gfp_allowed_mask被設置為GFP_BOOT_MASK,// 以限制在啟用中斷之前使用的GFP標志// 一旦啟用了中斷,在系統(tǒng)運行時將其設置為__GFP_BITS_MASK// 在休眠期間,PM使用它來避免設備掛起時內(nèi)存分配期間的I/O/* 應用作用域分配約束。這主要是關(guān)于GFP_NOFS的。GFP_NOIO必須從一個特定的上下文繼承所有的分配請求,該上下文被標記為memalloc_no{fs,io}_{save,restore}。PF_MEMALLOC_PIN確保在分配時不使用可移動區(qū)域 */gfp = current_gfp_context(gfp);alloc_gfp = gfp;if (!prepare_alloc_pages(gfp, order, preferred_nid, nodemask, &ac,&alloc_gfp, &alloc_flags))return NULL;

? 進入prepare_alloc_pages函數(shù):

static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,int preferred_nid, nodemask_t *nodemask,struct alloc_context *ac, gfp_t *alloc_gfp,unsigned int *alloc_flags) {ac->highest_zoneidx = gfp_zone(gfp_mask); // highest_zoneidx表示分配請求的最高可用區(qū)域索引// 由于分區(qū)的性質(zhì),低于highest_zoneidx的分區(qū)上的內(nèi)存將受到lowmem_reserve[highest_zoneidx]的保護// 回收/壓縮也使用最高的zoneidx來限制目標區(qū)域// 因為高于此索引的區(qū)域不能用于此分配請求ac->zonelist = node_zonelist(preferred_nid, gfp_mask); // 獲取區(qū)域列表ac->nodemask = nodemask;ac->migratetype = gfp_migratetype(gfp_mask); // 將GFP標志轉(zhuǎn)換為其相應的遷移類型if (cpusets_enabled()) { // cpusets有效*alloc_gfp |= __GFP_HARDWALL; // __GFP_HARDWALL 強制執(zhí)行cpuset內(nèi)存分配策略/** When we are in the interrupt context, it is irrelevant* to the current task context. It means that any node ok.*/if (in_task() && !ac->nodemask) // 在任務上下文中并且nodemask為NULLac->nodemask = &cpuset_current_mems_allowed;// #define cpuset_current_mems_allowed (current->mems_allowed)else*alloc_flags |= ALLOC_CPUSET; // 檢查cpuset是否正確}fs_reclaim_acquire(gfp_mask); // 檢查(符合)/增加頁面回收獨占鎖fs_reclaim_release(gfp_mask); // 釋放鎖might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM); // 可以回收的情況下,主動觸發(fā)一次調(diào)度if (should_fail_alloc_page(gfp_mask, order)) // should_fail_alloc_page做一些預檢查, 一些無法分配的條件會直接報錯return false;*alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, *alloc_flags); // 允許來自CMA區(qū)域的分配 ?ac->spread_dirty_pages = (gfp_mask & __GFP_WRITE);// __GFP_WRITE表示調(diào)用者允許獲取臟頁面// 在可能的情況下,這些頁面將分布在本地區(qū)域之間// 以避免所有臟頁面位于一個區(qū)域中(公平區(qū)域分配策略)ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask); // 首選區(qū)域用于統(tǒng)計,它也用作zonelist迭代器的起點// 返回位于或低于允許節(jié)點掩碼內(nèi)給定區(qū)域索引的第一個區(qū)域return true; }

? 繼續(xù)看__alloc_pages函數(shù):

alloc_flags |= alloc_flags_nofragment(ac.preferred_zoneref->zone, gfp); // 計算分配標志page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac); // 從足夠空閑的區(qū)域中分配頁 || \/ static struct page * get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,const struct alloc_context *ac) {...no_fallback = alloc_flags & ALLOC_NOFRAGMENT;z = ac->preferred_zoneref;for_next_zone_zonelist_nodemask(zone, z, ac->highest_zoneidx,ac->nodemask) { // 掃描區(qū)域列表,尋找一個有足夠空閑的區(qū)域if (cpusets_enabled() &&(alloc_flags & ALLOC_CPUSET) &&!__cpuset_zone_allowed(zone, gfp_mask))// cpusets有效并且已設置ALLOC_CPUSET標志,在符合條件的區(qū)域中尋找continue;if (ac->spread_dirty_pages) { // 允許獲取臟頁面if (last_pgdat != zone->zone_pgdat) { 如果區(qū)域的zone_pgdat節(jié)點不等于 last_pgdat節(jié)點last_pgdat = zone->zone_pgdat; // 賦給last_pgdatlast_pgdat_dirty_ok = node_dirty_ok(zone->zone_pgdat);}if (!last_pgdat_dirty_ok)continue;}if (no_fallback && nr_online_nodes > 1 &&zone != ac->preferred_zoneref->zone) {// 避免混合頁面塊類型 并且 NUMA節(jié)點數(shù)量大于1 并且 當前遍歷到的區(qū)域不是起點區(qū)域int local_nid;/** If moving to a remote node, retry but allow* fragmenting fallbacks. Locality is more important* than fragmentation avoidance.*/local_nid = zone_to_nid(ac->preferred_zoneref->zone);if (zone_to_nid(zone) != local_nid) { // 如果不是本地節(jié)點alloc_flags &= ~ALLOC_NOFRAGMENT; // 取消ALLOC_NOFRAGMENT標志goto retry;}}mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK); // 如果分配標志為低水位線(默認值)if (!zone_watermark_fast(zone, order, mark,ac->highest_zoneidx, alloc_flags,gfp_mask)) { // 區(qū)域水位線是否符合分配int ret;...ret = node_reclaim(zone->zone_pgdat, gfp_mask, order); // 回收未映射(使用)的文件備份頁面switch (ret) {case NODE_RECLAIM_NOSCAN:/* did not scan */continue;case NODE_RECLAIM_FULL:/* scanned but unreclaimable */continue;default:/* did we reclaim enough */if (zone_watermark_ok(zone, order, mark,ac->highest_zoneidx, alloc_flags)) // 當前內(nèi)存區(qū)中的頁面是否在水位線以上goto try_this_zone;continue;}}try_this_zone: page = rmqueue(ac->preferred_zoneref->zone, zone, order,gfp_mask, alloc_flags, ac->migratetype); // 遷移到指定類型,合并成連續(xù)的頁,喚醒kswapd

? rmqueue函數(shù)分析,繼續(xù)看__alloc_pages函數(shù):

if (page) {prep_new_page(page, order, gfp_mask, alloc_flags); // 準備分配頁,lru.next = 1 ?0/** If this is a high-order atomic allocation then check* if the pageblock should be reserved for the future*/if (unlikely(order && (alloc_flags & ALLOC_HARDER)))reserve_highatomic_pageblock(page, zone, order); // 保留pageblock以獨占使用高階原子分配return page;}...if (no_fallback) {alloc_flags &= ~ALLOC_NOFRAGMENT; // 去除避免混合頁面塊類型標志goto retry;}return NULL; }

? 繼續(xù)看__alloc_pages函數(shù):

alloc_gfp = gfp; ac.spread_dirty_pages = false; ac.nodemask = nodemask; page = __alloc_pages_slowpath(alloc_gfp, order, &ac);

? __alloc_pages_slowpath函數(shù)分析,繼續(xù)看__alloc_pages函數(shù):

out:if (memcg_kmem_enabled() && (gfp & __GFP_ACCOUNT) && page &&unlikely(__memcg_kmem_charge_page(page, gfp, order) != 0)) {// mem_cgroup開啟 并且 頁分配在mem_cgroup 并且 頁有效 并且 mem_cgroup不能控制頁__free_pages(page, order); // 釋放頁page = NULL;}trace_mm_page_alloc(page, order, alloc_gfp, ac.migratetype); // 增加trace打印頁相關(guān)信息return page; } EXPORT_SYMBOL(__alloc_pages);

? 到這里頁分配核心函數(shù)流程分析完成了,? 查看trace_mm_page_alloc函數(shù)分析


rmqueue

static inline struct page *rmqueue(struct zone *preferred_zone,struct zone *zone, unsigned int order,gfp_t gfp_flags, unsigned int alloc_flags,int migratetype) {unsigned long flags;struct page *page;if (likely(pcp_allowed_order(order))) { // 檢查是否符合order標準,普通頁order <= 3,32KB或以內(nèi)// 大頁order <= 9,2MB或以內(nèi)if (!IS_ENABLED(CONFIG_CMA) || alloc_flags & ALLOC_CMA ||migratetype != MIGRATE_MOVABLE) { // 如果支持CMA內(nèi)存分配器(分配大塊連續(xù)物理內(nèi)存)page = rmqueue_pcplist(preferred_zone, zone, order,gfp_flags, migratetype, alloc_flags);// 從每cpu列表中取出page(釋放lru)goto out;}}/* 我們絕對不希望調(diào)用方使用__GFP_NOFAIL分配大于order-1的頁面單元 */WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));do {page = NULL;spin_lock_irqsave(&zone->lock, flags);/* 當由于非cma分配上下文跳過pcplist時,order-0請求可以到達這里。*//* HIGHATOMIC區(qū)域是為高階原子分配保留的,因此order-0請求應該跳過它 */if (order > 0 && alloc_flags & ALLOC_HARDER)page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);// 遍歷指定的migratetype的空閑列表,并從空閑列表中刪除最小的可用頁面 zone->free_area[order].nr_free--;// 合并到連續(xù)的頁面 free_area->free_listif (!page) {page = __rmqueue(zone, order, migratetype, alloc_flags);// 合并到連續(xù)的(CMA)頁面 if (!page)goto failed;}__mod_zone_freepage_state(zone, -(1 << order),get_pcppage_migratetype(page)); // 修改頁狀態(tài)spin_unlock_irqrestore(&zone->lock, flags);} while (check_new_pages(page, order));__count_zid_vm_events(PGALLOC, page_zonenum(page), 1 << order); // 更新每cpu狀態(tài)zone_statistics(preferred_zone, zone, 1); // 更新當前zone的統(tǒng)計信息out:/* Separate test+clear to avoid unnecessary atomics */if (test_bit(ZONE_BOOSTED_WATERMARK, &zone->flags)) {clear_bit(ZONE_BOOSTED_WATERMARK, &zone->flags); // 清除ZONE_BOOSTED_WATERMARK標志wakeup_kswapd(zone, 0, 0, zone_idx(zone)); // 喚醒kswapd// 如果區(qū)域具有由伙伴分配器管理的頁面,則返回true// 所有回收決策都必須使用此函數(shù)}VM_BUG_ON_PAGE(page && bad_range(zone, page), page);return page;failed:spin_unlock_irqrestore(&zone->lock, flags);return NULL; }

__alloc_pages_slowpath

static inline struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,struct alloc_context *ac){bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER;...if (WARN_ON_ONCE((gfp_mask & (__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)) ==(__GFP_ATOMIC|__GFP_DIRECT_RECLAIM)))// 如果gfp_mask包含__GFP_ATOMIC和__GFP_DIRECT_RECLAIM標志,輸出一次警告// %__GFP_ATOMIC表示調(diào)用者不能回收或休眠// %__GFP_DIRECT_RECLAIM表示調(diào)用者可以直接回收gfp_mask &= ~__GFP_ATOMIC; // 去除__GFP_ATOMIC標志.../* 快速路徑僅在需要喚醒kswapd之前使用alloc_flags */alloc_flags = gfp_to_alloc_flags(gfp_mask);/* 我們需要重新計算區(qū)域列表迭代器的起點,因為我們可能在快速路徑中使用了不同的節(jié)點掩碼,或者有一個cpuset修改,我們正在重試,否則我們可能會在不合格的區(qū)域上無休止地迭代 */ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask);// 首選區(qū)域用于統(tǒng)計,它也用作zonelist迭代器的起點// 返回位于或低于允許節(jié)點掩碼內(nèi)給定區(qū)域索引的第一個區(qū)域if (!ac->preferred_zoneref->zone)goto nopage;...if (alloc_flags & ALLOC_KSWAPD) wake_all_kswapds(order, gfp_mask, ac); // 喚醒所有kswapd線程page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac); // 從足夠空閑的區(qū)域中分配頁if (page)goto got_pg;/* 對于開銷較大的分配,請先嘗試直接壓縮,因為很可能我們有足夠的基頁,不需要回收。對于不可移動的高順序分配,也要這樣做,因為壓縮將嘗試通過從相同migratetype的塊遷移來防止永久碎片。對于允許忽略水印的分配,不要嘗試這樣做,因為還沒有試過alloc_no_watermark(可以測試一下) */if (can_direct_reclaim &&(costly_order ||(order > 0 && ac->migratetype != MIGRATE_MOVABLE))&& !gfp_pfmemalloc_allowed(gfp_mask)) {page = __alloc_pages_direct_compact(gfp_mask, order,alloc_flags, ac,INIT_COMPACT_PRIORITY,&compact_result); // 在回收之前,嘗試對高階分配進行內(nèi)存壓縮 if (page)goto got_pg;/* 使用__GFP_NORETRY檢查昂貴的分配,其中包括一些THP頁面錯誤分配 */ if (costly_order && (gfp_mask & __GFP_NORETRY)) {if (compact_result == COMPACT_SKIPPED ||compact_result == COMPACT_DEFERRED) // 內(nèi)存無法壓縮或壓縮失敗goto nopage;/* 看起來回收/壓縮值得一試,但同步壓縮可能非常昂貴,所以請繼續(xù)使用異步壓縮 */compact_priority = INIT_COMPACT_PRIORITY;}} retry:/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */if (alloc_flags & ALLOC_KSWAPD)wake_all_kswapds(order, gfp_mask, ac); // 喚醒所有kswapd線程reserve_flags = __gfp_pfmemalloc_flags(gfp_mask);// 區(qū)分真正需要訪問全部內(nèi)存儲備的請求和可以接受部分內(nèi)存的請求if (reserve_flags)alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, reserve_flags); // 允許來自CMA區(qū)域的分配 ?/* 如果可以忽略內(nèi)存策略,請重置nodemask和zonelist迭代器這些分配具有高優(yōu)先級和系統(tǒng)性,而不是面向用戶 */if (!(alloc_flags & ALLOC_CPUSET) || reserve_flags) {ac->nodemask = NULL;ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,ac->highest_zoneidx, ac->nodemask);}/* 嘗試使用可能已經(jīng)調(diào)整的區(qū)域列表和alloc_flags */page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);if (page)goto got_pg;/* 調(diào)用者不愿意回收頁(頁標識沒有設置),我們無法平衡任何事情if (!can_direct_reclaim)goto nopage;/* 避免遞歸回收 */if (current->flags & PF_MEMALLOC)goto nopage;/* 嘗試直接壓縮,然后分配 */page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,&did_some_progress);if (page)goto got_pg;// __GFP_NORETRY 嘗試輕量級的回收,避免循環(huán),避免oom killer那樣直接破壞if (gfp_mask & __GFP_NORETRY)goto nopage;// 如果有跡象表明在其他地方取得了進展,// 虛擬機實現(xiàn)將重試以前失敗的內(nèi)存回收過程// 它可以等待其他任務嘗試高級方法來釋放內(nèi)存,例如壓縮(消除碎片)和分頁if (costly_order && !(gfp_mask & __GFP_RETRY_MAYFAIL))goto nopage;// 檢查重試回收以向前推進給定的分配請求是否有意義// 如果我們連續(xù)嘗試MAX_RECLAIM_RETRIES次重試均未成功,// 或者如果我們回收LRU列表上的所有剩余頁面,// 甚至無法滿足水印要求,我們就會放棄if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,did_some_progress > 0, &no_progress_loops))goto retry;// 如果order-0回收無法取得任何進展,// 則重試壓縮沒有任何意義,因為壓縮的當前實現(xiàn)取決于足夠的可用內(nèi)存量if (did_some_progress > 0 &&should_compact_retry(ac, order, alloc_flags,compact_result, &compact_priority,&compaction_retries))goto retry;// 在我們開始OOM殺死之前,處理可能的cpuset更新競爭if (check_retry_cpuset(cpuset_mems_cookie, ac))goto retry_cpuset;// 回收失敗,開始oom killerpage = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);if (page)goto got_pg;// 避免無水線的分配無限循環(huán)if (tsk_is_oom_victim(current) &&(alloc_flags & ALLOC_OOM ||(gfp_mask & __GFP_NOMEMALLOC)))// 當前線程被oom killer ?// oom 異步回收// __GFP_NOMEMALLOC被用來明確禁止進入緊急儲備goto nopage;/* 只要oom 有進展,就重試 */if (did_some_progress) {no_progress_loops = 0;goto retry;}nopage:/* 在失敗之前處理可能的cpuset更新競爭 */if (check_retry_cpuset(cpuset_mems_cookie, ac))goto retry_cpuset;/* 確保__GFP_NOFAIL請求不會泄漏,并確保我們總是重試 */if (gfp_mask & __GFP_NOFAIL) {/ * __GFP_NOFAIL的所有現(xiàn)有用戶都是可阻塞的,因此如果有任何新用戶實際需要GFP_NOWAIT,就會發(fā)出警告 */if (WARN_ON_ONCE_GFP(!can_direct_reclaim, gfp_mask))goto fail;/* 來自這個上下文的PF_MEMALLOC請求是相當奇怪的,因為我們不能回收任何東西,只能循環(huán)等待某人(線程)為我們做一個工作 */WARN_ON_ONCE_GFP(current->flags & PF_MEMALLOC, gfp_mask);/* 不失敗的昂貴orders是一項艱巨的要求,我們對此沒有太多準備,因此讓我們警告這些用戶,以便我們能夠識別他們并將其轉(zhuǎn)換為其他產(chǎn)品 */WARN_ON_ONCE_GFP(order > PAGE_ALLOC_COSTLY_ORDER, gfp_mask);/* 通過允許它們訪問內(nèi)存儲備來幫助非失敗的分配,但不要使用ALLOC_NO_WATERMARKS,因為這可能會耗盡整個內(nèi)存儲備,這只會使情況變得更糟 */page = __alloc_pages_cpuset_fallback(gfp_mask, order, ALLOC_HARDER, ac);if (page)goto got_pg;// 主動放權(quán),等待下一次的調(diào)度運行cond_resched();goto retry;} fail:// 輸出警告信息warn_alloc(gfp_mask, ac->nodemask,"page allocation failure: order:%u", order); got_pg:return page; }

trace_mm_page_alloc

? trace用于跟蹤打印信息,俗稱“插樁”,通過定義類型、變量和結(jié)構(gòu),輸出相關(guān)信息。

TRACE_EVENT(mm_page_alloc,TP_PROTO(struct page *page, unsigned int order,gfp_t gfp_flags, int migratetype), TP_ARGS(page, order, gfp_flags, migratetype), TP_STRUCT__entry(__field( unsigned long, pfn )__field( unsigned int, order )__field( unsigned long, gfp_flags )__field( int, migratetype )),TP_fast_assign(__entry->pfn = page ? page_to_pfn(page) : -1UL;__entry->order = order;__entry->gfp_flags = (__force unsigned long)gfp_flags;__entry->migratetype = migratetype;),TP_printk("page=%p pfn=0x%lx order=%d migratetype=%d gfp_flags=%s",__entry->pfn != -1UL ? pfn_to_page(__entry->pfn) : NULL,__entry->pfn != -1UL ? __entry->pfn : 0,__entry->order,__entry->migratetype,show_gfp_flags(__entry->gfp_flags)) );

目錄預覽


<<baddy:初始化內(nèi)存域>>

總結(jié)

以上是生活随笔為你收集整理的baddy:核心函数入口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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