OpenHarmony Liteos_A 内核之 iperf3 移植心得
一、iperf3 工作原理
iperf3 主要的功能是测试基于特定路径的带宽,在客户端和服务器端建立连接(三次握手)后,客户端发送一定大小的数据报并记下发送的时间,或者客户端在一定的时间内发送数据并记下发送的总数据。带宽的大小等于发送的总数据除以发送的总时间。对服务器端来说,在连接建立时间内,接收的总数据除以所花时间即为服务器端所测得的带宽。
iperf3 测试 UDP 的性能时,客户端可以指定 UDP 数据流的速率。客户端发送数据时,将根据客户提供的速率计算数据报发送之间的时延,客户还可以指定发送数据报的大小。每个发送的数据报包含一个 ID 号,用来唯一地标识该报文。服务器端则根据该 ID 号来确定数据报丢失和乱序。当把 UDP 报文大小设置可以将整个报文放入 IP 层的包(packet)内时,那么 UDP 所测得的报文丢失数据即为 IP 层包的丢失数据。这提供了一个有效的测试包丢失情况的方法。数据报传输延迟抖动(Jitter)的测试由服务器端完成,客户发送的报文数据包含有发送时间戳,服务器端根据该时间信息和接收到报文的时间戳来计算传输延迟抖动。传输延迟抖动反映传输过程中是否平滑。由于它是一个相对值,所以并不需要客户端和服务器端时间同步。
由上介绍我们可以知道 iperf3 的功能增加了操作系统网络度量的能力,而携带 Liteos_A 内核的 OpenAtom OpenHarmony(以下简称“OpenHarmony”)操作系统目前还不支持这个功能,特此尝试把 iperf3 移植到支持 Liteos_A 内核的 OpenHarmony 操作系统中,并作此文分享一些心得。
二、iperf3 移植过程
iperf3 可以运行在 Linux 和 Windows 平台下,其使用了标准的 POSIX 接口,因此将 iperf3 移植到 Liteos_A 上,目前 Liteos_A 支持用户态和内核态的命令,这个也造成了移植的很大困难,所以以下将 2 种添加命令的方式都记录下,供读者参考。
1. 确定库的类型
OpenHarmony 有如下几种目标类型:
executable: 生成可执行文件,对于 Liteos_A,在目录/bin 下可以找到可执行文件
shared_library: 生成.dll 或.so 动态链接库、对于 Liteos_A,在目录/lib 或者 /usr/lib 下可以找到动态库
static_library: 生成.lib 或.a 静态链接库
group: 生成依赖关系组
action: 运行脚本以生成文件
根据以上几种类型的描述可知,将 iperf3 移植成 executable 类型的组件最为合适。
2. 添加库到工程中
将源码下载到 Linux 下并解压,执行./configure,生成 iperf_config.h,将 iperf3 的源码拷贝到 OpenHarmony 代码库中合适的位置,如下将 iperf3 添加到/third_party 目录下。
需要注意的是非内核态的库不能添加到内核的目录下,不然编译和调试过程中相关的头文件可能找不到。在/vendor/厂商名/产品型号/config.json 中的某一子系统下添加组件。
在 /build/lite/components/子系统名.json 中添加组件,如下:
3. 编写配置 BUILD.gn
移植的 iperf3 代码目录下需要提供一个 gn 文件,指明需要编译的代码。此文件可以通过 import 组件模板函数。一方面,很多引用到的头文件需要逐个添加到系统 BUILD.GN 中去,import 组件模板函数可以省去很多麻烦;另一方面,头文件有多个,最终还很难确定是哪一个,使用系统配置好的组件模板函数,可以自动匹配。
4. 程序启动入口:将三方库添加到 shell 命令
1) 内核态的 shell 功能不符合 POSIX 标准,仅供调试使用,本文特记录下其添加命令的方法,此方法分为静态和动态两种方式,添加方式如下:
① 命令源代码包含如下头文件
② 静态注册命令方式
第一步:调用 SHELLCMD_ENTRY(l, cmdType, cmdKey, paraNum, cmdHook),在源文件最后增加这个调用即可。
第二步:在链接选项中添加链接该新增命令项参数。
即在 kernel/liteos_a/tools/build/mk/liteos_tables_ldflags.mk 中增加相应选项,SHELLCMD_ENTRY 的第一个参数前加-u。
此方法添加的 shell,在代码编译阶段就已编译进去了,其实现原理是利用了编译器的 section 特性(也是代码模块化的重要手段),将所有 shell 命令相关功能都放在一段连续的地址空间,将 SHELLCMD_ENTRY 宏一层层展开,即可得到下面原型。
如下是编译后生成的 map 文件中 shell 段的部分,可以观察到已经通过此方法加入的 shell 命令。
③ 动态注册命令方式
此类方式是在代码运行阶段动态的注册,osCmdReg 本身是一个函数,在命令初始化的源代码增加此函数调用。
函数原型在/kernel/liteos_a/shell/full/src/base/shcmd.c 可以找到,基本原理是将动态注册的 shell 命令加入到动态 shell 命令链表空间。
2)用户态的 shell 不用手动添加
添加目标的方式为 executable,将程序下载到目标板上,在/bin 目录下找到可执行的文件,只要在串口助手中输入 ./bin/可执行文件名,内核即可动态加载可执行的文件(或者输入 exec /bin/可执行文件名)。
三、iperf3 使用方式介绍
使用 iperf3 测试时必须将一台主机设置为客户端,一台主机设置为服务器。在 Linux 环境或者 Windows 或者 OpenHarmony shell 交互窗口输入 iperf3 -h 可以获取 iperf3 的帮助信息。以下介绍几种常见的使用方式:
1. iperf3 测试 TCP
在默认的情况下,iperf3 客户端与指定的监听 5201 端口的 iperf3 服务器建立一个 TCP 会话。
运行结果如下:
2. iperf3 测试 UDP
iperf3 测试 UDP 性能时,客户端可以指定 UDP 数据流的速率。客户端发送数据时,将根据客户端提供的速率计算数据报发送之间的时延。
客户端还可以指定发送数据报的大小。每个发送的数据报包含一个 ID 号,用来唯一标识报文,服务器端根据该 ID 号来确定数据报丢失和乱序。
当把 UDP 报文大小设置可以将整个报文放入 IP 层的包(packet)内时,那么 UDP 所测得的报文丢失数据即为 IP 层包的丢失数据,这提供了一个有效的测试包丢失情况的方法。
数据报传输延迟抖动(Jitter)的测试由服务器端完成,客户发送的报文数据包含有发送时间戳,服务器端根据该时间信息和接收到报文的时间戳来计算传输延迟抖动。传输延迟抖动反映传输过程中是否平滑。由于它是一个相对值,所以并不需要客户端和服务器端时间同步。测试过程如下:
测试结果:
- Jitter(延时变化):用 iperf3 UDP 测试来量度
- 数据报丢失:用 iperf3 UDP 测试来量度
- 带宽:通过 TCP 测试来量度
3. 反向带宽测试
服务端使用的命令不变,客户端需要加上参数-R,在帮助信息中,可以看到-R 的信息是 run in reverse mode (server sends, client receives)
测试结果如下:
4. 同步双向带宽测试
客户端加上命令参数 -bidir
测试结果如下:
四、注意事项和遇到的问题及解决方法
1. Hi3516 有三种下载程序的方式:串口、USB、网口转 USB。推荐使用 USB 来下载程序,使用串口来调试程序。
2. DevEco Device Tool 工具使用 USB 烧录 Hi3516DV300 镜像时失败,怎么解决?
解决措施:
出现这个问题,主要是因为开发者将 Device Tool 工具安装在系统盘符,在烧录大文件时会因为没有权限导致失败,可以根据以下操作解决:
● 在 Windows 平台找到安装目录,如图。鼠标右键,选中属性。
● 依次操作,步骤 5 将红框中两个选项都勾选上。
3. Hi3516 如果携带操作系统是支持 Linux 内核的 OpenHarmony,第一次下载时,需要格式化,下载完成后,系统启动到 boot,就不会引导整个系统应用程序,这时需要点击如下菜单,然后重新拔插 USB 才能进入整个系统。
4. 在编写.gn 文件时,如果三方组件为 executable,那么第三方库代码中需要有唯一个入口 main 函数,最终生成一个可执行的命令。
5. iperf 有大版本 1,2,3,目前最新的是 iperf3,不同的版本间命令参数不同,工作机制有所不同,所以在测试时,服务端和客户端要求使用相同的大版本。例如 iperf3 服务端不支持 iperf 的-u,-命令。
6. 在使用 iperf3 进行测试过程中,需要关闭防火墙,不然可能不能进行正常测试,可以提前使用 ping 测试一下网络是否已通。
7. 公司网络端的控制也会对测试造成影响,如果测试中发现发送和接收到的数据一直是 0,则可能是网络控制端进行了控制,在拔掉外网络的情况,测试出来的结果就会很稳定。
8. Hi3516 主板作为服务端,输入 iperf3 -s,然后在 PC 机上启动客户端进行测试,发现根本不能进行正常测试,原因也是公司网络控制导致,解决办法是将要测试的两个网络接口,接到同一交换机下,然后拔掉外网的网线,可以进行正常测试;或者需要 IT 开发权限,当然如果不是在公司特定网络环境下,这个现象是不会出现的。
9. Hi3516 需要设置网络才能进行测试,可以使用命令 ifconfig 来设置,例如:ifconfig eth0 172.17.5.253 netmask 255.255.254.0 gateway 172.17.4.1
10. MSS 在 OpenHarmony 的底层 LWIP 不支持,在 iperf_connect 函数中调用 getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len),会通过系统调用到达 kernel 层中的/kernel/liteos_a/compat/posix/src/socket.c, 最终走到底层 LWIP 的 lwip_getsockopt_impl 接口,在 level IPPROTO_TCP 下,对于分支 TCP_MAXSEG,没有实现,解决办法是先屏蔽 iperf3 对此处的操作。同样在调用 setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt))时,SO_SNDBUF 分支在 LWIP 也未实现,先屏蔽此处,将发送缓冲区设置成和接收缓冲区同样的大小。
11. Liteos_A 未实现延时删除,所以调用 unlink 时会出错,目前处理是先屏蔽此处。
12. Liteos_A 对 fprintf 的实现也不如 Linux,Windows 好,所以对 help 命令输出到 stdout 上,长字串显示不出来,解决办法是使用 fputs 替换 fprintf。
五、总结
本文从 iperf3 的工作原理、移植过程、使用方式、注意事项四个方面介绍了将 iperf3 移植到支持 Liteos_A 内核的 OpenHarmony 操作系统中的方法,希望本篇文章对开发者有所帮助。
关于 OpenHarmony 内核的内容,之前我还介绍了内核对象队列的算法、OpenHarmony LiteOS-M 内核事件的运作机制,以及内核 IPC 机制数据结构,感兴趣的读者可以点击阅读:
《OpenHarmony——内核对象队列之算法详解(上)》、
《OpenHarmony——内核对象队列之算法详解(下)》、
评论