《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 解决方案 > 开放嵌入式:构建嵌入式Linux发行版的又一途径

开放嵌入式:构建嵌入式Linux发行版的又一途径

2012-02-01

作者:

Nick Lethaby

操作系统产品经理

Denys Dmytriyenko

Linux及开源技术专家

德州仪器

在本文中,德州仪器(TI) Nick Lethaby 和 Denys Dmytriyenko 概括介绍了开放嵌入式(OE,Open Embedded) Linux构建环境的重要元素,并展示了如何使用它们构建和定制Linux发行版。随着嵌入式处理器的功能日益强大,特性不断丰富,Linux操作系统在嵌入式应用中得到了迅速普及。尽管Linux的开源性与免许可证费用这一事实是其迅速普及的一个主要推动力,但另一个重要推动因素则是来自Linux在台式机与服务器领域广泛使用所提供的丰富应用软件与驱动。

然而,嵌入式开发人员并不能简单地将台式机Linux发行版或应用直接应用于他们的系统中。总的来说,嵌入式Linux开发人员面临着三大挑战:1. 根据其硬件使用的处理器及外设,装配一个具有引导加载程序、内核、库、应用以及开发工具组件的兼容组合解决方案;2. 正确交叉构建多兆字节镜像;3. 优化各种内核及用户级组件,缩小占位面积,降低相关存储器成本。

解决这三大挑战不是一件简单的事情,需要开发团队在积累丰富经验的基础上付出大量心血。商业嵌入式Linux厂商可为特定嵌入式处理器提供预先测试的解决方案,但对倾向于“凡事自己搞定”的Linux开发人员而言,开放嵌入式构建环境提供了一种为众多嵌入式设备构建可靠定制Linux发行版的方法。许多公司一直都在使用OE构建与完整发布版配套的嵌入式Linux内核端口,进而提高维护的支持水平并且增强OE基础架构的重要元素。

此外,数量不断增加的嵌入式Linux发行版(如Angstrom)也可充分利用OE的优势。虽然这些发行版并非OE的正式部件,但它们可通过为开发人员提供随时可投入运行的启动点增添使用OE的吸引力。值得注意的是诸如MontaVista与Mentor Graphics这些公司推出的较新商业发行版本现在也建立在OE基础之上。他们同时提供了更加丰富的工具与商业支持发行版。

在本文中,首先对OE构建环境的重要元素进行总体介绍,然后展示如何应用这些元素构建和定制Linux发行版。德州仪器以Angstrom发行版为基础的Arago发行版将被用作范例,来说明如何在OE及已经使用OE的发行版基础上创建新的发行版。

为了更加准确说明OE的重要概念,文中大多数基于Arago或Angstrom的范例脚本都略有改动。开发人员应访问文章最后列出的网站查看原始脚本,了解真实环境中的完整实施方案。

开发嵌入式的概览

OE建立在为嵌入式应用开发的交叉编译与构建工具BitBake基础之上。开发人员使用 BitBake是通过创建各种配置与方法文件来指示BitBake在这些文件上构建来源以及如何构建。OE实际上是这些方法(.bb)及配置(.conf)文件的数据库,开发人员可使用这些文件为各种嵌入式平台交叉编译各种组件组合。

OE提供成千上万个方法文件来构建单个数据包以及完整的镜像。数据包可以是从通过内核的引导加载程序到用户级应用或开发工具集的任何数据。方法文件不但知道数据包来源的位置,而且还知道如何为特定目标构建数据包,并确保数据相关项也完全构建。因此开发人员无需深入了解在其应用中增加特定功能所需的每一款软件详情。OE不但能够以各种数据包格式(tar、rpm、deb以及ipk)创建数据包,而且还可为发行版创建数据包源程序(package feed)。

大多数OE用户开始的时候会选择特定发行版,而不是构建单个数据包。使用现成发行版的优势在于通过选择一定的数据包版本便可获得可行的组合。发行版可提供这一重要功能。发行版除“最新”构建之外,通常还提供一个“稳定”构建,以避免试图将各种最新版本进行整合而导致的内在不稳定性。

