2026年3月

前言

B 站的拜年祭,正在变成一座装修精美的<span style="color: red;font-size: 16px">「电子坟墓」</span>

很多人抱怨现在的节目「没那味儿了」,这话说得太温柔。事实是:当一个社区试图讨好所有人的时候,它就已经开始背叛最初的那群人了。

我扒开了这 17 年的互动数据,没看代码,只看人心。数据显示:制作精细度提升了几十倍,用户的灵魂却集体「离家出走」了。

今天,我们不聊技术,只聊聊这场大型的<span style="color: red;font-size: 16px">「变心」</span>现场。

2010-2012:「为爱发电」的社区

那两年,B 站还是个连服务器都随时会挂掉的小作坊,但那确是用户黏性最強的时候。

2010 年:给「赛博童年」补票

我分析 2010 年数据时发现了一个有趣的现象:<span style="color: red;font-size: 16px">评论区中 2017 年后的「考古」回复占比居然超过 65%</span>。

评论时间分布

这说明了什么?真正经典的内容是有「利息」的。爆发期那点流量只是「首付」,后来的十年里,无数老兵回来互动,这是在给当年的情怀补票。那时候大家不看画质,看的是「同类」。

互动区情感评分(AI)

评论话题分布(AI)

2012 年:那场关于「参与感」的集体狂欢

那是极客们至今还在怀念的黄金时代。那一年,B 站上线了高级弹幕。

<span style="border-bottom: 2px solid #fb7299; font-weight: bold;">用户可以用硬币换权限,亲手在屏幕上「涂鸦」。</span>

高级弹幕例子

虽然硬币是登录即送的「免费物资」,但在那个纯粹的年代,这些硬币承载的是用户的活跃度和社区荣誉。放到现在,这叫「付费解锁特效」,但在当年,这是用户认可社区的最高体现。

2010,2011,2012互动数据对比

数据直接翻倍,这证明了:当用户觉得这个地方属于自己时,他们是真的舍得「梭哈」掉手里所有的"积蓄"。

2014:陈睿进场,净土开始「挂牌招标」

这一年,陈睿正式任职董事,B 站开启了商业狂飙模式。一个有意思的现象是:播放量明明打不过 2012 年,但<span style="color: red;font-size: 16px">弹幕和硬币数却诡异地完成了反超</span>。

4年份的数据对比

这种数据倒挂说明了什么?

或许是老用户开始产生「被迫害妄想」了,他们觉得 B 站正在被主流社会围剿,所以疯狂投币、刷弹幕来证明自己的存在。这是一种典型的「饭圈化」雏形——用户投的不是币,是守护这个「小破站」的投名状。

很多人没注意到,2014 年也是 B 站商业化的「元年」。
<span style="color: red;font-size: 16px">开屏广告、首页 Banner 正式上线,专业的广告销售部门同年组建。</span>

这意味着:「二次元净土」,正式被划分成了待售的「广告位」。 当陈睿把品牌广告带进社区的那一刻,B 站就已经从「爱好的聚居地」变成了「待售的流量池」。拜年祭不再仅仅是 UP 主的狂欢,它开始承载起向甲方爸爸「秀肌肉」的任务。

2014评论时间分布

2016-2018:当回旋镖开始加速

如果说前几年是自嗨,那 2016 年就是 B 站试图跨越阶级的开始。

2016 年:那句名言背后的「信任危机」

爆款作品《九九八十一》横空出世,风光背后,是 B 站因为试水贴片广告被骂到公开道歉。由此陈叔叔还留下了那句著名的名言:

<p style="text-align: center; color: red; font-size: 18px; font-weight: bold; padding: 20px; border: 1px dashed red;">B 站未来有可能会倒闭,但绝不会变质。</p>

现在看来,这句话就是一个精准的「情感债务」。为了不倒闭,它必须学会赚钱;为了赚钱,它就得对老用户「痛下杀手」。

2018 年:45% 互动率的「巅峰时刻」

上市之年,数据给足了面子。<span style="color: red; font-weight: bold;">互动比例达到了恐怖的 45%</span>。

2016vs2017vs2018

这绝非偶然,而是 B 站当时在疯狂推行「一键三连」。大家觉得「小破站出息了」,那种社区自豪感被推到了顶点。但这也是核心用户最后一次大规模的「集体共鸣」。

2019-2020:7600 万的「流量巨浪」

这是综合数据上最亮眼的两年。

2019 年拜年祭直接冲到了一个离谱的高度,并开启了 2020 年那场 7600 万播放的全系列天花板。
<span style="color: red;font-size: 16px">流量像海啸一样灌进了这个曾经的小社区。</span>

18vs19vs20

从运营角度看,这是典型的「破圈红利」。B 站不再满足于二次元,它想要《后浪》,想要跨年晚会,想要全中国年轻人的注意力。

那两年的封面越来越有「大厂宣发」的高级感。

2019封面

看似 B 站赢麻了,但从数据上看,用户互动的比例已经开始悄然下滑。狂欢是大家的,但孤独(和被稀释的归属感)是老用户的。

2021:那一字之差的「战略代价」

2021 年是拜年祭历史上最沉重的一年。

承载了 11 年情怀的「拜年祭」,正式更名为「拜年纪」。这一字之差,宣告了「管理权」的彻底交接——从社区的狂欢,正式转向了大厂的叙事。

数据反馈极其扎心:播放量缩水 3 倍,互动比暴跌至 8%。
<span style="border-bottom: 2px solid #fb7299; font-weight: bold;">这不是内容的失败,而是「破圈」必经的阵痛。</span>

2020vs2021拜年纪

那一年的 B 站,试图在保持二次元纯度与迎合大众口味之间跳舞,结果却像是一个尴尬的平衡木选手。老用户觉得「灵魂丢了」,新用户觉得「节目像低配版春晚」。

名字改了,那个敢爱敢恨的小破站,也正式变成了要看财报眼色的 BiliBili。

2022-2026:当「情怀」变成了「电子养老院」

这几年的数据,写满了「疲软」。

2024 年:一个人的寂寞,千万人的静默

评论数跌破 1 万条。对于一个千万级播放的视频来说,这简直是<span style="color: red;">「社区死亡证明」</span>。

2023vs2024

我甚至怀疑:除了那些自动播放的流量,还有多少人是真的坐下来看完了?

2025 年:靠 2233 娘「喂情绪」

今年弹幕回升了,但我扒开数据一看:<span style="color: red;font-size: 16px">至少 20% 的互动是在刷 2233 娘。</span>

高频率分析(AI)

这证明了:观众不再关心节目好不好看,他们只是回来给「情怀」上柱香的。B 站也深谙此道,靠着主线动画精准拿捏。这不是晚会的胜利,这是「啃老」。

2026 由于没跑满一年,这里略过,数据上截止目前也是一般般

最后

16 年的拜年数据跑下来,最直观的感受是:拜年节目虽然变得越来越精美,但用户变得越来越冷了。

很多人会问:B 站变了吗?

  1. 从情怀看:它确实背叛了。那句「绝不变质」现在看来就是扎向自己的回旋镖,每当财报不好看,这镖就飞回来扎自己一刀。
  2. 从现实看:它只是成年了,需要为周围的人考虑而已。

<span style="color: red;font-size: 16px">没有商业化的理想主义,最终只会消失在互联网浪潮中。</span>

它不是内容不行了,而是那个曾经年轻充满战斗力的社区,已经在资本和流量的冲击下,彻底「变成了成年人」

本文由mdnice多平台发布

一、摘要

本文介绍了一个基于SNMP协议的网络设备批量巡检脚本,该脚本使用pysnmp库实现了对网络设备接口状态的自动采集和分析。简要介绍了SNMP协议并对比了不同网络设备管理方式的优缺点,讲解了pysnmp库的使用方法,最后对脚本的功能、使用场景和局限性进行了分析。

二、练习环境

  1. 使用华三HCL模拟器进行设备模拟,确认本地电脑可ping通模拟设备。具体可参考我的文章[网络自动化学习笔记-H3C 模拟器(HCL)基础环境配置]。

image-1.png

  1. 辅助工具

    [ManageEngine MibBrowser]

    通过工具,使用SNMP查询设备信息,辅助验证脚本功能。

  2. Python环境
  • Python 3.14
  • pysnmp,pysmi 库
  • VS code(可选)

三、SNMP协议简介

SNMP(Simple Network Management Protocol)是一种用于网络管理的标准协议,它允许网络管理员监控网络设备的状态、配置和性能。

提醒

只使用SNMP进行巡检和性能分析,不建议使用SNMP进行配置,尤其是实际网络中尽量不要使用配置功能
可能因空格,缩进,标识符等原因,导致CLI界面对应配置项不可用。

3.1 SNMP在网络设备管理中的作用

  • 实时监控:可以实时获取设备的运行状态,如接口状态、CPU使用率、内存使用情况等
  • 故障诊断:当网络出现问题时,可以通过SNMP快速定位故障点
  • 性能分析:通过收集历史数据,可以分析设备的性能趋势,预测潜在问题
  • - 配置管理:【不推荐,不建议可以远程配置设备参数,减少人工操作

四、CLI,SNMP,Netconf的不同方式对比

4.1 Paramiko CLI方式

优势

  • 可以执行任意CLI命令,功能全面
  • 易操作易理解,因为多数工程师才能熟练使用CLI命令行。
  • 可以获取设备特有的信息
  • 对设备的控制能力强

劣势

  • 依赖设备的CLI接口,不同设备命令格式不同
  • 解析命令输出复杂,容易出错
  • 需要回读屏显获取数据,不方便
  • 效率低,不适合大规模设备管理
  • 安全性依赖于SSH配置

4.2 SNMP方式

优势

  • 标准化,跨设备兼容性好
  • 效率高,适合批量操作
  • 实时性好,响应速度快
  • 实现简单,开发成本低

