心灵是自己的地方,在那里可以把地狱变成天堂,也可以把天堂变成地狱
约翰-弥尔顿《失乐园》
这次新平台采用了与之前不同的以太网方案, MAC是内置在SoC(System On Chip)上,而PHY采用了Marvell的一款100Mps的车规级的芯片,MAC/PHY的驱动都要重新开发适配,工作难度比之前预想的要大了很多,完成时间比预想的慢了近一个星期。不过,往后看,这种直接与硬件打交道的经验很能锻炼人,在一定程度改善了我对系统的认知与理解。这篇文章重点在梳理总结下车在以太网MAC/PHY遇到的一些问题,以及Linux下MAC/PHY驱动的一些基本流程。
大致分为如下几个部分:
- MAC/PHY的基础知识
- Linux下MAC/PHY驱动的加载流程
- PHY驱动的开发与适配
- 车载以太网MAC/PHY调试的一些经验总结
汽车电子的发展伴随着技术迭代而不断进化,汽车网络总线从最开的CAN,CAN-FD,到后来的Flexray, MOST再到LIN,到Ethernet,在市场需求与技术发展双重合力之下,经过了不断的发展积累,到如今已经有进40年的历史。近几年,随着各大汽车公司,尤其是新能源汽车公司如特斯拉/蔚来/小鹏等在自动驾驶领域的大力投入,汽车网络开始向高带宽/低延时的以太网方向发展。在这篇文章我们就来对比看看各种汽车网络总线的工作原理以及使用场景。
CAN(Controller Area Network)
CAN最初是由博世(Bosch)于1985年开发出来的车内总线。在这之前,汽车厂商需要通过一条条线将各种汽车控制器连接起来,形成一个个点对点的连接网络。这也导致了增加了汽车本身的重量,增加了系统的复杂度与成本。
CAN作为一个串行总线,具有低成本,轻量化的优势,最高支持1Mb/s的速率,因此迅速被各大厂商采用,并于1993年被定为ISO国际化标准(ISO 11898).
在看Linux内核代码时,经常会遇到各种锁(lock)的使用。对于像spin_lock_irq
/spin_lock_irqsave
的区别感到困惑,每次都要重新查一下资料。遂决定写一篇文章记录下内核中使用到的锁,以及使用的场景。
与应用中的锁类似,内核中的锁也只是为了保护某个内核数据结构或者内存区域在多个并发执行路径时不被破坏,确保数据的一致性。Linux内核作为应用层服务的提供者,一方面要为应用提供系统调用接口(system call
),代表用户进程执行任务,即process context, 在进程上下文中可以休眠,执行调度;同时与硬件直接交互,要响应硬件中断的请求,处理诸如网卡数据/串口数据等请求,即Interrupt Context,在中断上下文内核不能休眠,无法重新调度. 内核就是在进程上下文/中断上下文直接来回切换,执行相应的任务请求。这就自然产生了数据的并发访问,产生了竞争条件(race condition
)。另一方面,目前大多数的系统都是多核CPU、支持多进程,多个CPU、多个进程同时访问内核数据也同样会产生竞争条件。
前阵子在Android下调试一个只有IPv6地址的网络设备时,发现通过ping6
来测试网络连通时提示错误:
1 |
|
stackoverflow上有人说这是一个link-local address
本地链路地址,
最近新的项目又开始了, 开始还算顺利, 却不料碰到了一个奇怪的问题. 先来了解下问题的背景. 这个项目里, Android中有两个以太网网口, 一个用于内网通讯
开始我有点不相信竟然会有这样的问题, 但事实摆在面前, 我也不好抵赖, 于是自己找来一个板子, 看了下, 才逐渐找到答案. 问题的根源在于Android配置的策略路由fwmark
规则, 要解决问题, 只要我们将包含了
大致分如下几个部分来讲一讲这个问题:
- 介绍下什么是Policy Routing<策略路由>
- 分析具体的问题, 并给出方案
- 从源代码角度来分析下, 为何TCP无法建立, 但ping却可以
Excellence in any department can be attained only by the labor of a lifetime;it is not to be purchased at a lesser price
Paul Graham
前段时间, 看新闻说“微软4万多的软件开发工程师, 每天产生进3万个BUG”, 当时感觉有点震惊, 然后就哑然失笑. 震惊的是连微软这样厉害的公司, 工程师应该都很优秀, 人才济济, 为何却会每天产生这么多的BUG了? 于是,再想想自己的开发经历, 才恍然明白: 开发人员一旦走进办公室,打开电脑写代码, 就不可避免的要写出BUG来. 与BUG纠缠不清似乎是每个开发人员的宿命.恰逢最近遇到了一个BUG, 让我纠结不已, 痛定思痛, 觉得有必要把自己的开发”心得经验”写下来, 权当是给自己一点警醒, 给自己一点回顾的资料, 分享下自己在开发过程中遇到的困难与挫折, 苦恼与迷惑.
NAPI(New API)是Linux内核针对网络数据传输做出的一个优化措施,其目的是在高负载的大数据传输时,网络驱动收到硬件中断后,通过poll(轮询)方式将传输过来的数据包统一处理, 在poll时通过禁止网络设备中断以减少硬件中断数量(Interrupt Mitigation
),从而实现更高的数据传输速率。
基于NAPI接口, 一般的网络传输(接收)有如下几个步骤:
- 网络设备驱动加载与初始化(配置IP等)
- 数据包从网络侧发送到网卡(
Network Interface Controller
, NIC) - 通过DMA(Direct Memory Access),将数据从网卡拷贝到内存的环形缓冲区(ring buffer)
- 数据从网卡拷贝到内存后, NIC产生硬件中断告知内核有新的数据包达到
- 内核收到中断后, 调用相应中断处理函数, 此时就会调用NAPI接口
__napi_schedule
开启poll线程(实际是触发一个软中断NET_RX_SOFTIRQ
)(常规数据传输, 一般在处理NIC的中断时调用netif_rx_action
处理网卡队列的数据) ksoftirqd
(每个CPU上都会启动一个软中断处理线程)收到软中断后被唤醒, 然后执行函数net_rx_action
, 这个函数负责调用NAPI的poll
接口来获取内存环形缓冲区的数据包- 解除网卡
ring buffer
中的DMA内存映射(unmapped), 数据由CPU负责处理,netif_receive_skb
传递回内核协议栈 - 如果内核支持数据包定向分发(
packet steering
)或者NIC本身支持多个接收队列的话, 从网卡过来的数据会在不同的CPU之间进行均衡, 这样可以获得更高的网络速率 - 网络协议栈处理数据包,并将其发送到对应应用的
socket
接收缓冲区
Live as everything is a miracle or nothing is miracle
爱因斯坦
使用Android手机时, 我们不仅可以看到当前系统的流量使用情况, 还可以查看每个应用消耗了多少流量, 借此我们可以发现有那些流氓APP在偷偷在背后消耗流量.那么, Android是具体如何实现流量统计的? 又是如何对每个应用的流量使用进行监控? 这篇文章我们就来看看Android流量统计的具体实现原理.
大致说来, Android从如何几个方面进行流量统计:
- 统计每个网口当前发送/接收的流量数据
- 监控每个应用(对应唯一的UID)所消耗的流量
- 支持对总的流量配额进行限制, 如达到一定的流量阈值后, 会对网络进行限制
而具体到每个应用(比如system应用, UID=1000), Android还支持对应用内的每个socket进行标记(tag), 用于区分每个应用(UID)内部具体使用了那些流量.后面, 我们会讲到如何通过标签来区分UID内部的流量.