双系统OTA升级
一 开发板介绍
开发板使用 RD_AD100_EVB_V1.0
二 OTA 原理和介质介绍
2.1 OTA简介和实现方法
君正OTA简单来说分为三个步骤.
第一步:将需要更新的升级固件放到OTA升级服务器上.
第二步:通过网络将升级固件传输到设备上.
第三步:设备利用得到的OTA升级固件更新到自身的设备上,然后重启切换到更新好的系统上,从而完成OTA整个流程.
君正基于Linux 系统, 目前OTA 升级的策略是采用双系统备份, 也就是一台设备上同时含有两套系统, 每次只运行在其中一套系统上. 其中共用一个uboot,有两份kernel和rootfs文件系统. 这样不管OTA升级是否成功,都可以保证当前的系统是正常的.另外若升级分区有问题,会通过wdt切换到原有分区.
2.2 OTA 存储介质
君正OTA目前支持的存储介质包含两种:mtd 和 mmc两种. 其中mtd包含:nand 和 nor,本教程主要为mtd介质(nor)的OTA升级介绍.
三 ota 编译配置
3.1 uboot 添加支持 ota 功能
以RD_AD100_EVB_V1.0开发板编译配置为例,编译出的uboot固件需要支持ota功能:
首先需要确定你当前的编译配置使用的是那个uboot的编译配置文件,通过如下方法可以确定。使用工具 tools/iconfigtool/IConfigToolApp$ ./IConfigTool
选择Config.in文件:`build/Config.in`
选择Config文件: build/configs/ad100_evb_v10_nor_5.10_defconfig
如上图,根据uboot相对路径和默认配置,可以确定uboot编译的配置文件为 bootloader/uboot-x2000/boards.cfg
的 ad100_base_xImage_sfc_nor
,在文件 boards.cfg
中搜索ad100_base_xImage_sfc_nor
, 添加SPL_OS_OTA_BOOT
的标志, 有这个标志表示uboot支持ota的功能.
ad100_base_xImage_sfc_nor mips xburst2 ad100_base ingenic ad100 ad100_base:AD100N_DDR,SPL_SFC_NOR,ENV_IS_IN_SFC,MTD_SFCNOR,SPL_OS_BOOT,NOR_SPL_BOOT_OS,RMEM_MB=8,SPL_OS_OTA_BOOT
3.2 spl添加wdt
修改uboot配置文件ad100_base.h,位于bootloader/uboot-x2000/include/configs目录下
#ifndef __AD100_BASE_H__
#define __AD100_BASE_H__
#define CONFIG_ROOTFS_SQUASHFS
#define CONFIG_ROOTFS2_SQUASHFS
#define CONFIG_ARG_QUIET
#define CONFIG_SPL_SERIAL_SUPPORT
/* 添加以下宏 */
#define CONFIG_JZ_WATCHDOG
#include "ad100_base_common.h"
#ifdef CONFIG_AD_SLT
#include "ad100_slt_common.h"
#endif
#endif /* __AD100_BASE_H__ */
修改WATCHDOG时间位置
/bootloader/uboot-x2000$ ls ./include/configs/ad100_base_common.h
#ifdef CONFIG_JZ_WATCHDOG
#define CONFIG_HW_WATCHDOG
#define CONFIG_WDT_TIMEOUT_BY_MS (20*000) //修改时间
#endif
3.3 添加wdt驱动
四 制作OTA升级固件
以RD_AD100_EVB_V1.0开发板编译配置为例制作OTA升级固件. 使用ota升级为联网升级.
4.1 ota 相关配置
使用工具 tools/iconfigtool/IConfigToolApp$ ./IConfigTool
,选择配置文件:
build/configs/ad100_evb_v10_nor_5.10_defconfig
配置如下:
从上往下,每项宏的配置对应的功能如下所示:
1:Storage medium(存储介质)(APP_ota_updater_storage_medium):
//板子的存储介质,包含两种类型,nand和mmc,可以根据自己的设备存储介质选择不同的类型
2:当前系统的ota版本号(每次编译新的ota版本时都应该递增此值)(APP_ota_updater_version)
//当前ota的版本号,会写入文件系统的 /etc/ota_info 'ota_version=N'
该版本号是指打包升级包的版本号,如果你需要制作版本号为3,该处需要修改为3
3:ota 升级服务器的地址(App_ota_updater_site)
//ota的服务器地址路径,会写入文件系统的 /etc/ota_info 'ota_site=...'
4:ota 分包大小(App_ota_updater_block_size)
//ota分包大小,制作ota镜像时会将ota镜像按此大小分包
5:ota kernel 镜像路径(App_ota_updater_kernel_img_path)
//ota kernel镜像路径,制作ota镜像时需要
6:ota rootfs 镜像路径(App_ota_updater_rootfs_img_path)
//ota rootfs镜像路径,制作ota镜像时需要
4.2 编译 ota 升级包固件
使用配置 build/configs/ad100_evb_v10_nor_5.10_defconfig
进行整体编译
build$ make ad100_evb_v10_nor_5.10_defconfig
build$ make
工程/build/output/ota" 目录下会生成ota镜像文件:
cd output/ota/
ls
#保存 rootfs.squashfs.000*的md5sum 用于下载后设备校验
ota_md5_rootfs.squashfs.ba2445906b32c0a76a328c668de3e314
#保存xImage.000*的md5sum 用于下载后设备校验
ota_md5_xImage.c6cf35364c693e9ff0d5591fa17f5211
#镜像文件拆分的配置
ota_update.in
#"rootfs.squashfs.*"表示被拆分后的buildroot镜像文件
rootfs.squashfs.0000.ba2445906b32c0a76a328c668de3e314
rootfs.squashfs.0001.ae14ffbab09723d85d369676c208d874
rootfs.squashfs.0002.e93e4b17de3c622de1897803fe8cd826
rootfs.squashfs.0003.2786e5dec5583a2ec0d47d958dcf74c5
rootfs.squashfs.0004.6f490d17fd337aa8248e5c5eba3c8d43
rootfs.squashfs.0005.9dc8fc88a87670d987df147a89a989c6
#"xImage.*"表示被拆分后的kernel镜像文件
xImage.0000.c6cf35364c693e9ff0d5591fa17f5211
xImage.0001.88c3b71917b8cd4b86821762c005437d
xImage.0002.67feb4595d9e5ce64b63b2cb1d5d1931
xImage.0003.871b5ea8e7c720c02eee91db4716e899
xImage.0004.93269997d2ff305861c672aa263cdb1e
其中 ota_update.in文件
build$ cat output/ota/ota_update.in
ota_version=0
img_type=kernel
img_name=xImage
img_size=4771904
img_md5=c6cf35364c693e9ff0d5591fa17f5211
img_type=rootfs
img_name=rootfs.squashfs
img_size=5767168
img_md5=ba2445906b32c0a76a328c668de3e314
#关键字 ota_version:做版本检查,避免忙中出错和实际版本不一致
#关键字 img_type:定义升级的内容类型:kernel rootfs
#关键字 img_name:升级的文件名,在升级包目录的文件名前缀
#关键字img_size:升级文件的总大小,因为升级文件会被拆分成多个文件,默认按1Mbyte拆分
#关键字img_md5:升级文件的md5sum值
五 OTA 烧录工具配置
以RD_AD100_EVB_V1.0开发板 OTA 烧录为例, 进行烧录工具的说明. 配置如下:
上面 kernel和kernel2 烧录的是同一个kernel固件, rootfs 和 rootfs2 烧录的是同一个rootfs固件. 第一次烧录两份文件系统是为了后面ota升级备用使用的.每次板子都会只运行在正常的文件系统上面,保证板子能够正常运行.
填写label和选择ops后会自动补齐offset,均需要与sfc中的分区信息一致
ota 分区烧录的文件可以为一个空文件.
userdata为/usr/data下挂载的分区,根据需要设置大小与文件
六 OTA 测试服务器搭建方法(仅供参考)
君正不提供服务器, 客户需自己解决, 以下服务器搭建仅供测试使用. ota的升级文件在设备端, 能被wget命令获取即可.
6.1 安装 apache2 服务器,用于测试ota升级
安装命令: sudo apt-get install apache2
6.2 启动 apache2 服务器
启动命令: service apache2 start
电脑端放置OTA升级固件的目录:/var/www/html/
。
注:如果设备端无法访问apache2服务器,有可能是防火墙的问题。
可以关掉电脑端的防火墙再进行测试,命令如下:
暂时关闭防火墙:
systemctl stop firewalld
七 OTA服务器文件部署
以RD_AD100_EVB_V1.0
开发板为例, 将版本升级至1, 我的电脑的地址是10.4.3.9 , 所以地址:http://10.4.3.9//ota/board_test/
用于存放ota相关的文件,对应于服务器本机的路径为:/var/www/html/ota/board_test
, 此处与4.1章节ota配置保持一致.
该目录下的文件需要自己添加:
cd /var/www/html/ota/board_test/
ls -l
ota_config.in #定义当前最新版本的ota版本
ota_v1 #版本为1对应的ota升级文件夹
1 添加ota_config.in文件,该文件内容如下:
/*
关键字 current_version 用于定义当前最新的ota的版本
当`current_version=1`时,对应于目录`ota_v1/`存放ota的升级信息和文件
*/
current_version=1
2 ota_v'N'/目录 ('N'是数字,这里添加ota_v1),需要自己手动添加,并将编译好的ota升级镜像文件复制到该文件夹
之前已编译过ota版本为0的系统,这里升级为1版本, 需要重新编译ota版本为1的系统, 即iconfig需要修改ota版本号
cp output/ota/* /var/www/html/ota/board_test/ota_v1/ -arf #编译完ota版本为1的系统,复制ota镜像文件到服务器
3 手动创建 ota_v1.ok 到 /var/www/html/ota/board_test/ota_v1/ 文件夹下, 最终确定 ota_v1版本发布
ota_v1.ok (1是版本号 ota_v'N'.ok 且该文件的内容为空即可)
此文件是做 ota 的最后一步确认,制作ota镜像的时候不会有这个文件产生,这个文件需要客户自己手动创建生成,以确定此版本的ota验证是成功可行的,可以提供给外部的设备端进行下载更新.设备端如果检测到此文件才会启动ota升级流程
所以/var/www/html/ota/board_test/ota_v1
文件夹的内容如下:
bhu@bhu-PC:/var/www/html/ota/board_test/ota_v1$ ls -lh
总用量 11M
-rw-r--r-- 1 bhu bhu 198 12月 2 17:11 ota_md5_rootfs.squashfs.292d5d562c2f009b615fe0214a36d8fc
-rw-r--r-- 1 bhu bhu 165 12月 2 17:11 ota_md5_xImage.86604d0d204dcfff3f071f431388c13e
-rw-r--r-- 1 bhu bhu 206 12月 2 17:11 ota_update.in
-rw-r--r-- 1 root root 0 12月 2 17:13 ota_v1.ok
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 rootfs.squashfs.0000.292d5d562c2f009b615fe0214a36d8fc
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 rootfs.squashfs.0001.5e94ae309e74ac18fa2a5380f027b54a
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 rootfs.squashfs.0002.b60f20c8927b12e351340620fb9ad728
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 rootfs.squashfs.0003.0a06f6e09ab035f13986350669c0c375
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 rootfs.squashfs.0004.6af5ef60e753496cce44f83a4f8f3c99
-rw-r--r-- 1 bhu bhu 512K 12月 2 17:11 rootfs.squashfs.0005.0d0f226bba55a01f9a6d8bbe0d73dada
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 xImage.0000.86604d0d204dcfff3f071f431388c13e
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 xImage.0001.42dedb487d96b7766aeaaa258b7f0113
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 xImage.0002.67feb4595d9e5ce64b63b2cb1d5d1931
-rw-r--r-- 1 bhu bhu 1.0M 12月 2 17:11 xImage.0003.871b5ea8e7c720c02eee91db4716e899
-rw-r--r-- 1 bhu bhu 565K 12月 2 17:11 xImage.0004.93269997d2ff305861c672aa263cdb1e
八 OTA 升级的流程
8.1 ota 升级脚本
调用ota升级脚本,ota的升级脚本在 "/etc/ota_bin/" 目录下,ota的配置在文件 "/etc/ota_info" 中,ota_info的内容如下:
ota_version=0 #表示设备端当前的版本号
ota_site=http://10.4.3.9/ota/board_test #表示ota升级的服务器地址
ota的升级脚本文件:
# ls /etc/ota_bin/ -l
-rwxr-xr-x 1 root root 14147 Dec 2 2024 local_ota_update.sh
-rwxr-xr-x 1 root root 8313 Dec 2 2024 main_os_info_check.sh
-rwxr-xr-x 1 root root 13825 Dec 2 2024 network_main_os_update_recovery.sh
-rwxr-xr-x 1 root root 14378 Dec 2 2024 network_ota_update.sh
-rwxr-xr-x 1 root root 14014 Dec 2 2024 network_recovery_update_main_os.sh
-rwxr-xr-x 1 root root 418 Dec 2 2024 ota_clear_flag.sh
-rwxr-xr-x 1 root root 2596 Dec 2 2024 ota_img_data_provider.sh
-rw-r--r-- 1 root root 4161 Dec 2 2024 ota_local_method.sh
-rwxr-xr-x 1 root root 1077 Dec 2 2024 ota_update_kernel.sh
-rwxr-xr-x 1 root root 1077 Dec 2 2024 ota_update_rootfs_squashfs.sh
-rwxr-xr-x 1 root root 1053 Dec 2 2024 ota_update_rtos_bin.sh
-rw-r--r-- 1 root root 5809 Dec 2 2024 ota_utils.sh
-rw-r--r-- 1 root root 727 Dec 2 2024 recovery_version_utils.sh
执行sh /etc/ota_bin/network_ota_update.sh
"network_ota_update.sh" 脚本的升级过程如下:
1 检查是否设置过下次启动项(检查文件是否存在/tmp/ota_boot_set),如未设置过,则继续
2 下载测试服务器的 ota_config.in文件,解析当前ota版本号
3 获取/etc/ota_info 中的版本号并与服务器ota版本号做对比,如果服务器更高,则继续
4 下载测试服务器 ota_v1/ota_v1.ok 文件,如果无法下载,说明当前v1版本还没有准备好,则退出ota升级流程
5 下载测试服务器 ota_v1/ota_update.in 文件
6 获取 ota_version=字段,如果和当前版本不相等,则退出ota升级流程
7 获取img_type img_name img_size img_md5 字段,并且验证和保存
ota_kernel ota_kernel_name ota_kernel_size ota_kernel_md5
ota_rootfs ota_rootfs_name ota_rootfs_size ota_rootfs_md5
8 获取需要升级的kernel rootfs分区,检查kernel分区大小,检查rootfs 分区大小是否满足升级包要求
9 开始升级kernel
10 启动升级脚本 /etc/ota_bin/ota_update_kernel.sh
11 调用download_ota_img 函数下载 xImage 相关文件,校验 md5值
12 去掉文件 md5 后缀,生成 xImage.000N,再生成 xImage.000N.ok 通知升级脚本读取 xImage.000N数据
13 继续下载新的 xImage.000N+1.md5文件,等待xImage.000N.done产生,并删除 xImage.000N
14 继续11的步骤,直到xImage 相关文件下载完成
15 等待xImage.ok 生成,表示升级脚本成功,或者 xImage.failed 表示升级脚本失败
16 按前面的步骤升级rootfs
17 如果升级成功,则设置启动分区为刚才升级的分区,返回值0
18 如果什么都没有升级,也没有出错,则返回值2,中间任意步骤出错,返回1
8.2 ota 启动分区
设置ota启动分区
若升级成功会自动设置启动分区为升级的分区,若想设置回来可以设置ota分区的内容,uboot会检查ota分区的内容,以决定启动哪个分区:
#擦除ota分区的一块数据
flash_erase /dev/mtd5 0 1
#写入启动分区名 ota:kernel2 或者 ota:kernel
printf "%-256s" "ota:kernel2" | nandwrite -s 0 -p /dev/mtd5 -
#查看当前的启动分区名
nanddump -s 0 -l 256 /dev/mtd5 -a
#注意:/dev/mtd5 对应ota分区,可用"cat /proc/mtd"命令查看
8.3 分区错误切换
若升级到新分区,分区损坏,会切换到旧分区
U-Boot SPL 2013.07-00267-g1649e06eb-dirty (Dec 02 2024 - 17:11:10)
V1.0.0 <2024/06/25>
ERROR EPC 83f9c824
CPA_CPAPCR:0300490d
CPM_CPMPCR:0320490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a073310
DDR clk rate 600000000
DDR: M14F5121632A type is : DDR2
DDR_PAR of eFuse: 00000000 00000000
AD100 InnoPhy skew Settings...
GD25B512ME 00c8471a 00c8471a //升级切换到新分区,损坏无法启动
U-Boot SPL 2013.07-00267-g1649e06eb-dirty (Dec 02 2024 - 17:11:10)
V1.0.0 <2024/06/25>
ERROR EPC 807a58dc
CPA_CPAPCR:0300490d
CPM_CPMPCR:0320490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a073310
DDR clk rate 600000000
DDR: M14F5121632A type is : DDR2
DDR_PAR of eFuse: 00000000 00000000
AD100 InnoPhy skew Settings...
GD25B512ME 00c8471a 00c8471a
ota fail! //失败,切换到原分区
Uncompressing Linux...
Ok, booting the kernel.
[ 0.000000] Linux version 5.10.186+ (bhu@bhu-PC) (mips-linux-gnu-gcc (Ingenic Linux-Release5.1.9.sr02-Default_xburst2_glibc2.29 Fix: uclibc0.9.33.2 e6b2efb7a460ae93975997edb4fb1658230c5bec 2023.12-21 04:08:14)4
[ 0.000000] CPU0 RESET ERROR PC:807A58DC
[ 0.000000] [<807a58dc>] __down_killable+0xb0/0x128
[ 0.000000] printk: bootconsole [early0] enabled
[ 0.000000] CPU0 revision is: 00132000 (Ingenic XBurst II)
[ 0.000000] FPU revision is: 00f32000
[ 0.000000] MSA revision is: 00002000
[ 0.000000] ddr_size: 64 MBytes
[ 0.000000] OF: fdt: No chosen node found, continuing without
[ 0.000000] MIPS: machine is ingenic,ad100
[ 0.000000] [<8001a348>] ingenic_wait_irqoff+0x28/0x74
[ 0.000000] ingenic-dma 13660000.dma: IRQ pdmam not found
[ 0.000000] ingenic-dma 13420000.dma: IRQ pdmam not found
[ 0.032933] s2d successfully!-----0
Starting mdev... OK
[ 0.557353] debugfs: Directory 'GPIO-POWER0' with parent 'gpio_regulator-GPIO-POWER0' already present!
[ 0.917942] md_ingenic,sdhci md_ingenic,sdhci.1: card insert manually
/
Starting network: OK
killall: adbd: no process killed
Starting adb ...
0x0041544F //失败打印
ECC failed: 0
ECC corrected: 0
Number of bad blocks: 0
Number of bbt blocks: 0
Block size 32768, page size 256, OOB size 0
Dumping data starting at 0x00000000 and ending at 0x00000100...
Erasing 32 Kibyte @ 0 -- 100 % complete
Writing data to block 0 at offset 0x0
/
# [Warn] (0.000, +0) lv_demo_widgets: LV_FONT_MONTSERRAT_24 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead. (in lv_demo_widgets.c line #114)
[Warn] (0.000, +0) lv_demo_widgets: LV_FONT_MONTSERRAT_16 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead. (in lv_demo_widgets.c line #119)
install_listener('tcp:5037','*smartsocket*')
sh: write error: No such device
8.4 系统升级后启动流程
九 ota 补充说明
9.1 ota_bin 目录
# ls /etc/ota_bin/
#ota升级过程均使用脚本编写,都有中文注释
# 升级主程序 供开发者调用
local_ota_update.sh
ota 本地升级主程序,探测是否需要 ota 升级,以及本地读取 ota 镜像执行 ota 升级流程
network_ota_update.sh
ota 网络升级主程序,探测是否需要 ota 升级,以及下载 ota 镜像执行 ota 升级流程
network_main_os_update_recovery.sh
ota recovery方式进行ota升级的主程序,主系统 中用来探测是否需要 ota 升级,下载 recovery 镜像以及升级 recovery 系统
network_recovery_update_main_os.sh
ota recovery方式进行ota升级的主程序,recovery系统 中用来下载 主系统 镜像以及升级 主系统
# 功能性程序与函数
ota_local_method.sh
ota 升级程序的设备端方法和升级流程回调的实现
ota_update_kernel.sh
ota kernel的升级方法
ota_update_rootfs_squashfs.sh
ota rootfs.squashfs 的升级方法
ota_update_rtos_bin.sh
ota rtos.bin 的升级方法
ota_utils.sh
ota 用到的实用函数集
ota_img_data_provider.sh
ota 流式升级方式的数据提供者
被ota_update_kernel.sh ota_update_rootfs_squashfs.sh 调用
main_os_info_check.sh
ota 检查 主系统 的升级信息可用性
被network_main_os_update_recovery.sh 调用
recovery_version_utils.sh
ota 用于检测recovery版本信息的工具脚本
被network_main_os_update_recovery.sh 调用
9.2 ota 文件组织策略
编译ota升级镜像时,kernel和buildroot的镜像文件被拆分成多份,分成多个文件,依次追加md5值,并且保存md5值用做文件校验,这样的好处是可以确保下载的文件不会因为服务器缓存的问题而导致的包之间不一致的问题。即使文件名碰巧重合,设备端也可以通过后缀的md5值进行校验
1 以kernel(xImage)为例子,将 xImage(大小 3776576)拆分成4个文件:
xImage.0000.affe7e73ecb13d69956e401d0421ec62
xImage.0001.2102e4f4c28a9925f012eb4f3e0c8c9f
xImage.0002.48e976fc59a9849ff1ad35a31e4509f8
xImage.0003.1ef4ff4a1efec9eb35af6639d8d7d0e6
2 对xImage 以及 xImage.000 求 md5sum,关键字 img_md5 保存 xImage.md5sum,ota_md5_xImage 保存 xImage.000的md5sum 用于下载后设备校验
3 所有的xImage 相关文件被依次追加前一个xImage包文件的md5值作为后缀:
1 ota_md5_xImage -> ota_md5_xImage."xImage.md5"
2 xImage.0000 -> xImage.0000."xImage.md5"
3 xImage.0001 -> xImage.0001."xImage.0000.md5"
4 xImage.0002 -> xImage.0002."xImage.0001.md5"
5 xImage.0003 -> xImage.0003."xImage.0002.md5"
9.3 ota 分区策略
更新设备分区的时候必须保证当前被更新的分区不被占用,所以rootfs分区的程序不能更新rootfs分区的内容,只能去更新另外一个rootfs分区。
从安全的角度上也是一样,如果当前能更新程序所在的分区,比如 rootfs 打包在kernel中的情况,但是遇到掉电/死机/重启等不正常的时候,再一次启动分区的内容不是完整的,所以设备就会无法启动。
所以必须为ota升级程序划分专门的kernel分区和rootfs分区,另外uboot不能进行升级,一旦升级失败机器将无法启动。
目前采用的策略是双分区备份策略,即rootfs的升级程序升级kernel2,rootfs2,反之rootfs2升级kernel,rootfs。升级完成之后改变当前的启动分区为被升级的分区并且重启,启动分区的信息保存在“ota”分区中,分区列表的结构如下,针对 128M nand ,分区大小可以根据实际情况进行调整。
1 uboot 1M
2 kernel 4M
3 rootfs 48M
4 kernel2 4M
5 rootfs2 48M
6 ota 1M
7 userdata 剩余大小
十 ota 常见问题汇总
1 ota升级完成无法进入到新的系统?
可以检查设备端的固件uboot是否支持ota功能. 具体可以参考上面 4.1 uboot 添加支持 ota 功能.
2 设备端无法通过wget获取到升级服务器上的文件?
可以检查电脑端的防火墙是否关闭. 如果是局域网, 要检查你设备端的网络和你的升级服务器上的网络是否在同一个网段.