劣势

  • 功能相对有限,某些设备特有信息无法获取,或需要依赖厂家私有MIB。
  • 安全性较差(v1/v2c)
  • 依赖设备的SNMP实现质量

4.3 NETconf方式

优势

  • 基于XML/JSON,数据结构清晰
  • 支持配置和状态的获取与修改
  • 安全性高,支持多种认证方式
  • 功能强大,可扩展性好

劣势

  • 实现复杂,开发成本高
  • 对设备的支持程度不如SNMP广泛
  • 占用带宽较大,不适合带宽有限的环境
  • 学习曲线较陡峭

五、Pysnmp库介绍

Pysnmp是一个纯Python实现的SNMP库,支持SNMP v1、v2c和v3版本。它提供了丰富的API,使得开发者可以方便地实现SNMP管理功能。

5.1 版本区别

Pysnmp有两个主要版本:

  • 传统版本(v4.x):使用同步API,导入方式为from pysnmp.hlapi import *
  • 最新版本(v5.x):支持异步API,导入方式为from pysnmp.hlapi.v3arch.asyncio import *

本文使用的是最新版本的pysnmp,它支持异步操作,更适合处理大量设备的并发查询。

5.2 常见Pysnmp函数示例

以下示例均来自官方网站【GET Operation - PySNMP 7.1 Documentation】,并稍了做修改。

5.2.1 get操作(获取单个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
errorIndication, errorStatus, errorIndex, varBinds = await get_cmd(
SnmpEngine(),
CommunityData('readtest', mpModel=1),
await UdpTransportTarget.create(('10.6.6.6', 161)),
ContextData(),
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0)),
ObjectType(ObjectIdentity('IF-MIB', 'ifName', 2))
)
for varBind in varBinds:
print("-----------------------------")
print(f"查询到的内容是{varBind}")
print(f"sysName的OID值是{varBind[0]}")
print(f"ifName的OID值是{varBind[1]}")
print(f"sysName的可读化输出值是{varBind[0].prettyPrint()}")
print(f"ifName的可读化输出值是{varBind[1].prettyPrint()}")
print(f"{varBind[0].prettyPrint()} = {varBind[1].prettyPrint()}")
print("-----------------------------")

asyncio.run(run())

示例运行截图如下

get01.jpg

5.2.2 next操作(获取下一个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
    errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
        SnmpEngine(),
        CommunityData('readtest', mpModel=1),
        await UdpTransportTarget.create(('10.3.3.3', 161)),
        ContextData(),
        ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysContact', 0)),      #会返回sysContact的下一个OID值
        ObjectType(ObjectIdentity('IF-MIB', 'ifName', 2)),          #会返回ifName.2的下一个OID值
        lexicographicMode=False
    )
    for varBind in varBinds:
        print("-----------------------------")
        print(varBind[1].prettyPrint())
        print("-----------------------------")
    
asyncio.run(run())

next操作会获取下一个值,如下图所示

next01.jpg

next02.jpg

示例运行截图如下

next03.jpg

脚本查询sysContact,实际返回值是sysName的值,查询ifName.2,实际返回值是ifName.3的值。

5.2.3 bulk操作(批量获取多个OID的值)

import asyncio
from pysnmp.hlapi.v3arch.asyncio import *

async def run():
    errorIndication, errorStatus, errorIndex, varBinds = await bulk_cmd(
        SnmpEngine(),
        CommunityData('readtest', mpModel=1),
        await UdpTransportTarget.create(('10.3.3.3', 161)),
        ContextData(),
        0, 100,                                #循环查询100次。
        ObjectType(ObjectIdentity('IF-MIB', 'ifName'))
    )
    for varBind in varBinds:
        print(varBind)
    
asyncio.run(run())

示例运行结果如下

bulk01.jpg

因为是循环取值,结果显示范围远远超出了ifName的边界。

六、脚本介绍

6.1 功能概述

本次练习的脚本是一个基于SNMP v2c的模拟批量设备巡检工具,主要功能包括:

  1. 设备IP管理:从devices.txt文件读取设备IP列表,支持空行、注释行和重复IP的自动处理
  2. SNMP查询:使用异步方式批量查询设备的接口信息,包括接口索引、描述、名称、管理状态和运行状态
  3. 结果处理:将查询结果保存到CSV文件,并生成详细的日志文件
  4. 错误处理:对查询过程中的错误进行捕获和记录

6.2 脚本内容

  1. 导入模块
  2. 构建日志配置函数,IP文件读取函数,SNMP查询配置,配置格式化函数,查询函数,结果保存函数
  3. 主函数调用构建的功能函数,脚本入口。
import asyncio
import csv
import logging
import time
from datetime import datetime
from pysnmp.hlapi.v3arch.asyncio import *

# 配置日志函数
def setup_logger(log_file):
    """设置日志配置"""
    logger = logging.getLogger(log_file)
    logger.setLevel(logging.INFO)
    
    # 清除之前的处理器
    for handler in logger.handlers[:]:
        logger.removeHandler(handler)
    
    # 创建文件处理器
    file_handler = logging.FileHandler(log_file, encoding='utf-8')
    file_handler.setLevel(logging.INFO)
    file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
    logger.addHandler(file_handler)
    
    # 创建控制台处理器
    stream_handler = logging.StreamHandler()
    stream_handler.setLevel(logging.INFO)
    stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
    logger.addHandler(stream_handler)
    
    return logger

# 读取设备IP列表
def read_device_ips(file_path, logger=None):
    ips = []
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                stripped_line = line.strip()
                if not stripped_line or stripped_line.startswith("#"):
                    continue    #跳过空行和注释行
                ips.append(stripped_line)
        
        set_ip = set(ips)
        if len(set_ip) == len(ips):
            message = f"读取到的IP地址列表,共{len(ips)}个IP地址,没有重复的IP地址"
            print(message)
            if logger:
                logger.info(message)
        else:
            count_duplicates = len(ips) - len(set_ip)
            message = f"读取到的IP地址列表,共{len(ips)}个IP地址,有{count_duplicates}个重复的IP地址,已自动去重"
            print(message)
            if logger:
                logger.info(message)
            ips = list(set_ip)
            

        message = f"成功读取{len(ips)}个有效设备IP"
        print(message)
        if logger:
            logger.info(message)
        return ips
    except Exception as e:
        message = f"读取设备IP文件失败: {e}"
        print(message)
        if logger:
            logger.error(message)
        return []

# 定义SNMP查询配置
PORT_CONFIG = [
    ("IF-MIB", "ifIndex","端口索引"),
    ("IF-MIB", "ifDescr","端口描述"),
    ("IF-MIB", "ifName","端口名称"),
    ("IF-MIB", "ifAdminStatus","物理状态"),
    ("IF-MIB", "ifOperStatus","协商状态")
]

#构建OID路径,并返回格式化的MIB列表、 表格映射、 起始OID路径
def format_config(config=PORT_CONFIG):
    format_mib = []
    format_table = {}
    start_name = ""
    for item in config:
        if len(item) == 3:
            format_mib.append(ObjectType(ObjectIdentity(item[0], item[1])))
            format_table[item[1]] = item[2] # 端口描述
            start_name = f"{item[0]}::{item[1]}"
        else:
            format_mib.append(ObjectType(ObjectIdentity(item[0], item[1])))
            format_table[item[1]] = item[1]
            start_name = f"{item[0]}::{item[1]}"
        print(f"format_table is [{format_table}]")
        print(f"start_name is {start_name}")
    print(f"format_mib is {format_mib}")
    return format_mib, format_table, start_name

