關于Linux 內核配置系統(tǒng)淺析-基礎電子_第1頁
關于Linux 內核配置系統(tǒng)淺析-基礎電子_第2頁
關于Linux 內核配置系統(tǒng)淺析-基礎電子_第3頁
關于Linux 內核配置系統(tǒng)淺析-基礎電子_第4頁
關于Linux 內核配置系統(tǒng)淺析-基礎電子_第5頁
已閱讀5頁,還剩18頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

精品文檔-下載后可編輯關于Linux內核配置系統(tǒng)淺析-基礎電子隨著Linux操作系統(tǒng)的廣泛應用,特別是Linux在嵌入式領域的發(fā)展,越來越多的人開始投身到Linux內核級的開發(fā)中。面對日益龐大的Linux內核源代碼,開發(fā)者在完成自己的內核代碼后,都將面臨著同樣的問題,即如何將源代碼融入到Linux內核中,增加相應的Linux配置選項,并終被編譯進Linux內核。這就需要了解Linux的內核配置系統(tǒng)。

眾所周知,Linux內核是由分布在的Linux愛好者共同開發(fā)的,Linux內核每天都面臨著許多新的變化。但是,Linux內核的組織并沒有出現(xiàn)混亂的現(xiàn)象,反而顯得非常的簡潔,而且具有很好的擴展性,開發(fā)人員可以很方便的向Linux內核中增加新的內容。原因之一就是Linux采用了模塊化的內核配置系統(tǒng),從而保證了內核的擴展性。

本文首先分析了Linux內核中的配置系統(tǒng)結構,然后,解釋了Makefile和配置文件的格式以及配置語句的含義,,通過一個簡單的例子--TESTDriver,具體說明如何將自行開發(fā)的代碼加入到Linux內核中。在下面的文章中,不可能解釋所有的功能和命令,只對那些常用的進行解釋,至于那些沒有討論到的,請讀者參考后面的參考文獻。

1.配置系統(tǒng)的基本結構

Linux內核的配置系統(tǒng)由三個部分組成,分別是:

Makefile:分布在Linux內核源代碼中的Makefile,定義Linux內核的編譯規(guī)則;

配置文件(config.in):給用戶提供配置選擇的功能;

配置工具:包括配置命令解釋器(對配置腳本中使用的配置命令進行解釋)和配置用戶界面(提供基于字符界面、基于Ncurses圖形界面以及基于Xwindows圖形界面的用戶配置界面,各自對應于Makeconfig、Makemenuconfig和makexconfig)。

這些配置工具都是使用腳本語言,如Tcl/TK、Perl編寫的(也包含一些用C編寫的代碼)。本文并不是對配置系統(tǒng)本身進行分析,而是介紹如何使用配置系統(tǒng)。所以,除非是配置系統(tǒng)的維護者,一般的內核開發(fā)者無須了解它們的原理,只需要知道如何編寫Makefile和配置文件就可以。所以,在本文中,我們只對Makefile和配置文件進行討論。另外,凡是涉及到與具體CPU體系結構相關的內容,我們都以ARM為例,這樣不僅可以將討論的問題明確化,而且對內容本身不產生影響。

2.Makefile

2.1Makefile概述

Makefile的作用是根據(jù)配置的情況,構造出需要編譯的源文件列表,然后分別編譯,并把目標代碼鏈接到一起,終形成Linux內核二進制文件。

由于Linux內核源代碼是按照樹形結構組織的,所以Makefile也被分布在目錄樹中。Linux內核中的Makefile以及與Makefile直接相關的文件有:

Makefile:頂層Makefile,是整個內核配置、編譯的總體控制文件。

.config:內核配置文件,包含由用戶選擇的配置選項,用來存放內核配置后的結果(如makeconfig)。