OE的主要优势在于能够生成软件开发套件(SDK)。一些开发团队可能倾向于在OE中构建完整的应用,而另外的开发团队则可能会安排专门团队构建Linux平台供应用开发团队使用。在这种情况下,Linux平台团队可生成一个Linux发行版作为SDK,并便捷地集成于应用团队所倾向的构建流程中。这样,应用开发团队成员就不必深入了解OE了。

OE数据库还有一个重要方面,就是这个数据库的大部分维护工作都是由有志于确保Linux发行版在嵌入式设备上得到成功应用的相关方雇员来完成的。鉴于Linux内核及应用领域的大幅变动,这种维护工作的重要性不言而喻。

构建工具开发人员最为熟悉的命令是“make”,实现基于一套独立的make文件来生成单个项目。但是这种方法无法良好地扩展用于创建各种Linux发行版的任务,因为每个发行版都包含任意数量(往往有数百个)的大量数据包,其中许多大部分都是彼此独立的,且适用于任意数量的平台集。

由于这些局限的存在,Buildroot与BitBake等大量Linux发行版构建工具应运而生。BitBake的层级方法有助于各项数据包构建指令单独维护,而数据包本身则可便捷聚集,并按适当顺序进行编译并创建大型镜像。因此,BitBake可为二进制源程序及完整镜像中的后续整合构建单个数据包。

BitBake的一个重要方面是它没有强制规定单个数据包的构建方法。数据包的方法(或相关类型)可指定GNU autotool等任何构建工具,使之能够较为直接地将数据包导入OE。

为满足选择特定数据包版本来实现协同工作和指定不同嵌入式目标的需求,BitBake使用了配置文件。

BitBake使用方法中的位置信息通过下载工具(或诸如Git或svn等任何其它软件配置管理工具)从互联网提取数据包来源(如图1所示)。然后根据数据包描述中的规定,打上必要的补丁,这是在为嵌入式处理器交叉编译数据包时的一项常见要求。该数据包集在更高级的方法中有规定,比如用于镜像和任务的 方法。

(电子工程专辑)
图1:BitBake构建工具概览

许多开发人员都希望使用现有发行版。BitBake能够帮助开发人员在BBPATH搜索路径中尽早存入定制方法或配置文件,从而覆盖发行版中的默认值。这可帮助开发人员在不直接修改现有发行版文件(并随后维持现有发行版文件的定制版本)的条件下,针对其需求调整发行版。这种方法在 OE中称为“分层”,每个方法集称为“覆盖”。

下面来了解一些不同方法文件与配置文件,以便更加深入地掌控OE与BitBake的工作方式。首先介绍方法类型。

OE方法文件

OE方法文件不但能够以外壳脚本方式编写,而且还支持Python代码片段,总体上分为五类:

* 类型;

* 数据包;

* 任务;

* 镜像;

* 元。

这些都是按层级组织,镜像是最高层方法。镜像方法定义具体根文件系统镜像的内容。该方法只是定义构建镜像所需的各种任务。任务方法是构建相关特性或功能块所需的相关数据包集合。例如,用于智能手机的发行版可能具有数字音乐、数字摄像机以及通信录任务,汇集所有所需数据包来实现特定的功能。采用这种间接方法的原因之一是为了抽象任务中的各种数据包集,这样在不同的镜像中可以便捷地重复使用各种任务。

采用多个镜像文件提供各种功能/占位面积平衡,是发行版的常见做法。例如,Arago包含基本镜像、控制台镜像、数字视频演示镜像以及gstreamer镜像等。正如其镜像方法所示(见列表1),gstreamer镜像可通过构建4个不同的任务方法来创建。其它镜像方法也很类似,比如基本镜像只是省略了控制台、dvsdk以及gstreamer任务,换了一个镜像名称,并具有不同的根文件系统大小而已。

列表1:镜像recipe示例。

# Arago GStreamer image

# gives you an image with DVSDK libs and GStreamer demo

require arago-image.inc

?

COMPATIBLE_MACHINE = "(?!arago)"

?

# The size of the uncompressed ramdisk is 150MB

ROOTFS_SIZE = "153600"

IMAGE_INSTALL += "\

task-arago-base \

task-arago-console \

task-arago-dvsdk \

task-arago-gst \

"

export IMAGE_BASENAME = "arago-gst-image"