async def get_query(ip, community, config=PORT_CONFIG, logger=None):
    """
    使用next_cmd自动遍历所有接口的信息,直到没有更多数据为止。
    通过分析返回的OID,判断是否继续查询下一个接口。
    """
    try:
        snmpEngine = SnmpEngine()
        transport = await UdpTransportTarget.create((ip, 161))
        
        # 存储结果的字典
        interface_details = {}
        hostname = ""
        message = "开始遍历所有接口..."
        print(message)
        if logger:
            logger.info(f"{ip} - {message}")
        
        # 首先查询sysName
        errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
            snmpEngine,
            CommunityData(community, mpModel=1),
            transport,
            ContextData(),
            ObjectType(ObjectIdentity("SNMPv2-MIB", "sysName")),
            lexicographicMode=False
        )
        
        # 处理sysName
        if not errorIndication and not errorStatus and varBinds:
            for varBind in varBinds:
                try:
                    if isinstance(varBind, ObjectType):
                        oid = varBind[0]
                        val = varBind[1]
                    elif isinstance(varBind, tuple) and len(varBind) >= 2:
                        oid, val = varBind[0], varBind[1]
                    else:
                        continue
                    
                    oid_str = oid.prettyPrint()
                    val_str = val.prettyPrint()
                    
                    if oid_str == 'SNMPv2-MIB::sysName.0':
                        hostname = val_str
                        message = f"系统名称: {hostname}"
                        print(message)
                        if logger:
                            logger.info(f"{ip} - {message}")
                except Exception as e:
                    message = f"处理sysName异常: {e}"
                    print(message)
                    if logger:
                        logger.error(f"{ip} - {message}")
                    continue
        
        # 遍历config中的每个OID进行查询
        for item in config:
            mib, oid_name, desc = item
            message = f"\n开始查询 {mib}::{oid_name}..."
            print(message)
            if logger:
                logger.info(f"{ip} - 开始查询 {mib}::{oid_name}...")
            
            # 统计当前OID查询到的条目数
            item_count = 0
            non_expected_oid = None
            
            # 执行初始的next_cmd查询
            errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
                snmpEngine,
                CommunityData(community, mpModel=1),
                transport,
                ContextData(),
                ObjectType(ObjectIdentity(mib, oid_name)),
                lexicographicMode=False
            )
            
            while not errorIndication and not errorStatus and varBinds:
                # 处理当前批次的数据
                last_oid = None
                
                for varBind in varBinds:
                    try:
                        # 解包varBind
                        if isinstance(varBind, ObjectType):
                            oid = varBind[0]
                            val = varBind[1]
                        elif isinstance(varBind, tuple) and len(varBind) >= 2:
                            oid, val = varBind[0], varBind[1]
                        else:
                            continue
                        
                        oid_str = oid.prettyPrint()
                        val_str = val.prettyPrint()
                        
                        # 检查是否是当前OID的延续
                        if oid_str.startswith(f"{mib}::{oid_name}."):
                            # 提取索引
                            index = str(oid[-1])  # 直接从OID对象获取最后一个数字
                            
                            # 初始化接口信息
                            if index not in interface_details:
                                interface_details[index] = {}
                                for config_item in config:
                                    interface_details[index][config_item[1]] = None
                            
                            # 更新接口信息
                            interface_details[index][oid_name] = val_str
                            last_oid = oid
                            item_count += 1
                        else:
                            non_expected_oid = oid_str
                            break
                    except Exception as e:
                        message = f"处理数据异常: {e}"
                        print(message)
                        if logger:
                            logger.error(f"{ip} - {message}")
                        continue
                
                # 继续下一次查询
                if non_expected_oid:
                    break
                else:
                    errorIndication, errorStatus, errorIndex, varBinds = await next_cmd(
                        snmpEngine,
                        CommunityData(community, mpModel=1),
                        transport,
                        ContextData(),
                        ObjectType(ObjectIdentity(mib, oid_name, index)),  # 从上次的最后一个值继续
                        lexicographicMode=False  # 只遍历当前OID子树
                    )
            
            # 输出当前OID查询结果
            message = f"获取到: {mib}::{oid_name} 条目共计 {item_count} 个。"
            print(message)
            if logger:
                logger.info(f"{ip} - {message}")
            if non_expected_oid:
                message = f"非预期OID: {non_expected_oid}"
                print(message)
                if logger:
                    logger.info(f"{ip} - {message}")
        
        # 输出总体结果
        interface_count = len(interface_details)
        config_count = len(config)
        message = f"\n共找到 {interface_count} 个接口,查询了它们 {config_count} 项信息。"
        print(message)
        if logger:
            logger.info(f"{ip} - {message}")
        
        # 按索引排序输出前10个接口示例
        print("前10个接口示例:")
        if logger:
            logger.info(f"{ip} - 前10个接口示例:")
        for idx, details in sorted(interface_details.items(), key=lambda x: int(x[0]))[:10]:
            interface_message = f"  ifIndex.{idx}: {details.get('ifName', 'N/A')}"
            print(interface_message)
            if logger:
                logger.info(f"{ip} - {interface_message}")
        
        snmpEngine.close_dispatcher()
        return interface_details, hostname
        
    except Exception as e:
        print(f"异常: {e}")
        return {}, ""