arch/*/Makefile:位于各種CPU體系目錄下的Makefile,如arch/arm/Makefile,是針對特定平臺的Makefile。

各個子目錄下的Makefile:比如drivers/Makefile,負責所在子目錄下源代碼的管理。

Rules.make:規(guī)則文件,被所有的Makefile使用。

用戶通過makeconfig配置后,產生了.config。頂層Makefile讀入.config中的配置選擇。頂層Makefile有兩個主要的任務:產生vmlinux文件和內核模塊(module)。為了達到此目的,頂層Makefile遞歸的進入到內核的各個子目錄中,分別調用位于這些子目錄中的Makefile。至于到底進入哪些子目錄,取決于內核的配置。在頂層Makefile中,有一句:includearch/$(ARCH)/Makefile,包含了特定CPU體系結構下的Makefile,這個Makefile中包含了平臺相關的信息。

位于各個子目錄下的Makefile同樣也根據(jù).config給出的配置信息,構造出當前配置下需要的源文件列表,并在文件的有include$(TOPDIR)/Rules.make。

Rules.make文件起著非常重要的作用,它定義了所有Makefile共用的編譯規(guī)則。比如,如果需要將本目錄下所有的c程序編譯成匯編代碼,需要在Makefile中有以下的編譯規(guī)則:

%.s:%.c

$(CC)$(CFLAGS)-S$-o$@

有很多子目錄下都有同樣的要求,就需要在各自的Makefile中包含此編譯規(guī)則,這會比較麻煩。而Linux內核中則把此類的編譯規(guī)則統(tǒng)一放置到Rules.make中,并在各自的Makefile中包含進了Rules.make(includeRules.make),這樣就避免了在多個Makefile中重復同樣的規(guī)則。對于上面的例子,在Rules.make中對應的規(guī)則為:

%.s:%.c

$(CC)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$(*F))$(CFLAGS_$@)-S$-o$@

2.2Makefile中的變量

頂層Makefile定義并向環(huán)境中輸出了許多變量,為各個子目錄下的Makefile傳遞一些信息。有些變量,比如SUBDIRS,不僅在頂層Makefile中定義并且賦初值,而且在arch/*/Makefile還作了擴充。

常用的變量有以下幾類:

1)版本信息

版本信息有:VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION,KERNELRELEASE。版本信息定義了當前內核的版本,比如VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它們共同構成內核的發(fā)行版本KERNELRELEASE:2.4.18-rmk7

2)CPU體系結構:ARCH

在頂層Makefile的開頭,用ARCH定義目標CPU的體系結構,比如ARCH:=arm等。許多子目錄的Makefile中,要根據(jù)ARCH的定義選擇編譯源文件的列表。

3)路徑信息:TOPDIR,SUBDIRS

TOPDIR定義了Linux內核源代碼所在的根目錄。例如,各個子目錄下的Makefile通過$(TOPDIR)/Rules.make就可以找到Rules.make的位置。

SUBDIRS定義了一個目錄列表,在編譯內核或模塊時,頂層Makefile就是根據(jù)SUBDIRS來決定進入哪些子目錄。SUBDIRS的值取決于內核的配置,在頂層Makefile中SUBDIRS賦值為kerneldriversmmfsnetipclib;根據(jù)內核的配置情況,在arch/*/Makefile中擴充了SUBDIRS的值,參見4)中的例子。

4)內核組成信息:HEAD,CORE_FILES,NETWORKS,DRIVERS,LIBS

Linux內核文件vmlinux是由以下規(guī)則產生的:

vmlinux:$(CONFIGURATION)init/main.oinit/version.olinuxsubdirs

$(LD)$(LINKFLAGS)$(HEAD)init/main.oinit/version.o\

--start-group\

$(CORE_FILES)\

$(DRIVERS)\

$(NETWORKS)\

$(LIBS)\

--end-group\

-ovmlinux

可以看出,vmlinux是由HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS和LIBS組成的。這些變量(如HEAD)都是用來定義連接生成vmlinux的目標文件和庫文件列表。其中,HEAD在arch/*/Makefile中定義,用來確定被鏈接進vmlinux的文件列表。比如,對于ARM系列的CPU,HEAD定義為:

HEAD:=arch/arm/kernel/head-$(PROCESSOR).o\

arch/arm/kernel/init_task.o