任务方法代表各种单个数据包集合。例如,arago的基本任务可构建大约15个数据包。该方法(见列表2)引入了一些需要熟知的标准BitBake变量。PR意为数据包修订版,即数据包方法文件的版本号。PN代表数据包名称,而PV(此处未使用)则代表数据包版本,即实际数据包源文件版本。各种任务没有指定大多数数据包版本,因为这些是在配置文件中设置的。

列表2:任务recipe示例。

DESCRIPTION = "Basic task to get a device booting"

LICENSE = "MIT"

PR = "r9"

inherit task

# these can be set in machine config to supply packages needed to get machine booting

MACHINE_ESSENTIAL_EXTRA_RDEPENDS ?= ""

MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS ?= ""

ARAGO_ALSA_BASE = "\

???? alsa-lib \

???? alsa-utils-aplay \

???? "

ARAGO_BASE = "\

${ARAGO_ALSA_BASE} \

ldd \

mtd-utils \

curl \

arago-feed-configs \

initscript-telnetd \

devmem2 \

"

# minimal set of packages - needed to boot

RDEPENDS_${PN} = "\

base-files \

base-passwd \

busybox \

initscripts \

modutils-initscripts \

netbase \

update-alternatives \

module-init-tools \

${ARAGO_BASE} \

${MACHINE_ESSENTIAL_EXTRA_RDEPENDS} \

"

RRECOMMENDS_${PN} = "\

${MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS} \

"

数据包方法可满足每个数据包的特定需求。列表3是基于GStreamer的应用相对简单的示例,显示了数据包方法的一些重要功能。构建机制可按autotool与pkgconfig类型指定,相关性以及数据包版本及修订号均可识别,而且还提供数据包来源位置。

列表3:数据包recipe示例。

DESCRIPTION = "gstd: a GStreamer-based streaming server"

HOMEPAGE = "http://sourceforge.net/projects/harrier/"

LICENSE = "BSD"

SECTION = "multimedia"

PRIORITY = "optional"

inherit autotools pkgconfig

DEPENDS = "dbus dbus-glib gstreamer"

RDEPENDS_${PN} = "dbus dbus-glib gstreamer gst-plugins-base"

RRECOMENDS_${PN} = "gstreamer-ti"

SRCREV = "f3e22c93f4fd7ca47d6309b8450788127550ecb9"

PV = "1.0"

PR = "r13"

PR_append = "+gitr${SRCREV}"

SRC_URI = "git://gstd.git.sourceforge.net/gitroot/gstd/gstd;protocol=git \"

S = "${WORKDIR}/git"

# We don't want to run autoconf or automake, unless you have

# automake > 1.11 with vala support

do_configure() {

oe_runconf

}

FILES_${PN} += "${datadir}/dbus-1/*/*.service"

FILES_${PN}-dev += "${datadir}/dbus-1/interfaces/*.xml"

在数据包方法(见列表3)中,有一行写着“继承 autotools pkgconfig”。这一行正在使用已有的方法类型—BitBake类型。类型与其它方法是同级关系而非层级关系。类型可通过“继承(inherit)”命令提取可供以后重复使用的通用方法元素。最常用的类型将“继承”方法的功能或各个部分,支持GNU autotool等最常用的构建工具。列表4就是用于pkgconfig工具(pkgconfig.bbclass) 的类型示例。

列表4:类型recipe示例。

inherit base

DEPENDS_prepend = "pkgconfig-native "

do_install_prepend () {

for i in `find ${S}/ -name "*.pc" -type f` ; do \

sed -i -e 's:-L${STAGING_LIBDIR}::g' $i

done

}

do_stage_append () {

install -d ${PKG_CONFIG_DIR}

for pc in `find ${S} -name '*.pc' -type f | grep -v -- '-uninstalled.pc$'`; do

pcname=`basename $pc`

cat $pc > ${PKG_CONFIG_DIR}/$pcname

done

}

OE 配置文件

配置文件主要分为两类:设备配置与发行版(distro)配置。另外还有本地配置文件以及名为“bitbake.conf”的文件。Bitbake.conf是BitBake读取的第一个文件,包含所有其它配置文件。此外,它还可定义许多全局变量,并称之为bitbake.conf。不建议直接修改 bitbake.conf,建议在distro配置文件或本地配置文件中进行覆盖。设备配置文件可定义特定目标电路板。发行版配置文件可为一台以上的设备定义特定的Linux发行版(比如各种数据包版本)。