# 保存查询结果到CSV文件
def save_results(interface_details, hostname, ip, file_path, config=PORT_CONFIG):
    try:
        
        with open(file_path, 'a', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            print(f"正在保存结果到 {file_path}...")
            
            # 写入表头
            writer.writerow(["=" * 80])
            line = f"主机名={hostname};设备IP={ip};检查时间:{time.strftime('%Y-%m-%d %H:%M:%S')}"
            writer.writerow([line])
            writer.writerow(["=" * 80])
            
            # 动态生成表头
            headers = [item[2] for item in config]
            writer.writerow(headers)
            
            # 写入数据
            for interface in interface_details.values():
                row = []
                for item in config:
                    row.append(interface.get(item[1], ''))
                writer.writerow(row)
        print(f"成功保存查询结果到{file_path}")
    except Exception as e:
        print(f"保存查询结果失败: {e}")

async def main():
    # 配置参数
    device_file = 'devices.txt'  # 设备IP文件
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    port_result_file = f'snmp_port_results_{timestamp}.csv'  # 端口结果文件
    port_log_file = f'snmp_port_results_log_{timestamp}.log'  # 端口日志文件
    community = 'readtest'  # SNMP团体名
    
    # 设置日志
    port_logger = setup_logger(port_log_file)
    
    # 读取设备IP
    device_ips = read_device_ips(device_file, logger=port_logger)
    if not device_ips:
        port_logger.error("没有设备IP可查询")
        return
    
    # 批量查询设备信息
    for ip in device_ips:
        port_logger.info(f"开始查询设备: {ip}")
        
        # 获取端口信息和主机名
        interface_details, hostname = await get_query(ip, community, config=PORT_CONFIG, logger=port_logger)
        
        # 保存端口结果
        if interface_details:
            save_results(interface_details, hostname, ip, port_result_file, config=PORT_CONFIG)
            port_logger.info(f"{ip} - 成功保存端口结果到 {port_result_file}")
        
        # 避免查询过快,添加短暂延迟
        await asyncio.sleep(1)
    port_logger.info("批量查询完成")
if __name__ == "__main__":
    asyncio.run(main())

6.3 脚本输出结果保存

main01.jpg

6.4 脚本运行日志保存

log01.jpg

6.5 脚本局限性

  1. 仅支持SNMP v2c:没有对SNMP v3进行适配,安全性较差
  2. 功能有限:目前只支持接口状态的查询,无法获取CPU、内存、温度等系统资源信息
  3. 可扩展性不足:添加新的查询项需要修改代码,不够灵活
  4. 错误处理简单:对于SNMP查询失败的情况,处理方式较为简单
  5. 配置固定:SNMP团体名、查询参数等配置硬编码在脚本中,不够灵活

6.6 后续练习中可能尝试的功能

  1. 添加系统资源监控:增加对CPU使用率、内存使用情况、设备温度等信息的查询
  2. 支持SNMP v3:添加对SNMP v3的支持,提高安全性
  3. 配置文件化:将配置参数移到配置文件中,方便用户修改
  4. 结果可视化添加简单的结果可视化功能,如生成状态图表,或日志格式化提升可读性
  5. 告警机制:添加异常状态的告警功能,如接口down时发送通知

七、结语

SNMP作为一种成熟的网络管理协议,在网络设备监控和管理方面发挥着重要作用。本文介绍的批量巡检脚本利用pysnmp库实现了对网络设备接口状态的自动采集和分析,为网络管理员提供了一种高效、便捷的设备管理工具。

虽然该脚本目前存在一些局限性,但其基本功能已经能够满足日常网络巡检的需求。通过进一步的扩展和优化,它可以成为网络管理工作中的得力助手。

在实际应用中,网络管理员可以根据具体需求对脚本进行定制和扩展,例如添加更多的监控指标、实现更复杂的告警机制等,从而构建一个更加完善的网络监控系统。

SNMP协议虽然有其局限性,但在网络设备管理领域仍然是一种不可替代的工具。结合其他管理协议和工具,如NETconf、REST API等,可以构建一个更加全面、高效的网络管理解决方案。

背景

每年过年回去,我们都会打牌。
一共 3 个玩的比较好的发小。
认识了 20 多年了。 都是一个小区的。

这人工资 6000 多,背了 70 多万房贷
我们玩的很小,一晚上输赢最多 200 。。他连输 3 天, 输满了 400 ,就不玩了。。说明年也不玩了。。
哎,房贷把他搞麻了啊。看来房贷压力不小,,我是真的没想到他会说出这样的话


如果说 2024 年是 AI 生成代码(Copilot、Cursor)的元年,那么到了 2026 年,我们已经正式进入了“自然语言即接口、智能体(Agent)即员工”的深水区。

(图注:AI 开发演进时间轴:从早期的 TDD 时代,到 LLM 辅助编码,再到如今由 Testplan 支撑的“安全 AI 操作空间”与个人智能体时代。)

现在,哪怕是一个不懂编程的产品经理,只要动动嘴皮子,智能体就能在一个下午帮你拉起一个 SaaS 级的应用架构。这种生产力的爆发是极其震撼的。

然而,狂欢之下,暗流涌动。当开发流程被极度压缩,系统内部逻辑的可见性正在从“白盒”滑向“黑盒”。你不知道 AI 到底写了什么,你只知道它“看起来能跑”。速度被无限放大,但与此同时,代码失控的风险也达到了前所未有的高度。

在这个“高产但高危”的 AI 开发时代,我们迫切需要一套新的工程方法论。八年前我提出的 TPDD(测试计划驱动开发,Test Plan Driven Development),在今天,迎来了它真正的使命。

一、 危险的失衡:只有“油门”没有“刹车”的 AI 工程

当前绝大多数拥抱 AI 的团队,都陷入了一种结构性的策略失衡:极度关注“能力暴露(Capability Exposure)”,却系统性无视“禁止空间(Forbidden Zone)”。

(图注:AI 控制平衡图。真正的安全 AI 操作空间,必须建立在“能力暴露(绿区:能做什么)”与“禁止空间(红区:绝对不能做什么)”的交集之上。当前多数团队严重偏向绿区而忽视红区。)

什么是能力暴露?就是我们常写的 skill.md。比如告诉 AI:“你能读取 CSV,你能生成 PDF,你能调取天气 API。”大家都在拼命探索“AI 还能干什么”,这种模式在扩张期极其高效,但也极易造成灾难。

因为大家忘了定义“禁止空间”,也就是红线。比如,你没有告诉 AI:“绝对不允许访问系统底层文件、绝对不允许将包含隐私数据的图表发送到外网。”

【笔者观点:没有底线的全能,就是最大的炸弹】
我们现在使用 AI Agent,就像在开一辆马力无穷但没有刹车和安全带的超级跑车。
最近爆火的 OpenClaw 就因为对主机拥有无限制访问权限而遭到安全专家的炮轰。很多开发者享受着“一键生成”的快感,却忽略了 AI 存在幻觉(Hallucination)和提示词注入(Prompt Injection)的风险。如果不划定“禁止空间”,AI 失控删掉你整个数据库,或者把企业机密泄露出去,只是时间问题。在这个时代,“定义 AI 不能做什么”比“教 AI 能做什么”要重要一万倍。

二、 TDD 死亡,TPDD 崛起:当 AI 自己写单元测试时,人类该干嘛?

在传统的敏捷开发中,TDD(测试驱动开发) 是神圣的教条。程序员要先写一段单元测试,然后再写代码让测试通过。

但在 AI 时代,TDD 正在被“内化”。现在,大模型完全可以自己写代码,自己生成测试用例,如果跑不通,它还能自己重构。微观层面的单元级循环,人类已经插不上手了。


(图注:开发循环对比图。左侧传统的 TDD 循环(写测试->写代码->重构)已被 AI 替代;右侧是新的 AI+TPDD 循环,人类退守到高层的“定义测试计划(Define TestPlan)”与“高层验证(High-level Validation)”,中间的代码生成由 AI 完成。)

那人类工程师的价值在哪?答案是:退守到更高维度的“测试计划(TPDD)”控制层。

当 AI 把细节干完了,人类必须去回答三个宏观问题:

  1. 这个功能到底应该长什么样?
  2. 它的物理和逻辑边界在哪?
  3. 到底达到什么标准,才算真正的“验收通过”?

这就是 TPDD 的核心:高层先行 + 验证闭环。我们不再去跟 AI 卷每一行代码的逻辑,而是提前写好一份高维的“开发承诺书”。

【笔者观点:从“泥瓦匠”到“建筑监理”的身份跃迁】
过去我们是泥瓦匠(写代码),现在 AI 成了最高效的 3D 打印建筑机,而我们的身份必须立刻转变为“建筑监理”。
监理不需要自己去和泥,但他必须拿着图纸(TPDD 测试计划)站在旁边,明确指出哪里承重不合格、哪里不能用劣质材料。AI 最不缺的就是执行力,它最缺的就是“Failure Thinking(失败底线思维)”。人类通过 TPDD 为 AI 套上紧箍咒,这才是人机协作的终极形态。

三、 实战利器:用 TestPlan.md 打造 AI 的“安全护城河”

在实操中,我们如何把 TPDD 落地?很简单,给 AI 配备一个与 skill.md 同等地位的 TestPlan.md(或 redline.md


(图注:TestPlan.md 的两大核心作用:左侧为“内部限制(红线与禁止区域)”,右侧为“外部验证(基于 Must/Need/Should 优先级的测试计划)”。)

这个文件只做两件事:内部限制 + 外部验证

以“AI 生成销售报表”为例,你的 TestPlan.md 应该长这样:

  • 【Must Have(必须有)】:输入 CSV,必须生成内容正确的 PDF 报表。
  • 【能力边界】:文件最大不能超过 10MB,每小时最多调用 50 次(防 Token 刷爆)。
  • 【禁止空间(红线)】绝对不允许访问报表以外的本地文件;绝对不允许将数据发送到外部服务;绝对不允许执行任意代码。
  • 【异常预期】:如果缺少字段,必须返回明确报错,而不是瞎编数据。

当 AI Agent 在生成代码或执行任务时,它必须时刻被这个 TestPlan.md 监控和约束。

【笔者观点:将 QA(质量保证)思维变成全员的本能】
在 AI 时代,QA 不应该再是一个边缘化的测试岗位,而应该成为每一个开发者的“出厂设置”。
很多只追求短期速度的“高产型团队”,前期靠狂写 Prompt 出尽风头,但很快就会被无法维护的“技术债屎山”压垮。真正能活到最后的“克制型团队”,一开始就会花大量时间去写 Contract(契约)、写红线。在 AI 时代,防守比进攻更体现一家技术公司的底蕴。

四、 CTO 的行动指南:速度不再是唯一指标

对于技术管理者(CTO)而言,面对 AI 浪潮的冲击,必须立刻在管理层面做出转向:

  1. 拔高控制层级: 保留 TDD 的理念,但不要再让员工去卷单元测试。强制所有 AI 功能模块必须配套提交 TestPlan.md
  2. 强推沙盒机制: 所有引入的第三方 Agent 和 Skill,绝对不能直接接触核心生产数据库,必须在强隔离的沙盒(Sandbox)中运行。
  3. 分级验收: 建立严格的 Must / Need / Should 分级验证机制,确保核心的 Happy Path(完美路径)和 Edge Cases(极端异常)都被覆盖。
  4. Token 预算护栏: 警惕 AI 陷入死循环导致财务失控,建立坚固的 Token 消耗阻断机制。

总结:拥抱限制,方能穿越周期

AI 极大地降低了创造的门槛,但也放大了失控的风险。

在未来的软件开发中,“能力暴露”与“禁止空间”必须如同车之双轮,并行不悖。 速度不再是第一目标。因为当任何人都能用 AI 在一小时内生成一个 App 时,只有那些 “可控、可维护、坚守安全底线” 的系统,才能真正穿越技术的周期,沉淀为长期的商业价值。


👇 欢迎关注我的公众号

在 AI 爆发的深水区,我们一起探索真正能穿越周期的技术价值。
微信搜索 【睿见新世界】 或扫描下方二维码,获取每周硬核技术推文:

微信图片_20260301232734_225_35.jpg

欢迎关注【睿见新世界】!

All Money Back Me Home

一个用于 2Libra 的油猴脚本,提供金币收支可视化报表、筛选统计与徽章展示。
image
image

功能亮点

  • 自动拉取金币流水并生成统计报表
  • 收入 / 支出趋势图与类型分布图
  • 时间范围筛选:最近 7 天、30 天、90 天、自定义、全部
  • 分类筛选:按交易 reason 过滤
  • 用户级缓存隔离(不同账号互不污染)

数据与刷新策略

  • 打开报表时:
    • 第一次打开脚本会全量加载流水,徽章数据,请耐心等待(流水较多的用户)
  • 手动刷新按钮:
    • 同时执行“流水增量更新 + 徽章刷新”
    • 有变化才重渲染并写回缓存

使用说明

  1. 点击个人信息区域,佩戴徽章右边的统计 icon
    image
  2. 查看:
    • 当前余额
    • 累计收入 / 累计支出
    • 每日收支趋势
    • 收入/支出类型分布
  3. 使用筛选器调整时间范围和分类
  4. 需要最新数据时点击“刷新”图标

缓存键说明

脚本按用户隔离缓存(命名空间基于 username/id/token 片段):

  • 流水数据:2libra_audit_data:{namespace}
  • 流水缓存时间:2libra_audit_time:{namespace}
  • 最新流水时间戳:2libra_audit_latest:{namespace}
  • 筛选条件:2libra_audit_filter:{namespace}
  • 徽章数据:2libra_audit_badges:{namespace}
  • 徽章缓存时间:2libra_audit_badges_time:{namespace}

依赖

  • Chart.js
  • DaisyUI

注意事项

  • 如果页面结构大改,按钮注入位置可能需要适配
  • 若报表异常,可清理本地缓存后重试


主要是我自己想看看金币都花哪去了,所以捣鼓了这个脚本。smirksmirk

晒一下你门的金币流水吧,看看你是入不敷出还是富得流油?

脚本安装地址

在数字化办公日益普及的今天,企业内网承载着OA、ERP、CRM等大量核心业务系统。虽然这些系统部署在内部网络,但数据传输过程中的安全隐患依然存在。根据“等保2.0”标准,通信传输中必须采用密码技术保障数据的完整性和保密性 。为内网IP地址部署SSL证书实现HTTPS加密,已成为企业合规运营的刚需。

然而,为内网IP申请SSL证书并非易事。由于内网IP(如192.168.x.x)  属于私有IP段,全球通用的公共CA(证书颁发机构)通常无法通过公网验证其所有权。但这并不意味着没有办法,JoySSL 等国内CA机构提供了完善的解决方案。

一、为什么选择JoySSL申请内网IP证书?

在选择CA时,首先要确认其是否支持签发内网IP证书。目前,仅有少数国产CA支持这一业务,JoySSL便是其中的佼佼者 。

  • 国内验签,数据不出境:符合国家法律法规要求,尤其适合政府、教育、金融等领域 。
  • 支持内网IP验证:无需开放公网端口,通过特定文件验证或邮箱验证即可完成授权。
  • 技术响应迅速:提供中文技术支持,协助解决证书部署中的各种难题。

二、内网IP申请SSL证书详细步骤

以下是基于JoySSL平台的内网IP证书申请全流程,通常只需几分钟即可完成。

第一步:注册账号并获取技术支持

内网IP证书申请入口

  1. 访问 JoySSL 官方网站。
  2. 注册一个账号。为了顺利申请IP证书,在注册过程中务必填写注册码290970。这是为了对接IP证书专员,获取申请支持和免费证书权限。
第二步:提交证书申请
  1. 登录JoySSL后台,在证书选择列表中找到  “IP SSL证书”  或联系客服获取申请链接。
  2. 在申请页面填写您的内网IP地址(例如 192.168.1.100)以及其他必要信息(如组织名称、联系方式等)。
  3. 系统将自动生成CSR(证书签名请求),请妥善保存。
第三步:完成IP所有权验证

CA机构需要确认您对该内网IP拥有管理权。JoySSL支持针对内网环境的灵活验证方式:

  • 文件验证:将CA提供的验证文件上传至该IP地址所在服务器的指定目录下,并确保可通过HTTP(或HTTPS)访问到该文件 。
  • 邮箱验证:如果该IP关联了特定的管理邮箱,可通过查收验证邮件完成确认。
第四步:下载证书并部署到服务器
  1. 验证通过后,CA将签发证书。您可以在JoySSL账户后台下载证书文件包(通常包含 crt 文件和 key 私钥) 。
  2. 部署证书:以最常用的 Nginx 服务器为例:

    • 将证书文件和私钥文件上传至服务器指定目录。
    • 修改Nginx配置文件,添加HTTPS监听:1. - 保存配置后,重载Nginx服务:nginx -s reload

三、客户端配置与注意事项

由于内网IP证书并非由浏览器内置的公网根证书直接信任,您可能需要在访问的客户端设备上进行简单配置。

  1. 导入根证书:为了彻底消除浏览器“不安全”提示,可以在客户端电脑上安装JoySSL的根证书。企业可以通过域控策略批量推送,也可以手动双击安装。
  2. 固定IP地址:确保申请的IP地址是固定的。如果IP发生变更,证书将会失效。


WPS_Setup_11.1.0.13703是 WPS Office 11.1.0.13703 版本​ 的安装包,里面有文字(Writer)、表格(Spreadsheet)、演示(Presentation)三大件,兼容 Word、Excel、PPT,日常办公、写文档、做表格都能用。

一、准备工作

  1. 下载安装包

  2. 确认系统版本

    • 支持 Win7/Win10/Win11,32 位和 64 位都能装,老电脑也没问题。
  3. 清理旧版本(可选)

    • 如果电脑里已经有旧版 WPS,最好先卸载干净(控制面板 → 程序和功能 → 找到 WPS → 卸载),避免冲突。

二、安装步骤

  1. 双击 WPS_Setup_11.1.0.13703.exe运行(右键选“以管理员身份运行”更稳)。
  2. 第一次打开会弹出“用户账户控制”提示 → 点  “是”
  3. 进入安装向导,选语言(默认中文)→ 点  “下一步”
  4. 阅读许可协议 → 选“我接受协议” → 点  “下一步”
  5. 选安装位置:

    • 默认是 C:\Program Files (x86)\Kingsoft\WPS Office,可点“浏览”改到其他盘(比如 D 盘)。
  6. 选择组件:

    • 默认全选(文字、表格、演示),不用改,直接下一步。
  7. 附加任务:

    • 建议勾“创建桌面快捷方式”,以后找软件方便;不想开机自启就别勾“开机启动 WPS”。
  8. 点  “安装” ,等进度条走完(几分钟,看电脑速度)。
  9. 安装完会问是否立即体验 → 可先取消,等会儿再开。

三、首次使用与基本操作

  1. 在桌面或开始菜单找到 WPS Office​ → 点开。
  2. 第一次打开会让登录账号(微信、QQ、手机号都行),登录能同步文档、用云空间。
  3. 新建文档:点“新建” → 选“文字”“表格”或“演示”,就能开始写东西。
  4. 打开旧文件:点“打开” → 选电脑里的 Word/Excel/PPT 文件,WPS 能直接打开编辑。
  5. 云文档:登录后,文档能存到云端,换电脑也能接着改。

龙虾实在是不得行,终究还不能算商业产品。还是得 claude code ,codex ,cursor ,gemini cli 这些才能真正干活。但是不能在聊天软件控制有点蛋疼,折腾一天,现在终于可以在微信跟飞书上控制这些 AI Agent 。10 分钟就能配好。

使用下面的项目:

github 地址:https://github.com/chenhg5/cc-connect

参考截图:

3e86f8becfb6d09bfc486f986bd75a74.JPG

15aeef6888984fa0eac05dddc83799c3.PNG

5c04ea42ad7ab909a767fa41217dce32.PNG

可以加用户群:

用户群

大家好,我是 Java陈序员

做自媒体、写博客、团队协作,经常会遇到文件托管和图床问题 —— 要么付费图床层层限速、到期失效,要么自建存储配置复杂、耗费精力,免费工具又大多功能残缺、隐私无保障。

今天,给大家介绍一款开源的文件托管工具,基于 Cloudflare 生态打造!

关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。

项目介绍

CloudFlare-ImgBed —— 一款开源的文件托管工具,支持 Docker 和无服务器部署,支持 Telegram、Discord、Cloudflare R2、S3、Huggingface 等多种存储渠道,支持 WebDAV 协议和多种 RESTful API.

功能特色

  • 快速上手:轻松部署,支持 Cloudflare Pages 免费托管和 Docker 部署,满足不同需求
  • 多样上传:支持拖拽、点击、粘贴、URL 等多种上传方式,支持批量上传和目录管理
  • 多种渠道:支持 Telegram Bot、Cloudflare R2、S3 API 等多种存储方式,满足不同需求
  • 安全可靠:支持身份认证、IP 白名单、图片审查、防滥用等多重安全保障
  • 炫酷界面:流畅丝滑的过渡动画、呼吸灯效果、灵动的操作体验,支持深色模式
  • 个性定制:支持自定义背景、Logo、配色、链接前缀等,打造专属图床

快速上手

CloudFlare-ImgBed 支持 Cloudflare Pages 部署、Docker 部署、手动部署多种方式,可根据场景自定义部署。

  • Cloudflare Pages 是推荐的部署方式,具备免费托管、全球 CDN 加速和无需服务器维护的优势。部署方式可参考官方文档:
https://cfbed.sanyue.de/deployment/cloudflare.html
  • Docker 部署适合有自己服务器的用户,提供更多的控制权和自定义能力。

Docker 命令部署

1、创建项目目录

mkdir -p /data/software/cloudflare-imgbed/data
cd /data/software/cloudflare-imgbed

2、创建配置文件 wrangler.toml:

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 可选:添加环境变量,v2.0 版本后大部分配置已迁移到管理后台
# [vars]

3、拉取镜像

docker pull marseventh/cloudflare-imgbed:latest

4、运行容器

docker run -d \
  --name cloudflare-imgbed \
  -p 7658:8080 \
  -v /data/software/cloudflare-imgbed/data:/app/data \
  -v /data/software/cloudflare-imgbed/wrangler.toml:/app/wrangler.toml \
  marseventh/cloudflare-imgbed:latest

5、启动成功后,浏览器访问

http://{IP/域名:7658}

Docker Compose 部署

1、创建项目目录

mkdir -p /data/software/cloudflare-imgbed/data
cd /data/software/cloudflare-imgbed

2、下载 Docker Compose 文件

# 下载 docker-compose.yml
wget https://raw.githubusercontent.com/MarSeventh/CloudFlare-ImgBed/main/docker-compose.yml

# 或者手动创建
curl -o docker-compose.yml https://raw.githubusercontent.com/MarSeventh/CloudFlare-ImgBed/main/docker-compose.yml

3、如果由于网络原因无法下载 Docker Compose 文件,可以手动创建 docker-compose.yml 并填写如下内容:

version: '3.8'

services:
  imgbed:
    image: marseventh/cloudflare-imgbed:latest
    ports:
      - "7658:8080"
    volumes:
      - ./wrangler.toml:/app/wrangler.toml
      - ./data:/app/data
    restart: unless-stopped

4、创建配置文件 wrangler.toml:

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 可选:添加环境变量,v2.0 版本后大部分配置已迁移到管理后台
# [vars]

5、启动服务

# 启动服务(后台运行)
docker compose up -d

# 查看日志
docker compose logs -f

6、启动成功后,浏览器访问

http://{IP/域名}:7658

设置访问密码

CloudFlare-ImgBed 默认不需要密码即可上传文件和进入管理后台,建议安装成功后首先进行设置。

1、进入系统管理、系统设置、安全设置菜单

http://{IP/域名}:7658/systemConfig#security

2、输入密码并进行保存

功能体验

文件上传

  • 文件上传

  • 历史记录

系统管理

  • 文件管理

  • 系统状态

  • 上传设置

  • 网页设置

  • 其他设置

本地开发

CloudFlare-ImgBed 依赖 Node.js 环境,需提前安装 Node.js.

1、克隆或下载项目源码

git clone https://github.com/MarSeventh/CloudFlare-ImgBed.git

2、切换到项目根目录,安装依赖

cd CloudFlare-ImgBed
npm install

3、在项目根目录下新建 wrangler.toml 配置文件

name = "cloudflare-imgbed"
compatibility_date = "2024-07-24"

# 如果需要设置环境变量,可以在这里添加
# [vars]
# AUTH_CODE = "your_auth_code"
# TG_BOT_TOKEN = "your_bot_token"
# TG_CHAT_ID = "your_chat_id"

4、运行服务

npm run start

5、启动成功后,浏览器访问

http://localhost:8080

可以说,CloudFlare-ImgBed 是一款兼顾易用性和扩展性的开源文件托管系统,尤其适合需要基于 Cloudflare 生态快速搭建图床/文件托管服务的场景,同时支持多存储渠道和完善的管理能力。无论是个人使用还是小型团队协作,都能满足文件托管的需求。

项目地址:https://github.com/MarSeventh/CloudFlare-ImgBed

最后

推荐的开源项目已经收录到 GitHub 项目,欢迎 Star

https://github.com/chenyl8848/great-open-source-project

或者访问网站,进行在线浏览:

https://chencoding.top:8090/#/

我创建了一个开源项目交流群,方便大家在群里交流、讨论开源项目

但是任何人在群里打任何广告,都会被 T 掉

如果你对这个交流群感兴趣或者在使用开源项目中遇到问题,可以通过如下方式进群

关注微信公众号:【Java陈序员】,回复【开源项目交流群】进群,或者通过公众号下方的菜单添加个人微信,并备注【开源项目交流群】,通过后拉你进群

大家的点赞、收藏和评论都是对作者的支持,如文章对你有帮助还请点赞转发支持下,谢谢!

都说江西彩礼高,最近听到朋友说秦皇岛农村一个女生的彩礼,让我三观震裂。
女生背景:家里单亲跟随父亲,家里还有个瘫痪的爷爷。女生中专生,老家干活。
男生背景:老家干干活,月入几千。父亲似乎是工地干活普通家庭一个。
两人是媒婆牵线的,谈到彩礼的时候,女生奶奶一开始说要 60W ,后面跟男方家谈的是 50 万,男方家不同意,女生降低到 45W ,还是不同意,结果两人就分了。补充下,彩礼不带回,嫁妆几床被子。。。

这要是在南方有些地方说这个高彩礼,媒婆都不敢进门,怕是有人连媒婆都一起揍了。北方彩礼真的都是这么高吗。。。

摘要:在 AI 开发进入深水区的 2026 年,如何选择靠谱的大模型 API 聚合平台服务商?本文基于延迟、价格、大模型 覆盖与合规性四大维度,评测了全球主流 AI 大模型 聚合平台(OpenRouter, n1n.ai, SiliconFlow, DMXAPI 等),助您选择合适的大模型 API 合作伙伴,构建稳定的 AI 基础设施。

目录
评测标准:我们如何定义“好用”的 AI 大模型 API 聚合平台服务商?

第一梯队:企业级首选

第二梯队:极客尝鲜首选

第三梯队:国内大模型镜像与中转

深度横评:大模型 API 价格与延迟实测数据

避坑指南:选择 AI 聚合大模型 API 时的三大陷阱

结论:2026 年谁是 AI 大模型 API 性价比之王?

  1. 评测标准:我们如何定义“好用”的 AI 大模型 API 聚合平台服务商?
    在本次测评中,我们测试了多家国内外的 AI 大模型聚合平台,重点关注以下大模型 LLM API 核心指标:

稳定性 (SLA):高并发下大模型 LLM API 是否经常超时?这是 AI 应用能否落地的关键。

模型丰富度:是否覆盖 GPT-4o, Claude 3.5, Gemini 1.5 及国产主流 AI 大模型(如 DeepSeek, 文心一言)。

合规与支付:是否支持国内企业公对公转账及发票(这是企业采购 AI 大模型服务的刚需)。

性价比:是否存在“隐形汇率差”或“AI API 价格偏高”现象。

  1. 第一梯队:企业级首选
    这一梯队的大模型 LLM API 服务商拥有比较稳定的全球专线网络,适合生产环境部署,代表了 AI 大模型基础设施的高水平。
  2. n1n.ai
    n1n.ai 是 2025 年崛起的 AI 黑马,专为企业级 MaaS (Model-as-a-Service) 设计。

核心优势:

人民币直付 LLM API:1元=1美元 的超值汇率,相当于比市场价节省 85% 的 AI 大模型 成本,很大程度降低了大模型 API 的使用门槛。

全球大模型专线:对 GPT-4 和 Claude AI 模型的 API 进行了深度链路优化,响应速度几乎等同于官方,确保 AI 大模型 对话流畅。

企业合规:支持对公转账与增值税发票,不仅提供国产大模型,还完美解决国内 AI 团队采购国外大模型 API 的合规难题。

适用场景:企业内部 AI 大模型 知识库 (RAG)、高并发大模型 Agent 部署。

  1. Azure OpenAI
    微软官方提供的企业级 AI 服务。

优势:很高的大模型 API 安全性与数据隐私合规性,是 500 强企业的 AI 大模型 首选。

劣势:申请门槛很高,且仅支持 OpenAI 系列模型,无法调用 Claude 或 Google Gemini 等其他 AI 大模型 API。

  1. 第二梯队:极客尝鲜首选
    这一梯队适合个人 AI 开发者,主打“新”和“快”。
  2. OpenRouter
    海外很知名的 LLM API 聚合商,几乎是所有 AI 极客的必选项。

优势:AI 大模型 上架速度极快,拥有大量开源微调 LLM(如 Llama 4, Mistral)。

劣势:国内连接 AI API 不稳定,经常丢包;支付仅支持国外付款方式,对国内 AI 大模型 开发者不友好。

  1. SiliconFlow (硅基流动)
    国内知名的开源 AI 大模型推理平台。

优势:在开源 LLM(如 Qwen, DeepSeek)的推理速度上很具有优势,是测试国产 AI 大模型的佳选。

劣势:对闭源商业模型(如 GPT-4, Claude)的 API 支持较弱,无法满足全能型 AI 大模型开发需求。

  1. 第三梯队:国内大模型镜像与中转
  2. DMXAPI / OneAPI 等
    这些平台通常由社区运营,主打“中转大模型 LLM API”。

特点:API 价格通常较官方有折扣,但 AI 服务的稳定性参差不齐。

风险:部分小平台存在“跑路”风险,且常用低价 AI 模型替换 GPT-4。建议 AI 开发者在选择此类 LLM API 时谨慎测试。

  1. 深度横评:大模型 API 价格与延迟实测数据
    我们选取了标准的 GPT-4o LLM API 接口,在晚高峰(20:00)进行了 100 次并发测试,以验证各家 AI 大模型的真实表现。

服务商 | 平均AI延迟 | API成功率 | 1M Token 价格 (CNY)

n1n.ai | 320ms | 99.9% | ¥7.5(1:1汇率)

OpenRouter | 850ms | 92% | ¥55(需换汇)

Azure | 280ms | 99.9% | ¥72(官方API价)

DMXAPI | 1200ms+ | 85% | ¥30(波动大)

SiliconFlow | N/A (不支持) | N/A | N/A

数据解读:

稳定性:n1n.ai 与 Azure 并列第一,远超普通中转大模型 API 服务商,是 AI 生产环境的定海神针。

价格:得益于 1:1 的汇率补贴策略,n1n.ai 在保证企业级大模型 SLA 的前提下,做到了全网很低的大模型 AI API 实际支付成本。

  1. 指南:选择大模型聚合 API 的三大注意点
    一:低价(AI API 价格偏高)
    很多平台标榜“官方半价”,但充值汇率高达 1:10。这意味着你由于汇率差,实际支付了很昂贵的 LLM 费用。需要认准像 n1n.ai 这样公开承诺 1元=1美元 的 AI 大模型聚合服务商。

二:模型不对
小平台常将 GPT-3.5 包装成 GPT-4 售卖。这对 AI 应用效果有很大影响的。可以使用复杂的逻辑题(如“鲁迅打周树人”)进行测试。正规的 LLM API 大模型聚合商(如 OpenRouter, n1n.ai)不会进行 AI 模型替换。

三:合规发票
对于国内企业,没有发票意味着无法报销,这将阻碍 AI 项目的推进。国外平台(OpenRouter)通常无法提供国内认可的发票。请务必选择支持国内对公结算的 AI 大模型服务商。

  1. 结论:2026 年谁是 AI 大模型 API 性价比之王?
    综合 AI 模型 覆盖、API 网络稳定性、支付便捷性 三大维度,我们的评测结论如下:

对于企业/团队:首选 n1n.ai。

理由:它不仅是 LLM API 聚合工具,更是企业级 AI 大模型 基础设施。1:1 的汇率优势加上全球 AI 专线加速,使其成为 2026 年很具竞争力的 MaaS 平台。

对于个人极客:推荐 OpenRouter。

理由:如果你不需要发票,且能搞定海外支付,OpenRouter 是探索冷门 AI 模型的乐园,也是 LLM 生态的重要组成部分。

对于纯开源玩家:推荐 SiliconFlow。

理由:专注国产开源 大模型 推理,速度很快。

选择合适的 LLM API 聚合商,是构建成功 AI 应用的第一步。希望这份测评能助您在 2026 年的 AI 大模型 浪潮中乘风破浪,用优质的 API 驱动您的每一个 AI 创意。

往年的推荐感谢 v 友支持,刚好能喝上一杯 9.9 的瑞幸了。
今年去除了往年用起来繁琐的收纳挂袋,因为衣物晒完后还需要手动塞进收纳袋,如果晾晒的操作已经完成了收纳的操作,就不需要多做一步了。
去除了真空压缩收纳袋,主要还是不能擂放,透气衣物不适合放这类袋子。改成了透气的百纳箱。
去除了正常电商平台已下架的商品。但有些设计还是不错的,比如松下的山形结构两孔和三孔不占座可以同时使用。
新增了 v 友经常提的蕉内莫代尔内裤,其他品牌还没试过不知道有哪些推荐?
新增了迪卡侬袜子,主要是品类多容易补货,它的物品各个价位段都有。
新增了浸塑防滑衣架,从防滑坚固实用角度,55g 重的衣架也够了。
新增了 3M 的浴室挂钩,买过很多便宜挂钩,一分钱一分货便宜的尺寸小。
新增了宜家 不锈钢镜子,买过其他品牌的生锈了,MUJI 铝制的又天价。
新增了爱丽思 IRIS 脏衣篓,比起其他更多的是它本身好清洗。
新增了设计更合理的不锈钢盆碟,选了浅边的,弧形底过度圆滑的。
新增了永远也找不到完美设计的盐罐,各种设计都有问题。折中选个结构简单的。
新增了可调节厨房垃圾桶,主要是因为家人都喜欢拿买菜的塑料袋装垃圾,而这款垃圾桶专为这种需求设计。卖垃圾桶的强力竞争对手来了,它不挑垃圾袋,但目前只找到 pdd 有售。
新增了小米的不锈钢电热水壶,康宁的玻璃壶身养生壶,象印的保温壶,中年男人三件套?
新增了图拉斯亚克力透明硬质手机壳。
新增了 2026 年还有人用有线 EarPods 耳机?
源:github.com/thesomeexp/Awesome-Shopping

大家好,我是良许

在嵌入式系统开发领域,SoC(System on Chip,片上系统)已经成为了现代电子产品的核心。

从我们日常使用的智能手机、智能手表,到工业控制设备、汽车电子系统,SoC 无处不在。

作为一名从事嵌入式开发多年的工程师,我见证了 SoC 技术的快速发展,也在实际项目中深刻体会到了 SoC 给产品设计带来的革命性变化。

今天,我想和大家深入聊聊 SoC 的设计理念和实际应用。

1. SoC 的基本概念与架构

1.1 什么是 SoC

SoC,顾名思义,就是把一个完整的系统集成到一块芯片上。

传统的电子系统设计中,我们需要 CPU、内存、各种外设控制器、电源管理等多个独立芯片协同工作。

而 SoC 则将这些功能模块全部集成到一颗芯片内部,大大减少了 PCB 板的面积、降低了功耗,同时也提升了系统的可靠性。

举个简单的例子,早期的手机主板上可能需要十几颗甚至几十颗芯片,包括处理器、基带芯片、音频芯片、电源管理芯片等等。

而现在的智能手机,核心功能基本都集成在一两颗 SoC 芯片里了,这就是 SoC 带来的巨大变革。

1.2 SoC 的典型架构

一个典型的 SoC 通常包含以下几个核心部分:

1.2.1 处理器核心

这是 SoC 的大脑,负责执行程序指令。

现代 SoC 往往采用多核架构,比如 ARM Cortex-A 系列的高性能核心配合 Cortex-M 系列的低功耗核心,实现性能与功耗的平衡。

以 STM32MP1 系列为例,它集成了双核 Cortex-A7(主频 650MHz)和单核 Cortex-M4(主频 209MHz),A7 核心运行 Linux 系统处理复杂任务,M4 核心则负责实时性要求高的控制任务。

1.2.2 存储子系统

包括片上 SRAM、ROM,以及外部存储器接口(如 DDR 控制器、Flash 控制器)。

片上存储器速度快但容量有限,主要用于关键代码和数据的存储。

外部存储器接口则允许连接大容量的 DDR 内存和 Flash 存储器。

1.2.3 外设接口

这是 SoC 与外部世界交互的桥梁,包括 GPIO、UART、SPI、I2C、USB、以太网、CAN 等各种通信接口,以及 ADC、DAC、定时器、PWM 等功能模块。

以我之前做汽车电子项目时使用的 NXP i.MX 系列 SoC 为例,它集成了多达 8 个 CAN 接口,非常适合车载网络应用。

1.2.4 互连总线

负责连接 SoC 内部各个模块,常见的有 AHB、APB、AXI 等总线协议。

总线的设计直接影响系统的性能和功耗,高性能模块通常连接到高速总线上,而低速外设则连接到低速总线以降低功耗。

1.2.5 电源管理单元

现代 SoC 都集成了复杂的电源管理功能,可以根据工作负载动态调整各个模块的电压和时钟频率,实现精细的功耗控制。这在电池供电的移动设备中尤为重要。

2. SoC 的设计流程与关键技术

2.1 SoC 设计的基本流程

SoC 的设计是一个复杂的系统工程,通常包括以下几个阶段:

2.1.1 需求分析与规格定义

这是整个设计的起点,需要明确 SoC 的应用场景、性能指标、功耗要求、成本目标等。

比如设计一款用于智能音箱的 SoC,就需要重点考虑音频处理能力、低功耗待机、WiFi 连接等需求。

2.1.2 架构设计

根据需求选择合适的处理器核心、确定存储器配置、规划外设接口、设计互连总线等。

这个阶段需要在性能、功耗、面积、成本之间做权衡。

架构设计的好坏直接决定了最终产品的竞争力。

2.1.3 RTL 设计与验证

使用硬件描述语言(如 Verilog 或 VHDL)实现各个功能模块的寄存器传输级(RTL)设计,并进行功能仿真验证。

这个阶段需要大量的仿真测试,确保设计的正确性。

2.1.4 物理设计

包括逻辑综合、布局布线、时序分析等步骤,将 RTL 设计转换为实际的物理版图。

这个阶段需要考虑工艺参数、信号完整性、功耗优化等诸多因素。

2.1.5 流片与测试

将设计交给晶圆厂制造,然后进行芯片测试和验证。

这是最烧钱的环节,一次流片的成本可能高达数百万美元,所以前期的验证工作必须做得非常充分。

2.2 SoC 设计的关键技术

2.2.1 低功耗设计

功耗是 SoC 设计中最重要的考量之一。

常用的低功耗技术包括:

  • 多电压域设计:将 SoC 划分为多个电压域,不同的模块使用不同的供电电压
  • 动态电压频率调节(DVFS):根据负载动态调整工作电压和频率
  • 时钟门控:在模块空闲时关闭时钟,减少动态功耗
  • 电源门控:在模块不使用时完全切断供电,消除静态功耗

在我做过的一个项目中,通过合理使用 STM32 的低功耗模式,将待机功耗从几十毫安降低到了几微安,大大延长了电池寿命。

2.2.2 片上互连技术

随着 SoC 集成度的提高,片上互连成为性能瓶颈。

现代 SoC 普遍采用 NoC(Network on Chip,片上网络)技术,将传统的总线结构替换为网络结构,提供更高的带宽和更好的可扩展性。

2.2.3 IP 复用

为了缩短设计周期、降低风险,SoC 设计大量使用经过验证的 IP 核(Intellectual Property Core)。

比如 ARM 的处理器核心、Cadence 的接口 IP、Synopsys 的存储器控制器等。

合理选择和集成 IP 核是 SoC 设计成功的关键。

3. SoC 在嵌入式系统中的应用

3.1 消费电子领域

智能手机是 SoC 应用最成功的领域之一。

以高通的骁龙系列、苹果的 A 系列、华为的麒麟系列为代表,这些 SoC 集成了高性能 CPU、GPU、DSP、ISP(图像信号处理器)、基带等多个功能模块,支撑起了现代智能手机的强大功能。

在智能家居领域,ESP32 是一个非常典型的例子。

这颗由乐鑫科技设计的 SoC 集成了双核处理器、WiFi 和蓝牙模块,价格却非常亲民,成为了 IoT 设备的首选方案。

我曾用 ESP32 做过一个智能门锁项目,开发体验非常好:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_wifi.h"
​
#define LOCK_PIN GPIO_NUM_5
​
void lock_control_task(void *pvParameters) {
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << LOCK_PIN),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&io_conf);
    
    while(1) {
        // 接收WiFi控制命令
        // 控制门锁开关
        gpio_set_level(LOCK_PIN, 1);  // 开锁
        vTaskDelay(3000 / portTICK_PERIOD_MS);
        gpio_set_level(LOCK_PIN, 0);  // 锁门
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
​
void app_main() {
    // 初始化WiFi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);
    
    // 创建门锁控制任务
    xTaskCreate(lock_control_task, "lock_control", 2048, NULL, 5, NULL);
}

3.2 汽车电子领域

汽车电子是我工作的主要领域,这里的 SoC 应用非常广泛。

从仪表盘、中控娱乐系统,到 ADAS(高级驾驶辅助系统)、自动驾驶域控制器,都离不开高性能 SoC 的支持。

以 NXP 的 S32 系列为例,这是专门为汽车应用设计的 SoC 家族,集成了 ARM Cortex-R 和 Cortex-M 核心,支持 CAN FD、以太网等车载网络协议,并且通过了严格的车规级认证(如 AEC-Q100)。

我在项目中使用 S32K 系列做过一个电池管理系统(BMS),它的多核架构让我可以把安全关键任务和非关键任务分离:

// Cortex-M4核心运行的安全关键任务
void safety_task(void) {
    while(1) {
        // 读取电池电压
        float voltage = HAL_ADC_GetValue(&hadc1);
        
        // 读取电池温度
        float temperature = read_temperature_sensor();
        
        // 安全检查
        if(voltage > MAX_VOLTAGE || temperature > MAX_TEMP) {
            // 触发保护机制
            HAL_GPIO_WritePin(RELAY_PORT, RELAY_PIN, GPIO_PIN_RESET);
            trigger_emergency_shutdown();
        }
        
        HAL_Delay(10);  // 10ms周期
    }
}
​
// Cortex-M0+核心运行的通信任务
void communication_task(void) {
    while(1) {
        // 通过CAN总线发送电池状态
        CAN_TxHeaderTypeDef tx_header;
        uint8_t tx_data[8];
        
        tx_header.StdId = 0x123;
        tx_header.DLR = 8;
        
        // 打包电池数据
        pack_battery_data(tx_data);
        
        // 发送CAN消息
        HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data, NULL);
        
        HAL_Delay(100);  // 100ms周期
    }
}

特斯拉的 FSD(Full Self-Driving)芯片更是将 SoC 推向了极致,集成了两颗定制的神经网络处理器(NPU),算力高达 144TOPS,专门用于处理自动驾驶所需的海量传感器数据。

3.3 工业控制领域

在工业控制领域,SoC 同样发挥着重要作用。

TI 的 Sitara 系列、NXP 的 i.MX 系列都是工业级 SoC 的代表。

这些 SoC 通常集成了实时处理单元(如 PRU)、工业以太网接口(如 EtherCAT、PROFINET)、高精度 ADC 等工业应用所需的功能。

我曾参与过一个工业机器人控制器的项目,使用的是 TI 的 AM335x SoC。

它的特色是集成了两个 PRU(Programmable Real-time Unit),这是两个独立的 32 位 RISC 核心,专门用于实时控制任务,可以实现纳秒级的响应时间:

// PRU代码示例(使用C语言,编译为PRU指令)
#include <stdint.h>
#include "pru_ctrl.h"
​
#define GPIO_SETDATAOUT   0x194
#define GPIO_CLEARDATAOUT 0x190
#define STEP_PIN          (1 << 15)
​
volatile uint32_t *gpio1 = (volatile uint32_t *)0x4804C000;
​
void main(void) {
    uint32_t step_count = 0;
    uint32_t delay_cycles = 1000;  // 控制步进电机速度
    
    while(1) {
        // 产生步进脉冲
        gpio1[GPIO_SETDATAOUT/4] = STEP_PIN;
        __delay_cycles(delay_cycles);
        gpio1[GPIO_CLEARDATAOUT/4] = STEP_PIN;
        __delay_cycles(delay_cycles);
        
        step_count++;
        
        // 每1000步调整一次速度
        if(step_count >= 1000) {
            step_count = 0;
            // 从主机读取新的速度参数
            delay_cycles = read_shared_memory();
        }
    }
}

PRU 的优势在于它完全独立于主 CPU 运行,不受操作系统调度的影响,可以保证确定性的实时响应,这在工业控制中至关重要。

3.4 物联网领域

物联网是 SoC 应用增长最快的领域。

这个领域的 SoC 通常强调低功耗、小尺寸、低成本,同时集成无线通信功能。Nordic 的 nRF 系列、ST 的 STM32WB 系列都是典型代表。

STM32WB 系列特别有意思,它集成了双核处理器:Cortex-M4 核心运行用户应用,Cortex-M0+ 核心专门处理蓝牙协议栈。

这种架构让开发者可以专注于应用开发,而不用担心无线协议的复杂性:

#include "stm32wbxx_hal.h"
#include "ble.h"
​
// M4核心运行的应用代码
void sensor_task(void) {
    float temperature, humidity;
    
    while(1) {
        // 读取传感器数据
        temperature = read_temperature();
        humidity = read_humidity();
        
        // 通过BLE发送数据(实际由M0+核心处理)
        BLE_UpdateCharacteristic(TEMP_CHAR_HANDLE, 
                                 (uint8_t*)&temperature, 
                                 sizeof(temperature));
        BLE_UpdateCharacteristic(HUMIDITY_CHAR_HANDLE, 
                                 (uint8_t*)&humidity, 
                                 sizeof(humidity));
        
        // 进入低功耗模式
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, 
                              PWR_STOPENTRY_WFI);
        
        HAL_Delay(60000);  // 每分钟上报一次
    }
}
​
// BLE事件回调(由M0+核心触发)
void BLE_EventCallback(BLE_Event_t event) {
    switch(event) {
        case BLE_CONNECTED:
            // 设备已连接
            HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
            break;
            
        case BLE_DISCONNECTED:
            // 设备已断开
            HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
            break;
            
        case BLE_DATA_RECEIVED:
            // 接收到控制命令
            process_command(BLE_GetReceivedData());
            break;
    }
}