表明head-$(PROCESSOR).o和init_task.o需要被鏈接到vmlinux中。PROCESSOR為armv或armo,取決于目標CPU。CORE_FILES,NETWORK,DRIVERS和LIBS在頂層Makefile中定義,并且由arch/*/Makefile根據(jù)需要進行擴充。CORE_FILES對應著內核的文件,有kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,這些是組成內核為重要的文件。同時,arch/arm/Makefile對CORE_FILES進行了擴充:

#arch/arm/Makefile

#Ifwehaveamachine-specificdirectory,thenincludeitinthebuild.

MACHDIR:=arch/arm/mach-$(MACHINE)

ifeq($(MACHDIR),$(wildcard$(MACHDIR)))

SUBDIRS+=$(MACHDIR)

CORE_FILES:=$(MACHDIR)/$(MACHINE).o$(CORE_FILES)

endif

HEAD:=arch/arm/kernel/head-$(PROCESSOR).o\

arch/arm/kernel/init_task.o

SUBDIRS+=arch/arm/kernelarch/arm/mmarch/arm/libarch/arm/nwfpe

CORE_FILES:=arch/arm/kernel/kernel.oarch/arm/mm/mm.o$(CORE_FILES)

LIBS:=arch/arm/lib/lib.a$(LIBS)

5)編譯信息:CPP,CC,AS,LD,AR,CFLAGS,LINKFLAGS

在Rules.make中定義的是編譯的通用規(guī)則,具體到特定的場合,需要明確給出編譯環(huán)境,編譯環(huán)境就是在以上的變量中定義的。針對交叉編譯的要求,定義了CROSS_COMPILE。比如:

CROSS_COMPILE=arm-linux-

CC=$(CROSS_COMPILE)gcc

LD=$(CROSS_COMPILE)ld

CROSS_COMPILE定義了交叉編譯器前綴arm-linux-,表明所有的交叉編譯工具都是以arm-linux-開頭的,所以在各個交叉編譯器工具之前,都加入了$(CROSS_COMPILE),以組成一個完整的交叉編譯工具文件名,比如arm-linux-gcc。

CFLAGS定義了傳遞給C編譯器的參數(shù)。

LINKFLAGS是鏈接生成vmlinux時,由鏈接器使用的參數(shù)。LINKFLAGS在arm/*/Makefile中定義,比如:

#arch/arm/Makefile

LINKFLAGS:=-p-X-Tarch/arm/vmlinux.lds

6)配置變量CONFIG_*

.config文件中有許多的配置變量等式,用來說明用戶配置的結果。例如CONFIG_MODULES=y表明用戶選擇了Linux內核的模塊功能。

.config被頂層Makefile包含后,就形成許多的配置變量,每個配置變量具有確定的值:y表示本編譯選項對應的內核代碼被靜態(tài)編譯進Linux內核;m表示本編譯選項對應的內核代碼被編譯成模塊;n表示不選擇此編譯選項;如果根本就沒有選擇,那么配置變量的值為空。

2.3Rules.make變量

前面講過,Rules.make是編譯規(guī)則文件,所有的Makefile中都會包括Rules.make。Rules.make文件定義了許多變量,為重要是那些編譯、鏈接列表變量。

O_OBJS,L_OBJS,OX_OBJS,LX_OBJS:本目錄下需要編譯進Linux內核vmlinux的目標文件列表,其中OX_OBJS和LX_OBJS中的"X"表明目標文件使用了EXPORT_SYMBOL輸出符號。

M_OBJS,MX_OBJS:本目錄下需要被編譯成可裝載模塊的目標文件列表。同樣,MX_OBJS中的"X"表明目標文件使用了EXPORT_SYMBOL輸出符號。

O_TARGET,L_TARGET:每個子目錄下都有一個O_TARGET或L_TARGET,Rules.make首先從源代碼編譯生成O_OBJS和OX_OBJS中所有的目標文件,然后使用$(LD)-r把它們鏈接成一個O_TARGET或L_TARGET。O_TARGET以.o結尾,而L_TARGET以.a結尾。