发行版配置文件是进行全局设置的最佳场合,该设置可应用于发行版生成的所有镜像。OE可帮助开发人员根据具体镜像或数据包覆盖这些设置,从而高度灵活地处理特殊情况。例如,本地配置(local.conf)文件往往用于放置用户专用配置,以适度修改默认发行版配置的设置。在完成对发行版配置文件和设备配置文件的更详细讨论后,我们将从总体上了解在Arago local.conf文件中所做的其它配置设置。

该发行版配置文件可设定各种基本“内务处理”参数(见列表5),如发行版本名称、下载源文件和存储所构建数据包的目录以及uImage的支持文件格式等。

列表5:配置文件摘要

# For now Arago is not big enough to warrant a separate distribution,

# reuse Angstrom, but set the name to Arago

DISTRO = "angstrom-2008.1"

# Set the distro name and version, since we now produce own SDK

DISTRO_NAME = "Arago"

DISTRO_VERSION = "2010.05"

BUILDNAME = "${DISTRO_NAME} ${DISTRO_VERSION}"

# Use this to specify where BitBake should place the downloaded sources into

DL_DIR = "${SCRATCH}/downloads"

# Put resulting images and packages in deploy directory outside of temp

#DEPLOY_DIR = "${OEBASE}/arago-deploy"

……

# Add the required image file system types below. Valid are

# jffs2, tar(.gz|bz2), cpio(.gz), cramfs, ext2(.gz), ext3(.gz)

# squashfs, squashfs-lzma

IMAGE_FSTYPES = "jffs2 tar.gz ext2.gz"

此外,该发行版配置文件还可指定构建发行版的目标设备,尽管每台设备启动Linux的详细信息都存放在设备配置文件中。由于发行版配置文件所处的位置由工具链版本决定,因此支持大量设备可能会使文件变得更加复杂。例如,可能需要指定多个大数据包补丁或不同的工具链版本来适应各种不同的设备。其它需要在发行版配置文件中指定的架构相关项包括硬件与软件浮点、是否支持不同的指令集(ARM或Thumb)、以及可能不适应发行版中包含的某些数据包的各类寻址方式。

选择数据包版本是发行版配置文件中较为重要的常见功能之一,以确保所使用的已知兼容性版本。选择版本的方法很多。如果没有指定版本,就选择最新版本。同样,也可指定某种“从不落后于”最新版本的版本。对于需要特定版本的数据包,可为发行版配置文件中的整个发行版指定默认首选版本,如列表6的Angstrom发行版配置文件摘要所示。

列表6:发行版配置文件摘要

ANGSTROM_QT_VERSION ?= "4.6.2"

CE_VERSION ?= "latest"

PREFERRED_VERSION_autoconf = "2.65"

PREFERRED_VERSION_autoconf-native = "2.65"

PREFERRED_VERSION_automake-native = "1.10.3"

PREFERRED_VERSION_busybox?????? = "1.13.2"

PREFERRED_VERSION_glib-2.0????? = "2.24.0"

PREFERRED_VERSION_glib-2.0-native = "2.24.0"

发行版的另一项重要功能是提供一套源程序,以便访问预先构建的二进制数据包。发行版一般使用二进制源程序在运行时动态加载新的数据包。为稳定可靠的添加数据包,发行版还整合了一个数据包管理系统。例如,Angstrom发行版在开始时使用ipkg软件包管理器(近期转向opkg),因为与dpkag或RPM等桌面数据包管理技术相比,其在满足空间有限的嵌入式应用需求方面具有一定的优势。Ipk格式原本建立在deb格式基础之上,可用ipkg或opkg数据包管理器处理。相比之下,opkg更新,因此也更有优势。数据包格式会使更多的元数据存储在数据包中。这样能够避免加载不兼容的数据包,并确保带来正确运行时间的相关项。Arago可定义其预先构建的IPK源程序(见列表7),但可能不具有最为广泛的数据包。在需要更多数据包时,可启用Angstrom源程序,但是在混合使用不同源程序时需要更加小心谨慎。

列表7:发行版配置文件摘要