4. SoC 选型与开发建议

4.1 如何选择合适的 SoC

选择 SoC 是项目成功的第一步,需要综合考虑多个因素:

4.1.1 性能需求

首先要明确应用的计算需求。

如果只是简单的传感器数据采集和控制,Cortex-M 系列就足够了;如果需要运行 Linux 系统、处理图像视频,就需要 Cortex-A 系列。

不要过度设计,性能过剩会带来不必要的成本和功耗。

4.1.2 功耗要求

对于电池供电的设备,功耗是首要考虑因素。

要仔细研究 SoC 的功耗数据手册,包括运行功耗、待机功耗、唤醒时间等。

有些 SoC 虽然运行功耗低,但待机功耗高,反而不适合间歇工作的应用。

4.1.3 外设接口

确保 SoC 集成了项目所需的所有外设接口,包括数量和类型。

比如做电机控制,需要足够多的 PWM 通道和高速 ADC;做通信网关,需要多个 UART 和以太网接口。

4.1.4 开发生态

完善的开发生态可以大大降低开发难度和周期。

要考察 SoC 的开发工具链、软件库、技术文档、社区支持等。STM32 之所以流行,很大程度上是因为它有完善的 HAL 库、丰富的例程和活跃的社区。

4.1.5 供货和成本

要考虑 SoC 的长期供货能力和价格趋势。