2.4子目錄Makefile

子目錄Makefile用來控制本級目錄以下源代碼的編譯規(guī)則。我們通過一個例子來講解子目錄Makefile的組成:

#

#Makefileforthelinuxkernel.

#

#Allofthe(potential)objectsthatexportsymbols.

#Thislistcomesfrom'grep-lEXPORT_SYMBOL*.[hc]'.

export-objs:=tc.o

#Objectfilelists.

obj-y:=

obj-m:=

obj-n:=

obj-:=

obj-$(CONFIG_TC)+=tc.o

obj-$(CONFIG_ZS)+=zs.o

obj-$(CONFIG_VT)+=lk201.olk201-map.olk201-remap.o

#Filesthatarebothresidentandmodular:removefrommodular.

obj-m:=$(filter-out$(obj-y),$(obj-m))

#TranslatetoRules.makelists.

L_TARGET:=tc.a

L_OBJS:=$(sort$(filter-out$(export-objs),$(obj-y)))

LX_OBJS:=$(sort$(filter$(export-objs),$(obj-y)))

M_OBJS:=$(sort$(filter-out$(export-objs),$(obj-m)))

MX_OBJS:=$(sort$(filter$(export-objs),$(obj-m)))

include$(TOPDIR)/Rules.make

a)解釋

對Makefile的說明和解釋,由#開始。

b)編譯目標定義

類似于obj-$(CONFIG_TC)+=tc.o的語句是用來定義編譯的目標,是子目錄Makefile中重要的部分。編譯目標定義那些在本子目錄下,需要編譯到Linux內核中的目標文件列表。為了只在用戶選擇了此功能后才編譯,所有的目標定義都融合了對配置變量的判斷。

前面說過,每個配置變量取值范圍是:y,n,m和空,obj-$(CONFIG_TC)分別對應著obj-y,obj-n,obj-m,obj-。如果CONFIG_TC配置為y,那么tc.o就進入了obj-y列表。obj-y為包含到Linux內核vmlinux中的目標文件列表;obj-m為編譯成模塊的目標文件列表;obj-n和obj-中的文件列表被忽略。配置系統(tǒng)就根據(jù)這些列表的屬性進行編譯和鏈接。

export-objs中的目標文件都使用了EXPORT_SYMBOL()定義了公共的符號,以便可裝載模塊使用。在tc.c文件的部分,有"EXPORT_SYMBOL(search_tc_card);",表明tc.o有符號輸出。

這里需要指出的是,對于編譯目標的定義,存在著兩種格式,分別是老式定義和新式定義。老式定義就是前面Rules.make使用的那些變量,新式定義就是obj-y,obj-m,obj-n和obj-。Linux內核推薦使用新式定義,不過由于Rules.make不理解新式定義,需要在Makefile中的適配段將其轉換成老式定義。

c)適配段

適配段的作用是將新式定義轉換成老式定義。在上面的例子中,適配段就是將obj-y和obj-m轉換成Rules.make能夠理解的L_TARGET,L_OBJS,LX_OBJS,M_OBJS,MX_OBJS。

L_OBJS:=$(sort$(filter-out$(export-objs),$(obj-y)))定義了L_OBJS的生成方式:在obj-y的列表中過濾掉export-objs(tc.o),然后排序并去除重復的文件名。這里使用到了GNUMake的一些特殊功能,具體的含義可參考Make的文檔(infomake)。

d)include$(TOPDIR)/Rules.make

3.配置文件

3.1配置功能概述

除了Makefile的編寫,另外一個重要的工作就是把新功能加入到Linux的配置選項中,提供此項功能的說明,讓用戶有機會選擇此項功能。所有的這些都需要在config.in文件中用配置語言來編寫配置腳本,

在Linux內核中,配置命令有多種方式:

配置命令解釋腳本

Makeconfig,makeoldconfigscripts/Configure

Makemenuconfigscripts/Menuconfig

Makexconfigscripts/tkparse