ANGSTROM_PKG_FORMAT ?= "ipk"

require conf/distro/include/

angstrom-package${ANGSTROM_PKG_FORMAT}.inc

#Use this variable to select which recipe you want to use to

#get feed configs (/etc/ipkg/, #/etc/apt/sources.list). Useful

#for derivative distros and local testing

ANGSTROM_FEED_CONFIGS = "arago-feed-configs"

# Feed configuration

ARAGO_URI = "http://feeds.arago-project.org"

ARAGO_FEED_BASEPATH = "feeds/live/${ANGSTROM_PKG_FORMAT}"

DISTRO_FEED_URI = "${ARAGO_URI}/${ARAGO_FEED_BASEPATH}"

在发行版配置文件中可以设置其它通用构建参数。例如,开发人员可将具有更多调试信息或配置文件信息的已完成构建流程指定为标准流程。这些代码行可通过“生产代码”镜像方法注释掉或覆盖,从而可提高最终生产版本的性能

列表8:发行版配置文件摘要

# Comment these two out if you want BitBake to build

# production images.

DEBUG_BUILD = "1"

INHIBIT_PACKAGE_STRIP = "1"

# Build a package such that you can use gprof to profile it.

PROFILE_OPTIMIZATION = "-pg"

SELECTED_OPTIMIZATION = "${PROFILE_OPTIMIZATION}"

LDFLAGS =+ "-pg

设备配置文件可定义在电路板上引导Linux所需的基本要素。例如,ARM926或ARM Cortex-A8等目标CPU的定义,提供了Linux内核的优先方法提供方以及相应引导加载程序,如列表9所示,显示了TI ARM9视频器件的DM365设备配置文件。

列表9:设备配置文件

#@TYPE: Machine

#@NAME: DM365 CPUs on a Davinci DM365 EVM board

#@DESCRIPTION: Machine configuration for the TI Davinci

DM365 EVM board

require conf/machine/include/dm365.inc

require conf/machine/include/tune-arm926ejs.inc

# Increase this everytime you change something in the kernel

MACHINE_KERNEL_PR = "r45"

TARGET_ARCH = "arm"

KERNEL_IMAGETYPE = "uImage"

PREFERRED_PROVIDER_virtual/kernel = "linux-davinci-staging"

PREFERRED_PROVIDER_virtual/bootloader = "u-boot"

UBOOT_MACHINE = "davinci_dm365_evm_config"

UBOOT_ENTRYPOINT = "0x80008000"

UBOOT_LOADADDRESS = "0x80008000"

EXTRA_IMAGEDEPENDS += "u-boot"

SERIAL_CONSOLE ?= "115200 ttyS0"

EXTRA_IMAGECMD_jffs2 = "--pad --little-endian

--eraseblock=0x20000 -n"

#ROOT_FLASH_SIZE = "29"

MACHINE_FEATURES = "kernel26 serial ethernet usbhost

usbgadget mmc alsa"

每款电路板都需要设备配置文件。实际上,由于使用相同或类似器件的不同电路板的配置步骤通常相同,通用步骤可以合并到包含文件中。这样通过便于维护的无错误方法来简化新设备配置文件的创建。在以上示例中,我们了解了部分包含文件,并更深入地了解了整个设备配置文件的面貌。

虽然Linux应用一般都具有很大的占位面积,但对任何单个应用的需求将明显低于通常为嵌入式发行版所默认的桌面发行版。因此开发人员通常需要尽量缩小存储器占位面积或进行某些其它定制。

嵌入式开发人员面临的另一项重大挑战是Linux软件领域的快速变革。尽管首先要考虑的是不能落后于最新内核版本太多,以免给向后移植补丁带来困难,有时候开发人员仍需要锁定工具链、内核以及将要通过测试并投入生产的应用数据包版本。这样可能就需要创建某些定制方法或配置文件,以便发行版选择不同的组件版本。在对预先存在的发行版进行定制之前,需要花些时间了解该发行版可能提供的“现成”选项。例如,Angstrom可提供具有各种占位面积的稳定开发分支及构建。如果这些选项不能满足特定应用需求,随后就需要进行某些程度的定制。