特别是工业和汽车应用,产品生命周期可能长达十年以上,必须确保芯片供应商能够提供长期支持。

4.2 SoC 开发的最佳实践

4.2.1 充分利用硬件加速

现代 SoC 集成了很多硬件加速模块,如 DMA、硬件 CRC、加密引擎等。

充分利用这些硬件资源可以大大提升性能、降低 CPU 负载。

比如在做数据传输时,使用 DMA 而不是 CPU 轮询,可以让 CPU 腾出时间处理其他任务:

// 使用DMA传输数据的示例
void setup_dma_transfer(void) {
    // 配置DMA
    hdma_uart_tx.Instance = DMA1_Channel4;
    hdma_uart_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_uart_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart_tx.Init.Mode = DMA_NORMAL;
    hdma_uart_tx.Init.Priority = DMA_PRIORITY_LOW;
    HAL_DMA_Init(&hdma_uart_tx);
    
    // 关联DMA和UART
    __HAL_LINKDMA(&huart1, hdmatx, hdma_uart_tx);
    
    // 启动DMA传输
    uint8_t data[1024];
    prepare_data(data, sizeof(data));
    HAL_UART_Transmit_DMA(&huart1, data, sizeof(data));
    
    // CPU可以去做其他事情,传输完成后会触发中断
}
​
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    // DMA传输完成回调
    if(huart == &huart1) {
        // 处理传输完成后的逻辑
        transmission_complete_handler();
    }
}