以字符界面配置(makeconfig)為例,頂層Makefile調用scripts/Configure,按照arch/arm/config.in來進行配置。命令執(zhí)行完后產生文件.config,其中保存著配置信息。下再做makeconfig將產生新的.config文件,原.config被改名為.config.old

3.2配置語言

1)頂層菜單

mainmenu_name/prompt//prompt/是用'或"包圍的字符串,'與"的區(qū)別是'…'中可使用$引用變量的值。mainmenu_name設置層菜單的名字,它只在makexconfig時才會顯示。

2)詢問語句

bool/prompt//symbol/

hex/prompt//symbol//word/

int/prompt//symbol//word/

string/prompt//symbol//word/

tristate/prompt//symbol/

詢問語句首先顯示一串提示符/prompt/,等待用戶輸入,并把輸入的結果賦給/symbol/所代表的配置變量。不同的詢問語句的區(qū)別在于它們接受的輸入數(shù)據(jù)類型不同,比如bool接受布爾類型(y或n),hex接受16進制數(shù)據(jù)。有些詢問語句還有第三個參數(shù)/word/,用來給出缺省值。

3)定義語句

define_bool/symbol//word/

define_hex/symbol//word/

define_int/symbol//word/

define_string/symbol//word/

define_tristate/symbol//word/

不同于詢問語句等待用戶輸入,定義語句顯式的給配置變量/symbol/賦值/word/。

4)依賴語句

dep_bool/prompt//symbol//dep/...

dep_mbool/prompt//symbol//dep/...

dep_hex/prompt//symbol//word//dep/...

dep_int/prompt//symbol//word//dep/...

dep_string/prompt//symbol//word//dep/...

dep_tristate/prompt//symbol//dep/...

與詢問語句類似,依賴語句也是定義新的配置變量。不同的是,配置變量/symbol/的取值范圍將依賴于配置變量列表/dep/…。這就意味著:被定義的配置變量所對應功能的取舍取決于依賴列表所對應功能的選擇。以dep_bool為例,如果/dep/…列表的所有配置變量都取值y,則顯示/prompt/,用戶可輸入任意的值給配置變量/symbol/,但是只要有一個配置變量的取值為n,則/symbol/被強制成n。

不同依賴語句的區(qū)別在于它們由依賴條件所產生的取值范圍不同。

5)選擇語句

choice/prompt//word//word/

choice語句首先給出一串選擇列表,供用戶選擇其中一種。比如LinuxforARM支持多種基于ARMcore的CPU,Linux使用choice語句提供一個CPU列表,供用戶選擇:

choice'ARMsystemtype'\

"AnakinCONFIG_ARCH_ANAKIN\

Archimedes/A5000CONFIG_ARCH_ARCA5K\

Cirrus-CL-PS7500FECONFIG_ARCH_CLPS7500\

……

SA1100-basedCONFIG_ARCH_SA1100\

SharkCONFIG_ARCH_SHARK"RiscPC

Choice首先顯示/prompt/,然后將/word/分解成前后兩個部分,前部分為對應選擇的提示符,后部分是對應選擇的配置變量。用戶選擇的配置變量為y,其余的都為n。

6)if語句

if[/expr/]then

/statement/

...

fi

if[/expr/]then

/statement/

...

else

/statement/

...

fi

if語句對配置變量(或配置變量的組合)進行判斷,并作出不同的處理。判斷條件/expr/可以是單個配置變量或字符串,也可以是帶操作符的表達式。操作符有:=,!=,-o,-a等。

7)菜單塊(menublock)語句

mainmenu_optionnext_comment

comment'…..'

endmenu

引入新的菜單。在向內核增加新的功能后,需要相應的增加新的菜單,并在新菜單下給出此項功能的配置選項。Comment后帶的解釋就是新菜單的名稱。所有歸屬于此菜單的配置選項語句都寫在comment和endmenu之間。

8)Source語句

source/word/

/word/是文件名,source的作用是調入新的文件。

3.3缺省配置

