
一、行业缺口:缺的不是写代码的人
缺口大不大?大。 但缺的不是"纯写代码的人"。
大部分公司招人时的真实感受是:简历收一堆,技术面能过的很少。很多人停留在"会用厂商SDK、会改Demo、会配工具"的层面,一旦遇到芯片手册写得不清楚、FAE解决不了、需要自己从实际需求出发设计模块的时候,就搞不定了。所以准确说,缺口大的不是"纯写代码的人",而是能独立解决"非标准问题"的人——比如能把MCAL配稳、把UDS调通、能独立定位常见通信故障。
3年左右经验的是不是还不够? 从我接触的圈子看,是的。因为这个阶段的人大多还在熟悉业务和工具链,能独立负责一个底层模块(比如驱动、通信栈、诊断栈)的人确实不多。这不是能力问题,是这个方向的学习曲线本来就陡,3年时间刚好够入门到熟练,还来不及积累足够多的"非标准问题"处理经验。
那行业里真正公认的稀缺能力有哪些? 根据招聘数据和同行交流,真正供不应求的是以下几类高阶复合型人才:
• 把功能安全落实到代码细节:不仅仅是看过ISO 26262,而是知道一个ASIL B的ADC驱动,变量应该加什么保护?内存分区怎么设?故障响应怎么写?很多项目的功能安全是"文档安全",代码还是老一套。
• 搞定多核异构SoC的底层:现在域控制器越来越多,Cortex-R锁步核、Cortex-A跑Linux、M核做实时。能把启动、核间通信、内存共享、硬件加速器驱动写明白的人很少。
• 软硬结合的调试能力:偶发性CAN错误,能自己去抓波形、看共模电压、定位到收发器电源噪声。这种能力大部分软件工程师不具备,硬件工程师又不熟悉协议栈。
• 车载操作系统与中间件:具备5年以上AUTOSAR(CP/AP)、QNX、Linux RT等经验的人,猎头报告显示供需比低至1:8甚至1:16。
以上这些,我不敢说自己多擅长——有些还在学,有些只在项目里摸过皮毛。但这些方向确实是行业里公认的"稀缺"。而最能体现上述能力交集的战场之一,就是诊断栈。
二、AUTOSAR诊断栈的四模块架构
诊断不是一条单线,而是一套精细的分工流水线。AUTOSAR将诊断功能划分成了四个核心模块,各自负责诊断管线的不同阶段:


| DCM | ||
| DEM | ||
| DLT | ||
| FiM |
这四个模块里,DCM是UDS应用层的全部实现,也是我们接下来的焦点。
三、DCM内部的三层架构——ISO 14229-1的直接映射
DCM内部进一步分解为三个子模块,对应ISO标准中"诊断请求受理→路由→服务处理"的三个阶段。可以把它类比成一家医院的门诊体系:


Dcm是总控——Dcm_Init和Dcm_MainFunction调用这三个子模块的入口,保证执行顺序是DspPreDsdMain → DsdMain → DspMain → DspTimerMain → DslMain。
DSL(会话层)——不仅仅是收发缓冲区
这是DCM的最外层,直接面对传输层(PduR)。DSL的核心是一个缓冲区的六态机:
NOT_IN_USE →
IN_USE →
PROVIDED_TO_PDUR →
DSD_PENDING_RESPONSE_SIGNALED →
DCM_TRANSMIT_SIGNALED →
PROVIDED_TO_PDUR (发送中) →
PENDING_BUFFER_RELEASE →
NOT_IN_USE
这六种状态分别对应UDS请求从"传输层开始接收"到"DCM处理完成、回传响应、释放缓冲"的全部生命周期。DSL有两个Rx缓冲区和两个Tx缓冲区——一个"外部"(大块数据,给传输层的PDU),一个"内部"(本地小buffer,给响应NRC和responsePending等不需要大缓冲的场景)。DSL还负责:协议启动/停止回调链、会话切换时调用DspResetDiagnosticActivityOnSessionChange、功能寻址和物理寻址的统一管理。
DSD(调度层)——SID表查找器
DSD的核心数据结构是一个SID查找表——DsdServiceTable[]——每一行绑定一个SID、可选的子功能表、会话和安全的鉴权引用表、以及处理函数的函数指针。AUTOSAR栈通常支持三种协议同时运行——CAN、FlexRay、DoIP——每种协议有自己的DsdServiceTable实例。DSD的主函数DsdHandleRequest()流程:
1. 从 Rx buffer 读出 SID (pduRxData->SduDataPtr[0])
2. lookupSid(sid) —— 在 SID 表中线性搜索
3. 找到后调用 DspCheckSessionLevel —— 当前会话是否允许这个SID
4. 再调用 DspCheckSecurityLevel —— 当前安全等级是否允许
5. 如果有子功能码——调用 DsdLookupSubService() 查找子功能配置
6. 最终调用 selectServiceFunction → runInternalService(SID) —— 这是一个巨大的 switch(SID) 分发到 DspUds* 函数
DSP(执行层)——所有UDS服务在这里变成C代码
Dcm_Dsp是DCM最大的文件——它实现了全部22个UDS服务的业务逻辑。Dsp看到的每一个DspUdsXxx()函数,都直接对应一个SID:
// DspUdsDiagnosticSessionControl(0x10) —— 会话切换
// DspUdsEcuReset(0x11) —— ECU复位
// DspUdsSecurityAccess(0x27) —— Seed/Key
// DspUdsReadDataByIdentifier(0x22) —— 按DID读
// DspUdsWriteDataByIdentifier(0x2E) —— 按DID写
// DspUdsReadDtcInformation(0x19) —— 读DTC
// DspUdsRoutineControl(0x31) —— 例行控制
// ... 总计 22+ 个服务处理函数
这就引出了下面这个核心问题:DSP里究竟要处理哪些服务?这些服务又是怎么来的?
四、UDS服务的完整体系
主谓宾结构:C/S模型的固定语法
在UDS通信里,有两句话永远成立:
• 1. 诊断仪发起请求,ECU返回响应。 没有例外。ECU永远不会主动发起一个UDS请求——它只答复。
• 2. 每一个请求对应至多一个响应。 要么正响应(成功+数据),要么负响应(失败+原因码),要么(在功能寻址+suppressPosRsp条件下)无响应。
这在形式上非常类似HTTP的Request-Response模型——但UDS更加严格地遵守"一请求、一响应"。HTTP有Server-Sent Events、WebSocket升级、100 Continue中间响应的概念——UDS没有任何"流式响应"或"推送"机制。ResponseOnEvent(0x86)是ECU"主动发送数据"的唯一方式,但这也严格绑定到诊断仪先在0x86请求里注册事件——是准主动。
全部26个SID一览
*注:0x23、0x24、0x83、0x84、0x87这5个SID在量产诊断场景中极少使用。0x23被0x22替代(语义化寻址);0x24依赖ODX文件而非在线查询;0x83的P2/P2*值通常在0x10正响应中一次协商;0x84协议栈复杂且多数ECU未实现;0x87为K-Line波特率切换,在CAN/DoIP时代已基本废弃。*
每一个SID都有"为什么存在"的故事
从历史发展的角度看,这26个SID不是一次性设计的——它们是逐层堆叠的:
• 第一波(OBD-II遗留,1996):0x22读数据、0x19读DTC、0x14清除DTC——这是OBD-II Mode 0x01、0x03和0x04的UDS化。排放诊断必须得有这些。
• 第二波(KWP2000进化,2000):0x10会话控制、0x11 ECU复位、0x27安全访问——KWP2000把"修车不仅限于看故障码"的需求带了进来。要执行主动治疗和安全操作,得先挂号、先授权。
• 第三波(CAN适配,2003-2006):0x34/0x35/0x36/0x37下载上传、0x28通信控制、0x3E保活——CAN总线带来了大块数据分段传输和共享总线通信管理(刷写时关掉应用报文)的需求。0x3E则独立应对S3会话超时。
• 第四波(现代汽车电子,2013+):0x2F IO控制、0x31例行控制、0x2A周期读取、0x86事件响应——控制功能的深化。诊断已从"读故障码修车"变成通过诊断接口重新标定、重新编程、重新校准整辆车。
为什么不合并? 为什么有0x22还要0x23?为什么0x84独立于0x27?为什么0x86不和0x2A合并?
• 0x22 vs 0x23(DID读取 vs 地址读取):0x22面向应用层——DID是语义化命名;0x23面向存储层——直接按物理地址读。前者是"给我发动机转速",后者是"给我0x40024000的内容"。两者面对不同的角色和安全边界。
• 0x27 vs 0x84(安全访问 vs 安全数据传输):0x27是会话级鉴权,解决"你是谁";0x84是消息级加密,解决"你的消息是否被偷看/篡改"。两者正交。
• 0x2A vs 0x86(周期读取 vs 事件响应):0x2A是定时器驱动("每500ms发一次"),0x86是事件驱动("DTC状态变化时发给我")。这是时间轴上两种不同的采样模式。
核心洞察:每一个"为什么不合并"背后都有一个两难权衡——合并可以简化接口,但会混淆语义,导致一个SID承担太多不相关的职责。这26个SID被ISO委员会认定为最小充分集合。
哪些仅能在非默认会话中使用
UDS有一套严格的访问控制矩阵,确保普通OBD-II扫描仪不能意外触发敏感操作:
这张表就是UDS"挂号"逻辑的实质:默认会话是一个公共可读空间,但要写、控制、做手术,必须挂到其他号。
SID地图的医院化映射
五、贯通:从需求到代码的完整链路
你会发现:
• 稀缺能力的第一条"把功能安全落实到代码细节",在诊断栈里就体现为:DSP服务处理中显式调用E2E保护、DSD的会话/安全鉴权矩阵严格配置、DSL缓冲区管理能正确处理S3超时和响应Pending。
• 第二条"多核异构SoC",则对应着诊断栈在锁步核、高性能核上的不同部署策略,以及跨核诊断通信的安全设计。
• 第三条"软硬结合调试",正是当偶发CAN错误导致诊断响应丢失时,能立刻沿着"物理层波形→DSL状态机→DSD查表→DSP逻辑"这条链一路排查到底的能力。
• 第四条"车载OS与中间件",则直接指向AUTOSAR CP/AP诊断栈本身的深度掌握。

面试
资料
学习
路线
学习
指导
多名10年+大厂经验
工程师在线指导
学习
交流
众多开发者一起交流
助力你提升技能
夜雨聆风