4.2.2 合理的软件架构

对于复杂的应用,建议使用 RTOS(实时操作系统)来管理多个任务。

FreeRTOS 是一个非常好的选择,它轻量级、可靠,并且被广泛支持。

使用 RTOS 可以让代码结构更清晰、更容易维护:

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
​
QueueHandle_t sensor_queue;
​
// 传感器读取任务
void sensor_task(void *pvParameters) {
    sensor_data_t data;
    
    while(1) {
        data.temperature = read_temperature();
        data.pressure = read_pressure();
        data.timestamp = HAL_GetTick();
        
        // 将数据发送到队列
        xQueueSend(sensor_queue, &data, portMAX_DELAY);
        
        vTaskDelay(pdMS_TO_TICKS(100));  // 100ms采样周期
    }
}
​
// 数据处理任务
void process_task(void *pvParameters) {
    sensor_data_t data;
    
    while(1) {
        // 从队列接收数据
        if(xQueueReceive(sensor_queue, &data, portMAX_DELAY) == pdTRUE) {
            // 数据处理
            float filtered_temp = apply_filter(data.temperature);
            
            // 异常检测
            if(filtered_temp > THRESHOLD) {
                trigger_alarm();
            }
            
            // 数据存储
            store_to_flash(&data);
        }
    }
}
​
int main(void) {
    HAL_Init();
    SystemClock_Config();
    
    // 创建队列
    sensor_queue = xQueueCreate(10, sizeof(sensor_data_t));
    
    // 创建任务
    xTaskCreate(sensor_task, "Sensor", 256, NULL, 2, NULL);
    xTaskCreate(process_task, "Process", 512, NULL, 1, NULL);
    
    // 启动调度器
    vTaskStartScheduler();
    
    while(1);
}