TI推动Arago发展的原因之一是实现了占位面积的缩小与可扩展性。另一个原因是支持GPLv3许可证软件以及任何加密软件的公司法律问题,这要求与出口控制规定相关的其它合规工作。接下来将讨论Arago中引入的部分方法及配置文件,并说明如何从现有发行版中导出定制发行版。

为解决占位面积与可扩展性问题,Arago创建了其自己的镜像与任务 方法集,其中一部分已经讨论过了。对许多应用而言,这样已经足够。但是对Arago而言,就必须自行修改部分数据包的方法。虽然最初在Arago中所做的大量修改将用于修复方法中的普通漏洞,以避免其进入上游的标准OE与Angstrom文件,但还是要注意避免GPLv3或加密所要求的那些不适合推向上游的变更。例如,由于SSH内含加密功能,我们不得不将其从发行版中移除。这可连带影响 busybox方法,它需要启用独立远程登录后台程序,通过其默认配置“defconfig”文件远程访问外壳脚本。因为后续需要维护修改过的 方法,使之能随主方法的变更而变更,这种方法仅建议在没有其他方法可以使用的情况下使用。

可以通过强制选择在引入GPLv3许可证或任何该许可证下发行的补丁之前发行的gdb与gdbserver版本来清除GPLv3内容。除了选择特定GBD版本外,Arago还可选用CodeSourcery工具链的预构建版本。这样就减少了用户的初期构建时间,因为这样无需通过源程序构建这些工具。

这种方法不是修改Angstrom配置文件或方法文件,而是在本地配置文件中选择定制Arago的GCC选择。这样可以覆盖发行版配置文件中的优先版本。如列表10所示,Arago本地配置文件可设置各种gdb相关组件的最新优先版本,禁用SSH包含,从而可避免出现加密软件。需要注意的是,使用预构建GCC二进制文件而不从源程序中构建,需要能够在OE中添加和修复外部工具链,而并非简单地选择特定版本。

列表10:本地配置文件摘要

# Set some preferences

PREFERRED_PROVIDER_update-alternatives-cworth =

"update-alternatives-cworth"

PREFERRED_PROVIDER_ncurses-tools = "ncurses"

PREFERRED_PROVIDER_gdbserver = "gdbserver"

PREFERRED_VERSION_gdbserver = "6.6"

PREFERRED_VERSION_gdb = "6.6"

PREFERRED_VERSION_gdb-cross-sdk = "6.6"

PREFERRED_PROVIDER_libopkg-dev = "opkg-nogpg"

# Disable DropBear for now due to export restrictions

DISTRO_SSH_DAEMON = ""

虽然本地配置文件是Arago中选择的主要定制方法,开发人员也可使用BitBake搜索路径插入自己的方法文件,覆盖已有发行版中的部分选择。

OE的不足之处

虽然OE是一种功能强大的工具,但其学习过程非常漫长,构建错误的根源可能很难发现。不熟悉OE的开发团队至少需要几周时间才能达到中等熟悉程度。因此,除非能找到之前有过OE经验的开发人员,否则在现有项目中使用OE或者在日程要求紧张的项目中首次使用OE都是不提倡的。

新推出的商业工具旨在解决某些易用性问题,但都要收取许可证费。此外,Narcissus项目还可为嵌入式设备构建基于Angstrom的根文件系统提供基于web的易用型前端。

第二个不足之处是构建时间。在开发人员首次使用OE构建发行版时,需要一切从源程序开始构建,其中包括工具链以及额外的主机工具等。因此初期构建非常耗时,并且需要设备至少支持千兆字节RAM。当然,后续构建的速度会显著提升,因为工具链等众多组件都无需重复构建。如果增量构建仍然不能满足时间要求,使用OE生成一个能够便捷地与其它构建工具配合使用的SDK则是一个不错的选择。

结论

OE提供一种创建嵌入式Linux发行版的途径,该发行版可通过预测试数据包构建方法的大型数据库得到充分利用。这可显著缩短创建新发行版所需的时间。此外,开发人员还能够以大量已有发行版为出发点,创建自己的发行版。OE不但功能强大,而且还具有高度的灵活性,能够帮助开发人员根据自身特定应用需求量身定制发行版。但这种高性能与高灵活性的另一面是学习难度增大。因此,在项目日程过紧的情况下开发OE专业技术并不是最佳选择。

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。