Linux內(nèi)核入門,包教會
原創(chuàng)【51CTO精選譯文】這年頭,Linux成了一個時髦詞。自詡對電腦玩的精通的學(xué)生和IT人士們,沒有哪個不在自己的電腦上安裝一、兩個Linux,并自覺趕上了時髦。然而,在Ubuntu或SUSE的論壇中,經(jīng)常有這樣的對話:
“你學(xué)Linux學(xué)了這么久,都學(xué)到了什么?”
“哦,我現(xiàn)在Linux的安裝、升級、桌面美化都很熟練!你看我這是最新版的Ubuntu,桌面很漂亮吧!”
“……”
Linux社區(qū)中有一句名言:如果你進入你的操作系統(tǒng)不知道該做什么,那最好還是關(guān)掉電腦,一定有更重要的事等著你去做。說真的,如果對Linux命令不熟練,真的不能算是學(xué)過Linux。然而另一方面,Linux內(nèi)核雖然是一般用戶可學(xué)可不學(xué)的內(nèi)容,但可以說卻是Linux操作系統(tǒng)中最好玩的部分。尤其對于開發(fā)者而言,Linux內(nèi)核開發(fā)絕對是最理想的磨練場所。51CTO編輯一直認為,國外之所以IT技術(shù)大拿林立,和他們從小接觸類UNIX系統(tǒng)、把玩內(nèi)核開發(fā)是脫不了關(guān)系的。
下面是Linux內(nèi)核開發(fā)者Robert Love寫的一篇入門文章,號稱“包教會”,推薦對Linux內(nèi)核開發(fā)感興趣的學(xué)生、Linux愛好者、開發(fā)者以及系統(tǒng)管理員們一定不要錯過。當(dāng)然,雖然標(biāo)題說是包教會,你可能需要一定的Linux命令以及C語言的基礎(chǔ)。
以下是正文內(nèi)容:
Linux內(nèi)核一直都被視為學(xué)習(xí)Linux最難的一塊,相信大家也一定看過不少關(guān)于內(nèi)核的文章,但捫心自問,你現(xiàn)在究竟掌握了多少?本文將從零開始介紹被視為高深的Linux內(nèi)核,內(nèi)容涉及內(nèi)核源代碼的下載,編譯,安裝,以及內(nèi)核開發(fā)相關(guān)的內(nèi)容。
如何獲取Linux內(nèi)核源代碼
下載Linux內(nèi)核當(dāng)然要去官方網(wǎng)站了,網(wǎng)站提供了兩種文件下載,一種是完整的Linux內(nèi)核,另一種是內(nèi)核增量補丁,它們都是tar歸檔壓縮包。除非你有特別的原因需要使用舊版本的Linux內(nèi)核,否則你應(yīng)該總是升級到最新版本。
使用Git
由Linus領(lǐng)頭的內(nèi)核開發(fā)隊伍從幾年前就開始使用Git版本控制系統(tǒng)管理Linux內(nèi)核了(參考閱讀:什么是Git?),而Git項目本身也是由Linus創(chuàng)建的,它和傳統(tǒng)的CVS不一樣,Git是分布式的,因此它的用法和工作流程很多開發(fā)人員可能會感到很陌生,但我強烈建議使用Git下載和管理Linux內(nèi)核源代碼。
你可以使用下面的Git命令獲取Linus內(nèi)核代碼樹的最新“推送”版本:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
然后使用下面的命令將你的代碼樹與Linus的代碼樹最新狀態(tài)同步:
$ git pull
安裝內(nèi)核源代碼
內(nèi)核包有GNU zip(gzip)和bzip2格式。Bzip2是默認和首選格式,因為它的壓縮比通常比gzip更好,bzip2格式的Linux內(nèi)核包一般采用linux-x.y.z.tar.bz2形式的文件名,這里的x.y.z是內(nèi)核源代碼的具體版本號,下載到源代碼包后,解壓和抽取就很簡單了,如果你下載的是bzip2包,運行:
$ tar xvjf linux-x.y.z.tar.bz2
如果你下載的是gzip包,則運行:
$ tar xvzf linux-x.y.z.tar.gz
無論執(zhí)行上面哪一個命令,最后都會將源代碼解壓和抽取到linux-x.y.z目錄下,如果你使用Git下載和管理內(nèi)核源代碼,你不需要下載tar包,只需要運行g(shù)it clone命令,它就會自動下載和解壓。
內(nèi)核源代碼通常都會安裝到/usr/src/linux下,但在開發(fā)的時候最好不要使用這個源代碼樹,因為針對你的C庫編譯的內(nèi)核版本通常也鏈接到這里的。
應(yīng)用補丁
Linux內(nèi)核開發(fā)人員會將自己的修改做成補丁與其它人員分享,而且補丁是增量的,增量補丁是從一個內(nèi)核樹移動到另一個內(nèi)核樹的有效方法,不用下載完整的內(nèi)核包就可以升級內(nèi)核,不僅可節(jié)省帶寬,也節(jié)省了內(nèi)核升級時間,應(yīng)用補丁之前先進入內(nèi)核源代碼樹所在目錄,然后運行:
$ patch –p1 < ../patch-x.y.z
注意,補丁包也有明確的版本號,這里的版本號與Linux內(nèi)核源代碼的版本號要一致,內(nèi)核和補丁版本號不一致時,強制應(yīng)用補丁會引起意想不到的后果。
內(nèi)核源代碼樹介紹
內(nèi)核源代碼樹分為許多目錄,它們下面又包含許多子目錄,源代碼樹的頂級目錄及其描述參見下表。
目錄 | 描述 |
arch | 特定架構(gòu)的源代碼 |
block | 塊I/O層 |
crypto | 加密API |
Documentation | 內(nèi)核源代碼文檔 |
drivers | 設(shè)備驅(qū)動 |
firmware | 使用某個驅(qū)動需要的設(shè)備固件 |
fs | VFS和獨立文件系統(tǒng) |
include | 內(nèi)核頭 |
init | 內(nèi)核啟動和初始化 |
ipc | 進程間通信 |
kernel | 核心子系統(tǒng),如調(diào)度器 |
lib | 助手例行程序 |
mm | 內(nèi)存管理子系統(tǒng)和VM |
net | 網(wǎng)絡(luò)子系統(tǒng) |
samples | 示例,示范代碼 |
scripts | 用于生成內(nèi)核的腳本 |
security | Linux安全模塊 |
sound | 聲音子系統(tǒng) |
usr | 早期的用戶空間代碼(叫做initramfs) |
tools | 輔助Linux開發(fā)的工具 |
virt | 虛擬化基礎(chǔ)設(shè)施 |
在源代碼樹的根目錄下還有很多文件需要說明,COPYING是內(nèi)核許可描述文件(即GNU GPL v2),CREDITS是參與Linux內(nèi)核的開發(fā)人員名單,MAINTAINERS列出了維護各個子系統(tǒng)和驅(qū)動的個人,Makefile是內(nèi)核Makefile的基礎(chǔ)。
#p#
生成內(nèi)核
生成內(nèi)核其實很簡單,甚至比編譯和安裝其它系統(tǒng)級組件,如glibc還要簡單,從2.6版本開始,Linux內(nèi)核引入了一個新的配置和生成系統(tǒng),它使生產(chǎn)內(nèi)核的操作變得更加簡單了。
配置內(nèi)核
既然已經(jīng)拿到內(nèi)核源代碼,那我們在開始編譯前就可以根據(jù)需要自行配置和定制,可以編譯你指定的功能和想要的驅(qū)動,配置內(nèi)核是生成內(nèi)核必須的一步,因為內(nèi)核提供了大量的功能,支持各種不同的硬件,有很多都需要配置,內(nèi)核配置是由配置選項控制的,配置選項都有CONFIG前綴,例如,對稱多處理(SMP)是由CONFIG_SMP配置選項配置的,如果設(shè)置了這個選項,SMP就被啟用了,反之則被禁用,配置選項可以確定會生成哪個文件,也可以通過預(yù)處理指令操控代碼。
配置選項可以控制生成過程要么是布爾型,要么是三態(tài)型,布爾型就是“是”或“否”,大部分內(nèi)核配置選項都屬于布爾型,如CONFIG_PREEMPT,而三態(tài)型則在“是”和“否”的基礎(chǔ)上,又增加一個“模塊”選項,模塊選項表示配置選項被設(shè)置了,但最后會編譯成模塊,而不是直接編譯進內(nèi)核,模塊可以理解為可獨立動態(tài)載入的對象,一般來說,驅(qū)動配置通常都是三態(tài)型。
配置選項也可以是字符串或整數(shù),這樣的選項不會控制生成過程,指定的值由內(nèi)核源代碼訪問預(yù)處理宏時使用,例如,可以為某個配置選項指定靜態(tài)分配數(shù)組的大小。
Linux廠商也會隨發(fā)行版提供預(yù)編譯的內(nèi)核,如Canonical為Ubuntu,或Red Hat為Fedora提供的內(nèi)核,這樣的內(nèi)核通常只啟用了需要的內(nèi)核功能,幾乎所有驅(qū)動都被編譯成模塊了,這樣的內(nèi)核提供了一個良好的基礎(chǔ)內(nèi)核和廣泛的硬件模塊支持,無論如何,想要成為內(nèi)核高手,你應(yīng)該編譯自己的內(nèi)核。
值得慶幸的是,內(nèi)核提供了很多工具簡化配置 ,最簡單的工具是基于文本命令行的實用程序,如:
$ make config
這個工具會一個選項一個選項地配置,但用戶需要參與,如指定“是(y)”,“否(m)”還是“模塊(m)”,整個配置過程需要很長的時間,因此,除非是有人按小時計費請你升級內(nèi)核,實在找不出別的理由用這種最原始的方法配置內(nèi)核了,相反,有現(xiàn)成的基于ncurses的圖形化工具可以代替。
$ make menuconfig
或是基于gtk+的圖形化工具
$ make gconfig
上述三個工具都將配置選項分成多個類別,如“處理器類型和特征”,你可以在這些類別上來回移動,查看內(nèi)核選項,當(dāng)然也可以修改它們的設(shè)置了。
下面這個命令會根據(jù)你的架構(gòu)創(chuàng)建一個默認的配置基礎(chǔ)。
$ make defconfig
雖然默認配置有些武斷(在i386上,默認配置是由Linus配置的),但如果你從未配置過內(nèi)核,它提供了一個良好的開端。
配置選項存儲在源代碼樹根目錄下一個名叫.config的文件中,你可以打開這個文件手工編輯其中的配置選項,修改后或要在新的內(nèi)核源代碼樹上應(yīng)用現(xiàn)有配置文件,你可以使用下面的命令驗證和更新配置:
$ make oldconfig
在生成內(nèi)核之前必須運行這個命令。
配置選項CONFIG_IKCONFIG_PROC指定了完整的內(nèi)核配置文件壓縮包位置,默認是/proc/config.gz,這樣在生成新內(nèi)核時要克隆現(xiàn)有的配置就變得非常簡單了。如果你當(dāng)前的內(nèi)核開啟了這個選項,你可以從/proc拷貝該配置文件,然后在此基礎(chǔ)上生成新的內(nèi)核:
$ zcat /proc/config.gz > .config $ make oldconfig
內(nèi)核配置好后,使用下面的命令進行生成:
$ make
和2.6以前的內(nèi)核不一樣,在生成內(nèi)核前不再需要執(zhí)行make dep命令了,依賴樹會自動維護,也不需要再指定特定的生成類型,如bzImage,或獨立生成模塊,默認Makefile規(guī)則會自動處理好一切。
將干擾信息最小化
在生成過程中會遭到警告和錯誤的干擾。最小化干擾信息的一個訣竅是重定向make的輸出,但仍然會看到一些警告和錯誤:
$ make > ../detritus
如果你想查看生成輸出,你可以事后閱讀這個文件,如果你完全不想看到任何輸出,那么就重定向到/dev/null:
$ make > /dev/null
同時執(zhí)行多個生成作業(yè)
Make命令提供了一個功能可以將生成過程拆分成多個平行的作業(yè),這些作業(yè)可以獨立運行,也可以并行運行,在多處理器系統(tǒng)上可以極大地提高生成速度,也提高了處理器利用率,因為生成大型源代碼樹會出現(xiàn)大量的I/O等待時間。
默認情況下,make只能拆分成一個作業(yè),因為Makefiles常常會包含不正確的依賴信息,如果真是這樣,多個并行執(zhí)行的作業(yè)將會引起混亂,最終會導(dǎo)致生成過程失敗,如果Makefiles中的依賴信息無誤,那么完全可以拆分成多個作業(yè)執(zhí)行,如:
$ make –jn
這里的n表示拆分的作業(yè)數(shù)量,通常按每個處理器拆分成1-2個作業(yè),例如,在一個16核心的機器上 ,你可以運行:
$ make -j32 > /dev/null
使用distcc或ccache等優(yōu)秀的工具也可以大大提高生成速度。
#p#
安裝新內(nèi)核
內(nèi)核生成好之后,你需要安裝它,如何安裝于系統(tǒng)架構(gòu)和引導(dǎo)加載程序有關(guān),我們以x86架構(gòu),grub引導(dǎo)加載程序為例進行說明。
首先將arch/i386/boot/bzImage拷貝到/boot,重命名為vmlinuz- version,這里的version也是版本號,然后編輯/boot/grub/grub.conf,為新內(nèi)核添加相應(yīng)的項目,如果是使用LILO引導(dǎo)裝載程序,則修改/etc/lilo.conf文件,然后運行l(wèi)ilo。
模塊的安裝與系統(tǒng)架構(gòu)無關(guān),都是自動完成的,以root用戶運行:
% make modules_install
這個命令會將所有編譯好的模塊安裝到/lib/modules下對應(yīng)的子目錄中。
生成過程會在源代碼樹根目錄下創(chuàng)建一個System.map文件,它包含一個符號查找表,映射內(nèi)核符號到它們的起始地址,在調(diào)試期間可以用它將內(nèi)存地址轉(zhuǎn)換成函數(shù)和變量名。
可能會遇到的問題
與普通用戶空間的應(yīng)用程序相比,Linux內(nèi)核有多個特殊的屬性,下面是我認為最重要的一些不同:
◆內(nèi)核既不訪問C庫也不訪問標(biāo)準(zhǔn)C頭;
◆內(nèi)核是用GNU C編碼的;
◆內(nèi)核缺少用戶空間提供的內(nèi)存保護;
◆內(nèi)核不能容易地執(zhí)行浮點運算;
◆內(nèi)核有一個小型的固定大小的進程堆棧;
◆由于內(nèi)核支持異步中斷和SMP,因此同步和并發(fā)是內(nèi)核主要擔(dān)心的問題;
◆可移植性也很重要。
下面我們就逐個來了解一下這些問題,所有內(nèi)核開發(fā)人員都必須記住它們。
#p#
無libc或標(biāo)準(zhǔn)頭
和用戶空間應(yīng)用程序不一樣,內(nèi)核并沒有鏈接到標(biāo)準(zhǔn)的C庫,也沒有鏈接到任何其它的庫,這樣設(shè)計的原因有很多,包括如先有雞還是先有蛋的問題,但主要原因還是速度和內(nèi)核大小,不要說完整的C庫,就是它的一個子集也夠大,內(nèi)核太大只會導(dǎo)致效率低下。
不要擔(dān)心,許多常用的libc函數(shù)都在內(nèi)核中實現(xiàn)了,例如,常見的字符串操作函數(shù)就位于lib/string.c中,只需要包括它的頭文件<linux/string.h >就可以了。
這里的頭文件指的是內(nèi)核源代碼樹中的頭文件,內(nèi)核也只能使用樹內(nèi)的頭文件,基礎(chǔ)文件位于源代碼根目錄的include/目錄下,例如,<linux/inotify.h>頭文件就位于include/linux/inotify.h。
與架構(gòu)相關(guān)的頭文件則位于arch/<architecture>/include/asm,例如,如果在x86架構(gòu)下編譯,與你架構(gòu)相關(guān)的文件就是arch/x86/include/asm,只需要在引用這些頭的地方加上asm/前綴即可,如<asm/ioctl.h>。
漏掉的大部分都是類似printf()這樣的函數(shù),內(nèi)核不會使用printf(),但它提供了printk()函數(shù),其表現(xiàn)絕不比printf()差,printk()會拷貝格式化的字符串到內(nèi)核日志緩沖區(qū),syslog程序就是從這里讀取信息的,其用法也和printf()類似:
printk("Hello world! A string '%s' and an integer '%d'\n", str, i);
printf()和printk()之間最大的不同是,printk()允許你指定一個優(yōu)先級標(biāo)記,syslogd使用這個標(biāo)記確定在哪里顯示內(nèi)核消息,下面是一個使用優(yōu)先級標(biāo)記的示例:
printk(KERN_ERR "this is an error!\n");
注意在KERN_ERR和打印的消息之間沒有逗號,這是故意這么設(shè)計的,優(yōu)先級使用一個預(yù)定義的字符定義,在編譯期間它與打印的信息是串聯(lián)的。
GNU C
和許多Unix內(nèi)核類似,Linux內(nèi)核也是用C編寫的,但也許會讓人很意外,內(nèi)核不是用嚴(yán)謹?shù)腁NSI C編寫的,內(nèi)核開發(fā)人員用的卻是gcc(GNU編譯器集,包含了編譯內(nèi)核和Linux C程序的C編譯器)中的各種語言擴展。
內(nèi)核開發(fā)人員同時使用了C語言的ISO C99和GNU C擴展,這些變化讓Linux內(nèi)核與gcc結(jié)合得更緊密,但最近又出現(xiàn)了一個編譯器 – 英特爾的C編譯器 – 也對gcc的功能支持得相當(dāng)好,因此也可以用它來編譯Linux內(nèi)核。最低支持的gcc版本是3.2,建議采用gcc 4.4或更高的版本編譯。使用ISO C99擴展也是可以的,因為C99是C語言的官方版本。
內(nèi)聯(lián)函數(shù)
C99和GNU C都支持內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)是直接插入到每個函數(shù)調(diào)用的位置的,消除了函數(shù)調(diào)用和返回的開銷,允許進一步優(yōu)化,因為編譯器可以同時優(yōu)化調(diào)用者和被調(diào)用函數(shù),但它也有缺點,代碼大小會增加,因為函數(shù)的內(nèi)容被直接復(fù)制到所調(diào)用者內(nèi)部了,因此也會增加內(nèi)存消耗和指令緩存空間。內(nèi)核開發(fā)人員一般在小型時間很關(guān)鍵的函數(shù)中才會使用內(nèi)聯(lián)函數(shù)。
定義函數(shù)時,使用static和inline關(guān)鍵字聲明內(nèi)聯(lián)函數(shù),例如:
static inline void wolf(unsigned long tail_size)
函數(shù)必須先聲明后使用,否則編譯器就不能使函數(shù)內(nèi)聯(lián),一般做法是將內(nèi)聯(lián)函數(shù)放在頭文件中,因為它們被標(biāo)記為static,不會創(chuàng)建輸出函數(shù),如果內(nèi)聯(lián)函數(shù)僅在一個文件中使用,可以放在該文件的頂部。
在內(nèi)核中,與復(fù)雜的宏相比,出于安全和可讀性方面考慮,內(nèi)聯(lián)函數(shù)是首選。
內(nèi)聯(lián)匯編
Gcc C編譯器允許在C函數(shù)中嵌入?yún)R編指令,asm()編譯器指令用于內(nèi)聯(lián)匯編代碼,例如,這個內(nèi)聯(lián)匯編指令執(zhí)行x86處理器的rdtsc指令,返回時間戳寄存器(tsc)的值:
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low and high now contain the lower and upper 32-bits of the 64-bit tsc */
Linux內(nèi)核是用C和匯編語言混合編寫的,與底層硬件相關(guān)的代碼很多都是用匯編語言寫的,剩下的大部分內(nèi)核代碼都是直接用C編寫的。
分支注解
Gcc C編譯器內(nèi)置了一個指令優(yōu)化條件分支,內(nèi)核將這個打包成易于使用的宏 - likely()和unlikely()。
先看下面這樣的if語句:
if (error) { /* ... */ }
將這個分支標(biāo)記為非常不可能采用
/* we predict 'error' is nearly always zero ... */ if (unlikely(error)) { /* ... */ }
相反,將這個分支標(biāo)記為非常可能采用
/* we predict 'success' is nearly always nonzero ... */ if (likely(success)) { /* ... */ }
當(dāng)分支指令已經(jīng)知道一個優(yōu)先級,或你想在一種情況下優(yōu)化另一種情況時應(yīng)該使用上述指令,最重要的是,當(dāng)分支正確標(biāo)記時,這些指令會提升性能,但如果分支標(biāo)記錯誤則會降低性能,在內(nèi)核代碼中,unlikely()要使用得更多,因為if語句傾向于表示一種特殊情況。
無內(nèi)存保護
當(dāng)用戶空間的應(yīng)用程序嘗試一個非法的內(nèi)存訪問時,內(nèi)核可以捕捉到錯誤,發(fā)送SIGSEGV信號,殺掉進程,如果內(nèi)核嘗試一個非法的內(nèi)存訪問時,結(jié)果就不受控制了,因為誰也無法去控制內(nèi)核,這也是內(nèi)核最主要的失誤。
此外,內(nèi)核內(nèi)存也是不可分頁的,因此你消耗的每個內(nèi)存字節(jié)都比物理內(nèi)存的一個字節(jié)要少。
不能(容易)使用浮點數(shù)
當(dāng)用戶空間進程使用浮點指令時,內(nèi)核要負責(zé)處理從整型到浮點模式的轉(zhuǎn)換。
與用戶空間不一樣,內(nèi)核不能無縫支持浮點數(shù),因為它自己不能輕易地捕捉到自己,在內(nèi)核中使用浮點數(shù)需要手動保存和恢復(fù)浮點數(shù)寄存器,因此除非卻有必要,否則盡量不要在內(nèi)核中做浮點運算。
小型,固定大小的堆棧
用戶空間可以靜態(tài)分配許多不同的堆棧,包括巨型結(jié)構(gòu)和千元數(shù)組,這個行為是合法的,因為用戶空間有很大的堆棧,并可以動態(tài)增長。
內(nèi)核堆棧不大也不是動態(tài)的,相反,它很小且是固定的,內(nèi)核堆棧的精確大小根據(jù)架構(gòu)有所不同,在x86上,堆棧大小是在編譯時確定的,一般是4KB或8KB,歷史上,內(nèi)核堆棧有2頁,通常表示它處于32位架構(gòu)上,大小是8KB,如果是16KB就表示是64位架構(gòu),總之大小是固定的,每個進程接收它自己的堆棧。
同步和并發(fā)
內(nèi)核最容易受競爭條件影響,和一個單線程的用戶空間應(yīng)用程序不一樣,有許多內(nèi)核特性允許同時訪問共享資源,因此需要同步以防止競爭,特別是:
◆Linux是一種搶占式多任務(wù)操作系統(tǒng),進程是由內(nèi)核的進程調(diào)度器隨意調(diào)度和再次調(diào)度的,內(nèi)核必須在這些任務(wù)之間同步;
◆Linux支持對稱多處理(SMP),因此,如果沒有適當(dāng)?shù)谋Wo,在兩個或多個處理器上同時執(zhí)行的內(nèi)核代碼可能會同時訪問相同的資源;
◆中斷是異步發(fā)生的,因此,如果沒有適當(dāng)?shù)谋Wo,在訪問資源期間也可能發(fā)生中斷,中斷處理程序可能就會訪問到相同的資源;
◆Linux是有優(yōu)先權(quán)的,因此,如果沒有適當(dāng)?shù)谋Wo,內(nèi)核代碼可能會優(yōu)先執(zhí)行,訪問其它代碼正在使用的資源。
解決這些問題的一般方法是自旋鎖和信號量。
可移植性的重要性
雖然用戶空間應(yīng)用程序一般不會太重視可移植性,但Linux的確是一個可移植性操作系統(tǒng),應(yīng)該保持一致,這意味著與架構(gòu)無關(guān)的C代碼必須在大量的系統(tǒng)上正確地編譯和運行,與架構(gòu)相關(guān)的代碼必須在內(nèi)核源代碼樹中使用特定的目錄分隔開。
總結(jié)
可以肯定,內(nèi)核有它獨特的性質(zhì),它有它自己的一些原則,不過,內(nèi)核的復(fù)雜性和障礙與其它大型軟件項目相比,并沒有什么大的不同,Linux開發(fā)道路上最重要的一步是認識到內(nèi)核并不可怕,不熟悉?當(dāng)然!不可逾越?當(dāng)然不是!
原文地址:http://www.informit.com/articles/article.aspx?p=1610334
【編輯推薦】