4.2.3 电源管理优化

在电池供电的应用中,精细的电源管理至关重要。

要充分利用 SoC 的低功耗模式,在不需要工作时让系统进入睡眠状态:

void enter_low_power_mode(void) {
    // 关闭不需要的外设
    HAL_ADC_Stop(&hadc1);
    HAL_TIM_Base_Stop(&htim2);
    
    // 配置唤醒源(比如RTC定时器)
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 60, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    
    // 进入停止模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    // 唤醒后恢复系统时钟
    SystemClock_Config();
    
    // 重新启动外设
    HAL_ADC_Start(&hadc1);
    HAL_TIM_Base_Start(&htim2);
}

4.2.4 调试与优化

SoC 开发中,调试是一个挑战。

要善用各种调试工具,如 JTAG 调试器、逻辑分析仪、示波器等。

对于性能优化,可以使用 SoC 内置的性能计数器和追踪单元(如 ARM 的 ETM)来分析程序的运行情况。

5. SoC 的未来发展趋势

5.1 异构计算

未来的 SoC 将集成更多种类的处理单元,包括 CPU、GPU、DSP、NPU(神经网络处理器)等,形成异构计算架构。

不同的任务由最适合的处理单元来执行,实现性能和能效的最优化。

比如苹果的 M 系列芯片,就集成了高性能 CPU 核心、高效能 CPU 核心、GPU、神经网络引擎、视频编解码引擎等多种处理单元。

5.2 AI 加速

随着人工智能的普及,越来越多的 SoC 开始集成 AI 加速器。

这些专用硬件可以高效地执行神经网络推理任务,让边缘设备也能运行复杂的 AI 算法。

比如 Google 的 Edge TPU、华为的达芬奇架构 NPU 等。

5.3 先进制程

SoC 的制程工艺不断进步,从 28nm、14nm 到现在的 5nm、3nm,甚至未来的 2nm。

更先进的制程带来更高的集成度、更低的功耗和更强的性能。

但同时,设计和制造成本也在急剧上升,这使得只有少数公司能够负担得起最先进制程的 SoC 开发。

5.4 chiplet 技术

为了应对先进制程的高成本,chiplet(小芯片)技术正在兴起。

这种技术将 SoC 分解为多个小芯片,每个小芯片使用最适合的制程工艺制造,然后通过先进封装技术组装在一起。

这样可以在控制成本的同时,实现高性能和高集成度。

AMD 的 Ryzen 和 EPYC 处理器就采用了 chiplet 架构。

5.5 开源硬件

RISC-V 等开源指令集架构的兴起,正在改变 SoC 设计的格局。

开源硬件降低了 SoC 设计的门槛,让更多的公司和个人能够参与到芯片设计中来。

中国的很多芯片公司已经开始基于 RISC-V 开发 SoC 产品,这个趋势在未来会更加明显。

结语

SoC 技术的发展深刻地改变了电子产品的设计方式。

作为嵌入式工程师,我们既要掌握 SoC 的使用方法,也要理解其背后的设计原理。

只有这样,才能在项目中做出正确的技术选择,开发出高性能、低功耗、高可靠性的产品。

从我个人的经验来看,SoC 开发最重要的是要有系统思维。

不能只关注某个模块或某个功能,而要从整体上考虑系统的性能、功耗、成本等各个方面。

同时,要保持学习的热情,因为这个领域的技术更新非常快,只有不断学习,才能跟上时代的步伐。

希望这篇文章能够帮助大家更好地理解 SoC 的设计和应用。

如果你在实际项目中遇到了问题,欢迎和我交流讨论。让我们一起在嵌入式开发的道路上不断前行!

更多编程学习资源