IMX6Q_CPU中断与负荷均衡


最近在做IMX6Q平台的视频应用中,遇到一个关于CPU负荷过高的问题,觉得解决的方法很有意思,必须要记录一下;下面详细描述一下,解决这个问题的过程。

应用需求

硬件平台MCU:imx6qdl,4核
Linux kernel:fsl-yocto-L4.1.15
编译器版本:fsl-imx-fb
功能需求:

  • 2路USB摄像头视频与音频数据采集、保存与rtmp推流,视频分辨率640*360,
  • 1路360环视视频合成、数据保存、视频rtmp推流,分辨率1280*720;
  • 1路经过TX2算法分析识别的视频,通过以太网传输到imx6数据保存与rtmp推流,分辨率1280*720
  • 其它gps、加速度、CAN等传感器数据采集与推送
    根据以上功能需求,系统设计为2个进程,与视频相关的一个进程;传感器相关的为另外一个进程,两者互不干扰,各自进行数据推送。
    视频相关部分用到了Gstreamer1.0库、librtmp库

问题描述

  • 2个独立的进程正常运行后,使用rtmp库函数实现3路视频推流时,在后台页面上查看视频画面,发现视频的延迟比较严重,而且会随着时间进行累积,累积延迟会达到“分钟”级别,这就让人无法忍受了。
  • 视频播放的画面严重卡顿,就像在看图片一样,可是每一路视频的帧率明明都是30fps啊,怎么会卡呢?
  • 本地以太网出现丢包现象,在添加TX2这一路视频传输后,视频延迟以及卡顿现象会更加严重

问题分析

以上问题的现象总结起来就是,在每路视频帧率达到每秒30帧的情况下,视频推流出现延迟以及卡顿,

  • 经过验证,存储的文件没有卡顿以及延迟,说明代码的整体逻辑并没有问题

  • 网络丢包现象是查看 ifconfig eth0
    RX packets 3553389376 bytes 2599862532475 (2.3 TiB)
    RX errors 8745827 dropped 0 overruns 8745827 frame 0
    TX packets 3479495131 bytes 3205366800850 (2.9 TiB)
    TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

  • 出现overrun,网上搜索结果说是网卡丢包是因为设置的缓存区(ring buffer)太小,或者中断过于频繁,导数据来不及进入MAC的ringbuffer,出现overrun

  • 所有的代码都运行起来后通过top命令查看CPU的运行状况,发现CPU的总计占有率为130%左右, 4核CPU嘛,也不算高,怎么视频就延迟和卡顿了呢

  • 灵光一闪,总计CPU占有率是不高,但是都有哪些CPU占着呢,在top界面下按“1”,出现4个CPU占有率的情况,CPU0为125%,其它CPU都为0,感情活都让CPU0一个人干了,其它几个都闲着呢

  • 那好咱就接着查看中断都是谁响应的吧,cat /proc/interrupts命令查看,我操发现所有的中断都是CPU0响应的,

  • 而且发现跟eth0相关的290号中断特真的不少,是不是CPU0太忙,来不及响应网卡中断,导致overrun呢

    扫描二维码关注公众号,回复: 4712543 查看本文章
  • 进一步猜想,是不是CPU0太忙导致视频推送不出去,延迟累积,并且多路推送不平衡,出现视频卡顿呢

问题解决

问题解决要一步一来,首先解决网络丢包的问题吧

网络丢包问题解决

在没具体理解overrun之前,曾经这样尝试:
linux 系统在接收报文之后,会把报文保存到缓存区中。因为缓存区的大小是有限的,如果出现 UDP 报文过大(超过缓存区大小或者 MTU 大小)、接收到报文的速率太快,都可能导致 linux 因为缓存满而直接丢包的情况。在系统层面,linux 设置了 receive buffer 可以配置的最大值,可以在下面的文件中查看,一般是 linux 在启动的时候会根据内存大小设置一个初始值。

/proc/sys/net/core/rmem_max:允许设置的 receive buffer 最大值
/proc/sys/net/core/rmem_default:默认使用的 receive buffer 值
/proc/sys/net/core/wmem_max:允许设置的 send buffer 最大值
/proc/sys/net/core/wmem_dafault:默认使用的 send buffer 最大值

但是这些初始值并不是为了应对大流量的 UDP 报文,如果应用程序接收和发送 UDP 报文非常多,需要讲这个值调大。可以使用 sysctl 命令让它立即生效:
sysctl -w net.core.rmem_max=26214400 # 设置为 25M
另外一个可以配置的参数是 netdev_max_backlog,它表示 linux 内核从网卡驱动中读取报文后可以缓存的报文数量,默认是 1000,可以调大这个值,比如设置成 2000:
sudo sysctl -w net.core.netdev_max_backlog=2000

经过这些调整,发现情况并没改善,而且进一步验证,只有在发送大于5000字节以上的包时,才会出现丢包现象,IMX6的以太网配置可是千兆的,外部是RTL8365,4个端口的PHY。
进一步验证发现是在IMX6与TX2通信时,大于5000字节以上的包会出现丢包现象,而当与外部的百兆网通信时,却没有问题。
那么只能说明imx6配置的RTL8365的千兆网配置有问题,这样解决这个问题的办法有两种:

  • 1、将RTL8365配置成百兆网
  • 2、自己写网络套接字,将所有的大包都拆分成4096字节以内的包发送,取代gstreamer库调用,这样反而更灵活,可以双向收发数据