Linux內核支持非常多的硬件平臺,對于具體的硬件平臺而言,有些配置就是必需的,有些配置就不是必需的。另外,新增加功能的正常運行往往也需要一定的先決條件,針對新功能,必須作相應的配置。因此,特定硬件平臺能夠正常運行對應著一個的基本配置,這就是缺省配置。

Linux內核中針對每個ARCH都會有一個缺省配置。在向內核代碼增加了新的功能后,如果新功能對于這個ARCH是必需的,就要修改此ARCH的缺省配置。修改方法如下(在Linux內核根目錄下):

備份.config文件

cparch/arm/deconfig.config

修改.config

cp.configarch/arm/deconfig

恢復.config

如果新增的功能適用于許多的ARCH,只要針對具體的ARCH,重復上面的步驟就可以了。

3.4helpfile

大家都有這樣的經驗,在配置Linux內核時,遇到不懂含義的配置選項,可以查看它的幫助,從中可得到選擇的建議。下面我們就看看如何給給一個配置選項增加幫助信息。

所有配置選項的幫助信息都在document.tion/Configure.help中,它的格式為:

description

variablename

helpfile

description給出本配置選項的名稱,variablename對應配置變量,helpfile對應配置幫助信息。在幫助信息中,首先簡單描述此功能,其次說明選擇了此功能后會有什么效果,不選擇又有什么效果,,不要忘了寫上"如果不清楚,選擇N(或者)Y",給不知所措的用戶以提示。

4.實例

對于一個開發(fā)者來說,將自己開發(fā)的內核代碼加入到Linux內核中,需要有三個步驟。首先確定把自己開發(fā)代碼放入到內核的位置;其次,把自己開發(fā)的功能增加到Linux內核的配置選項中,使用戶能夠選擇此功能;,構建子目錄Makefile,根據(jù)用戶的選擇,將相應的代碼編譯到終生成的Linux內核中去。下面,我們就通過一個簡單的例子--testdriver,結合前面學到的知識,來說明如何向Linux內核中增加新的功能。

4.1目錄結構

testdriver放置在drivers/test/目錄下:

$cddrivers/test

$tree

.

|--Config.in

|--Makefile

|--cpu

||--Makefile

|`--cpu.c

|--test.c

|--test_client.c

|--test_ioctl.c

|--test_proc.c

|--test_queue.c

`--test

|--Makefile

`--test.c

4.2配置文件

1)drivers/test/Config.in

#

#TESTdriverconfiguration

#

mainmenu_optionnext_comment

comment'TESTDriver'

bool'TESTsupport'CONFIG_TEST

if["$CONFIG_TEST"="y"];then

tristate'TESTuser-spaceinterface'CONFIG_TEST_USER

bool'TESTCPU'CONFIG_TEST_CPU

fi

endmenu

由于testdriver對于內核來說是新的功能,所以首先創(chuàng)建一個菜單TESTDriver。然后,顯示"TESTsupport",等待用戶選擇;接下來判斷用戶是否選擇了TESTDriver,如果是(CONFIG_TEST=y),則進一步顯示子功能:用戶接口與CPU功能支持;由于用戶接口功能可以被編譯成內核模塊,所以這里的詢問語句使用了tristate(因為tristate的取值范圍包括y、n和m,m就是對應著模塊)。

2)arch/arm/config.in

在文件的加入:sourcedrivers/test/Config.in,將TESTDriver子功能的配置納入到Linux內核的配置中。

4.3Makefile

1)drivers/test/Makefile

#drivers/test/Makefile

#

#MakefilefortheTEST.

#

SUB_DIRS:=

MOD_SUB_DIRS:=$(SUB_DIRS)

ALL_SUB_DIRS:=$(SUB_DIRS)cpu

L_TARGET:=test.a

export-objs:=test.otest_client.o

obj-$(CONFIG_TEST)+=test.otest_queue.otest_client.o

obj-$(CONFIG_TEST_USER)+=test_ioctl.o

obj-$(CONFIG_PROC_FS)+=test_proc.o

subdir-$(CONFIG_TEST_CPU)+=cpu

include$

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論