这两条路我都试过了,将IMX6与RTL8365的自适应配置成百兆网以后,使用gstreamer的RTP元件进行视频推送,没有出现丢包现象。
但是我最终选择的是第二条路,自己写套接字,控制收发过程,还好我这样选择了,因为后来又需要将imx6的一些数据回传给TX2,使用RTP库是做不到的。
另外再将中断负载均衡到各个CPU,尤其是290好的eth0中断,echo 2 > /proc/irq/290/smp_affinity,imx6为4核CPU,因此前面的数字只能是1,2, 4, 8,f,分别表示CPU0, CPU1, CPU2,CPU3,或者全部CPU

视频延迟以及卡顿问题的解决

既然在问题分析步骤已经发现,是CPU0占用率过高,而其它CPU几乎不干活情况下,出现这两问题,那么我就自然想到两个办法,

提高CPU的主频

  • 提高CPU的主频,imx6在负荷较低的情况下主频是396M,可通过以下命令查看
  • cd /sys/devices/system/cpu/cpu0/cpufreq
  • 查看当前CPU的频率:cat cpuinfo_cur_freq
  • 查看最小主频:cat cpuinfo_min_freq
  • 查看最大主频:cat cpuinfo_max_freq
  • 查看CPU支持的主频:cat scaling_available_frequencies
  • 查询CPU支持的工作模式:cat scaling_available_governors
  • 工作模式有:interactive conservative ondemand userspace powersave performance
    Performance. 不考虑耗电,只用最高频率。高性能模式,按你设定范围的最好频率运行
    Interactive. 直接上最高频率,然后看CPU负荷慢慢降低。
    Powersave. 通常以最低频率运行,流畅度会受影响,一般不会用这个吧!按设定最小频率低负荷运行,省电但系统响应速度慢
    Userspace. 可以在用户空间手动调节频率。
    conservative 随着CPU负荷加大,逐步提升频率到最高,然后降至最低
    Ondemand. 定期检查负载,根据负载来调节频率。系统的超频模式,在最大最小频率之间自动调整
  • 如果要设置CPU主频,需要如下操作:
  • echo userspace > scaling_governor,先进入用户模式
  • echo 996000 > scaling_setspeed,设置主频
  • echo Interactive > scaling_governor,这样当CPU根据负荷调整主频时可以达到996M
  • 以上3步操作可以写在/etc/init.d/rc.local脚本中
    经过以上设置,视频的延迟以及卡顿,没有得到根本性的解决。

为CPU做负荷均衡

既然要做CPU负荷均衡,那就要好好规划一下都有哪些任务,经过具体分析,任务可分为一下集中

  • 视频数据采集,包括USB摄像头,360 环视、TX2视频;其中360 环视较为特殊,其占用资源较多,因此决定单独为其分配一个CPU
  • 视频的rtmp推流,总计4路,其中2路640360;2路1280720
  • 视频文件存储

经过以上分析后,遂决定CPU的分配如下:

  • CPU0:负责视频数据存储以及其它不常用的任务对应的线程
  • CPU1:负责4路rtmp视频推流线程
  • CPU2:负责2路USB摄像头数据采集、以及TX2视频数据接收
  • CPU3:负责360环视视频数据的转码、编码操作
    要达到此操作效果需要在代码转给你包含以下头文件
    #define __USE_GNU
    #include <sched.h>
    且这两者必须在其它包含头文件的前面
    然后在主进程中,进行CPU绑定
	cpu_set_t mask;

	//CPU_ZERO(&mask);
	//CPU_SET(cpu_id, &mask);
	__CPU_ZERO_S (sizeof (cpu_set_t), &mask);
	__CPU_SET_S (0, sizeof (cpu_set_t), &mask);
	if (sched_setaffinity(0, sizeof(mask), &mask) < 0)
	{
		printf("set affinity error!\n");
		return -1;
	}

这一步只是将主进程绑定到CPU0,进程绑定CPU与线程绑定CPU可以分开,线程绑定CPU需进行如下操作

	cpu_set_t mask;
	__CPU_ZERO_S (sizeof (cpu_set_t), &mask);
	__CPU_SET_S (2, sizeof (cpu_set_t), &mask);
	pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);
// 更改第3行代码中的数字2,即可绑定到其它CPU,对于imx6来说可选择的数字为:0,1,2,3

经过这样的CPU绑定后,每个CPU各司其职,通过top命令查看,除去360环视需要消耗较多的CPU资源占用率在70%左右,其它3个CPU的占用率均在15%左右,没有明显的差异,不会出现无活可干的CPU。
经过以上整改,再次运行所有代码进行测试,视频延迟与卡顿的现象消失,而且不会出现视频的累积延迟,测试时间达到18小时,延迟时间始终稳定在3秒以内。
甚感欣慰,觉得自己又进步了一点。

猜你喜欢

转载自blog.csdn.net/zhaoyun_zzz/article/details/83651364