[{"content":"C++ 智能指针的核心并不是“自动释放内存”，而是用类型明确表达对象的所有权。当智能指针离开作用域时，它会自动完成资源释放，这正是 RAII（Resource Acquisition Is Initialization，资源获取即初始化）的典型应用。\n1. 为什么需要智能指针 使用裸指针管理动态内存时，程序员必须确保每一条执行路径都能正确调用 delete：\n1 2 3 4 5 6 7 8 9 10 11 void process() { auto* model = new Model(); if (!model-\u0026gt;load()) { delete model; return; } run(*model); delete model; } 一旦中途抛出异常、增加新的 return，或者所有权在多个函数之间传递，就容易产生：\n内存泄漏； 重复释放； 悬空指针； 所有权不清晰。 使用 std::unique_ptr 后，析构过程由对象生命周期自动管理：\n1 2 3 4 5 6 7 8 9 void process() { auto model = std::make_unique\u0026lt;Model\u0026gt;(); if (!model-\u0026gt;load()) { return; } run(*model); } // 自动析构 Model 2. std::unique_ptr：独占所有权 std::unique_ptr\u0026lt;T\u0026gt; 表示：当前资源只有一个所有者。它不能复制，只能移动。\n1 2 3 4 5 6 7 8 9 #include \u0026lt;memory\u0026gt; auto model = std::make_unique\u0026lt;Model\u0026gt;(\u0026#34;model.onnx\u0026#34;); // 错误：不能复制独占所有权 // auto model2 = model; // 正确：转移所有权 auto model2 = std::move(model); 移动后，model 变为空指针，资源由 model2 管理。\n函数接口如何表达所有权 1 2 3 4 void observe(const Model\u0026amp; model); // 只使用，不接管 void observe(const Model* model); // 可为空的观察者 void consume(std::unique_ptr\u0026lt;Model\u0026gt; model); // 接管所有权 std::unique_ptr\u0026lt;Model\u0026gt; createModel(); // 返回所有权 如果函数只临时使用对象，不要为了“看起来安全”而传递智能指针；使用引用或裸指针作为非拥有型观察者即可。\n数组 1 2 auto buffer = std::make_unique\u0026lt;float[]\u0026gt;(1024); buffer[0] = 1.0f; 现代代码更推荐 std::vector\u0026lt;float\u0026gt;，因为它同时保存长度信息，也更方便传给算法接口。\n自定义删除器 智能指针不只能管理 new 创建的内存，还能管理文件、系统句柄或 C API 资源：\n1 2 3 4 5 6 7 8 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;memory\u0026gt; using FilePtr = std::unique_ptr\u0026lt;FILE, decltype(\u0026amp;std::fclose)\u0026gt;; FilePtr openFile(const char* path) { return FilePtr(std::fopen(path, \u0026#34;rb\u0026#34;), \u0026amp;std::fclose); } 对于 ONNX Runtime、OpenVINO 或相机 SDK，也可以用同样方式把对应的释放函数放入删除器。\ncv::Mat、std::vector、std::string 本身已经使用 RAII，不需要再套一层智能指针。\n3. std::shared_ptr：共享所有权 std::shared_ptr\u0026lt;T\u0026gt; 允许多个对象共同拥有同一资源。最后一个所有者销毁时，资源才会释放。\n1 2 3 4 5 6 auto session = std::make_shared\u0026lt;InferenceSession\u0026gt;(); auto worker1 = session; auto worker2 = session; std::cout \u0026lt;\u0026lt; session.use_count() \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; 它通常包含两个部分：\n指向实际对象的指针； 控制块，其中保存强引用计数、弱引用计数和删除器。 优先使用 std::make_shared：\n1 auto session = std::make_shared\u0026lt;InferenceSession\u0026gt;(config); 这通常能把对象和控制块放在一次内存分配中，同时避免构造过程中的异常安全问题。\n不要因为“不确定”就使用 shared_ptr 引用计数会增加内存占用，并带来原子计数操作。更重要的是，它容易掩盖本应明确的生命周期设计。只有资源确实需要被多个长期存在的对象共同拥有时，才应该使用它。\n线程安全不等于对象安全 不同 shared_ptr 实例对同一控制块进行复制和销毁通常是线程安全的，但它指向的对象并不会因此自动变成线程安全：\n1 2 3 4 auto model = std::make_shared\u0026lt;Model\u0026gt;(); // 引用计数可以安全变化， // 但多个线程同时调用 model-\u0026gt;run() 是否安全，仍由 Model 决定。 4. std::weak_ptr：观察共享对象 std::weak_ptr\u0026lt;T\u0026gt; 不增加强引用计数，用来观察由 shared_ptr 管理的对象。\n1 2 3 4 5 6 7 std::weak_ptr\u0026lt;InferenceSession\u0026gt; weakSession = session; if (auto locked = weakSession.lock()) { locked-\u0026gt;run(); } else { // 对象已经被释放 } 不要先调用 expired() 再使用对象，因为两次操作之间对象仍可能被其他线程释放。直接调用 lock()，得到一个临时 shared_ptr。\n解决循环引用 1 2 3 4 5 6 7 8 9 struct Child; struct Parent { std::shared_ptr\u0026lt;Child\u0026gt; child; }; struct Child { std::weak_ptr\u0026lt;Parent\u0026gt; parent; }; 如果 Parent 和 Child 都使用 shared_ptr 指向对方，它们的引用计数永远不会归零。将不负责拥有对方的一侧改成 weak_ptr，即可打破循环。\n5. 常见错误 用同一个裸指针创建多个 shared_ptr 1 2 3 4 auto* raw = new Model(); std::shared_ptr\u0026lt;Model\u0026gt; a(raw); std::shared_ptr\u0026lt;Model\u0026gt; b(raw); // 错误：两个独立控制块，会重复释放 正确做法是复制已有的 shared_ptr：\n1 2 auto a = std::make_shared\u0026lt;Model\u0026gt;(); auto b = a; 随意调用 get() 1 Model* raw = model.get(); // 只借用，不拥有 get() 返回的裸指针不能被 delete，也不应该被保存到超过智能指针生命周期的位置。\n混淆 release() 和 reset() 1 2 3 4 5 6 7 auto ptr = std::make_unique\u0026lt;Model\u0026gt;(); Model* raw = ptr.release(); // 放弃所有权，不释放对象 delete raw; ptr.reset(new Model()); // 释放旧对象并接管新对象 ptr.reset(); // 释放并置空 除非必须把资源交给接管所有权的旧式 C API，否则应谨慎使用 release()。\n对 this 重新创建 shared_ptr 如果对象已经由 shared_ptr 管理，不要写：\n1 std::shared_ptr\u0026lt;Model\u0026gt; self(this); // 可能形成第二个控制块 需要安全获得自身的 shared_ptr 时，应继承 std::enable_shared_from_this\u0026lt;T\u0026gt;：\n1 2 3 4 5 6 class Model : public std::enable_shared_from_this\u0026lt;Model\u0026gt; { public: std::shared_ptr\u0026lt;Model\u0026gt; self() { return shared_from_this(); } }; 6. C ABI 推理库中的用法 C++ 智能指针不能直接跨 C ABI 导出，也不适合直接暴露给 C# P/Invoke。常见做法是对外提供不透明句柄，对内使用 RAII。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Runtime { public: explicit Runtime(const char* modelPath); void detect(const unsigned char* data, int width, int height); }; extern \u0026#34;C\u0026#34; { void* CreateRuntime(const char* modelPath) { try { return new Runtime(modelPath); } catch (...) { return nullptr; } } void Detect(void* handle, const unsigned char* data, int width, int height) { if (!handle) { return; } auto* runtime = static_cast\u0026lt;Runtime*\u0026gt;(handle); runtime-\u0026gt;detect(data, width, height); } void DestroyRuntime(void* handle) { delete static_cast\u0026lt;Runtime*\u0026gt;(handle); } } 在 DLL 内部，Runtime 可以继续使用智能指针管理模型资源：\n1 2 3 4 5 class Runtime { private: std::unique_ptr\u0026lt;Ort::Session\u0026gt; session_; std::vector\u0026lt;float\u0026gt; inputBuffer_; }; 关键原则是：在哪个模块创建，就在哪个模块释放。C# 只保存句柄，并确保最终调用 DestroyRuntime；不要让 C# 直接释放由 C++ DLL 分配的对象。\n如果一个模型会被多个推理任务长期共享，可以在内部使用 shared_ptr\u0026lt;Session\u0026gt;；如果每个 Runtime 独占自己的模型会话，则优先使用 unique_ptr。\n7. 如何选择 需求 推荐类型 单一对象独占资源 std::unique_ptr\u0026lt;T\u0026gt; 多个对象共同决定资源生命周期 std::shared_ptr\u0026lt;T\u0026gt; 观察共享资源，不延长生命周期 std::weak_ptr\u0026lt;T\u0026gt; 必须可为空，但不拥有资源 裸指针 T* 必须存在且不拥有资源 引用 T\u0026amp; 连续动态数组 std::vector\u0026lt;T\u0026gt; 实际工程中可以遵循一个简单顺序：\n默认让对象直接按值存在； 必须动态分配时，优先 unique_ptr； 确实存在共享所有权时，才使用 shared_ptr； 用 weak_ptr 表达非拥有关系并打破循环引用； 除非接口明确转移所有权，否则裸指针只作为观察者。 智能指针真正解决的是资源生命周期和所有权表达问题。选对所有权模型，比单纯把 new/delete 替换成 shared_ptr 更重要。\n","date":"2026-09-09T22:00:00+08:00","permalink":"https://blog.mn00.net/p/cpp-smart-pointers/","title":"C++ 智能指针：从 RAII 到工程实践"},{"content":"Linux 配置 HTTP 代理 在网络受限或需要统一出口的环境中，可以通过 HTTP 代理访问软件仓库、容器镜像仓库和 Python 包索引。下面以代理地址 http://127.0.0.1:7890 为例，请按实际地址和端口替换。\nLinux 环境变量 临时配置 只对当前终端会话生效：\n1 2 3 4 5 6 export http_proxy=\u0026#34;http://127.0.0.1:7890\u0026#34; export https_proxy=\u0026#34;http://127.0.0.1:7890\u0026#34; export HTTP_PROXY=\u0026#34;$http_proxy\u0026#34; export HTTPS_PROXY=\u0026#34;$https_proxy\u0026#34; export no_proxy=\u0026#34;localhost,127.0.0.1,::1\u0026#34; export NO_PROXY=\u0026#34;$no_proxy\u0026#34; 可以用 curl 验证代理是否能正常访问外网：\n1 curl -x \u0026#34;http://127.0.0.1:7890\u0026#34; https://ipinfo.io/ip 持久配置 将上面的 export 命令写入当前用户的 shell 配置文件。Bash 通常使用 ~/.bashrc，Zsh 通常使用 ~/.zshrc：\n1 2 3 4 5 6 7 8 9 10 cat \u0026gt;\u0026gt; ~/.bashrc \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; export http_proxy=\u0026#34;http://127.0.0.1:7890\u0026#34; export https_proxy=\u0026#34;http://127.0.0.1:7890\u0026#34; export HTTP_PROXY=\u0026#34;$http_proxy\u0026#34; export HTTPS_PROXY=\u0026#34;$https_proxy\u0026#34; export no_proxy=\u0026#34;localhost,127.0.0.1,::1\u0026#34; export NO_PROXY=\u0026#34;$no_proxy\u0026#34; EOF source ~/.bashrc 如果需要让通过 systemd 启动的服务使用代理，应在对应服务中设置环境变量，而不是只修改用户的 shell 配置。例如：\n1 sudo systemctl edit your-service 加入：\n1 2 3 4 [Service] Environment=\u0026#34;HTTP_PROXY=http://127.0.0.1:7890\u0026#34; Environment=\u0026#34;HTTPS_PROXY=http://127.0.0.1:7890\u0026#34; Environment=\u0026#34;NO_PROXY=localhost,127.0.0.1,::1\u0026#34; 然后重新加载并重启服务：\n1 2 sudo systemctl daemon-reload sudo systemctl restart your-service Docker 使用 HTTP 代理 Docker 拉取镜像由 Docker daemon 执行，因此仅设置当前用户的环境变量通常不够。Linux 上可以通过 /etc/docker/daemon.json 为 Docker daemon 配置代理。下面以局域网代理地址 192.168.0.100:7890 为例：\n1 2 3 4 5 6 7 8 9 10 11 12 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json \u0026gt; /dev/null \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; { \u0026#34;proxies\u0026#34;: { \u0026#34;http-proxy\u0026#34;: \u0026#34;http://192.168.0.100:7890\u0026#34;, \u0026#34;https-proxy\u0026#34;: \u0026#34;http://192.168.0.100:7890\u0026#34;, \u0026#34;no-proxy\u0026#34;: \u0026#34;localhost,127.0.0.1,192.168.0.0/16\u0026#34; } } EOF sudo systemctl restart docker 如果 /etc/docker/daemon.json 中已经存在 registry-mirrors 等配置，不要直接覆盖文件。请使用 sudo nano /etc/docker/daemon.json，将上面的 proxies 字段合并到已有 JSON 对象中，并确保 JSON 语法正确。\n检查 Docker daemon 是否读取到配置：\n1 docker info | grep -i proxy 确认代理配置生效后，拉取镜像：\n1 docker pull vincentqin/image-matching-webui:latest 如果连接失败，请确认局域网机器 192.168.0.100 上的代理软件已开启“允许来自局域网的连接”（Allow LAN），并确认该机器的防火墙已放行 7890 端口。可以从当前 Linux 主机测试代理：\n1 curl -x \u0026#34;http://192.168.0.100:7890\u0026#34; https://ipinfo.io/ip 仅为容器设置代理 如果只希望容器内的程序使用代理，可以在启动时传入环境变量，不修改 Docker daemon：\n1 2 3 4 5 docker run --rm \\ -e HTTP_PROXY=\u0026#34;http://127.0.0.1:7890\u0026#34; \\ -e HTTPS_PROXY=\u0026#34;http://127.0.0.1:7890\u0026#34; \\ -e NO_PROXY=\u0026#34;localhost,127.0.0.1,::1\u0026#34; \\ alpine sh -c \u0026#39;wget -qO- https://example.com | head\u0026#39; 使用 Docker Compose 时，可以在服务的 environment 中配置同样的变量：\n1 2 3 4 5 6 services: app: environment: HTTP_PROXY: http://127.0.0.1:7890 HTTPS_PROXY: http://127.0.0.1:7890 NO_PROXY: localhost,127.0.0.1,::1 注意：容器中的 127.0.0.1 指向容器自身。如果代理运行在宿主机上，Linux 容器通常需要使用宿主机可访问的地址，例如宿主机局域网 IP，并确保代理监听的地址允许容器访问。\npip 使用 HTTP 代理 单次安装 使用 --proxy 只影响当前命令：\n1 python -m pip install requests --proxy http://127.0.0.1:7890 也可以通过环境变量配置当前 shell：\n1 2 3 export HTTP_PROXY=\u0026#34;http://127.0.0.1:7890\u0026#34; export HTTPS_PROXY=\u0026#34;http://127.0.0.1:7890\u0026#34; python -m pip install requests 持久配置 使用 pip config 写入当前用户配置：\n1 python -m pip config set global.proxy http://127.0.0.1:7890 查看当前配置：\n1 python -m pip config list 取消该配置：\n1 python -m pip config unset global.proxy 也可以直接编辑用户级配置文件。Linux 上通常是 ~/.config/pip/pip.conf：\n1 2 [global] proxy = http://127.0.0.1:7890 代理需要用户名和密码时 代理地址格式为：\n1 http://用户名:密码@代理地址:端口 如果用户名或密码包含 @、:、/ 等特殊字符，应先进行 URL 编码。不要把包含密码的命令写入公开脚本、文章或 shell 历史记录；更安全的做法是使用代理软件提供的本地无认证端口，或通过环境变量和权限受控的配置文件传递凭据。\n排查方法 验证代理是否生效 执行以下命令查看出口 IP：\n1 curl https://ipinfo.io/ip 如果命令返回的 IP 是代理服务器的公网 IP，说明请求已经通过代理；如果仍然是当前 Linux 主机的公网 IP，请检查代理环境变量、代理服务的 Allow LAN 设置以及防火墙规则。\n用 curl -I https://pypi.org 验证基础代理连接。 用 env | grep -i proxy 检查当前 shell 的代理变量。 用 docker info | grep -i proxy 检查 Docker daemon 配置。 用 python -m pip config debug 查看 pip 实际读取的配置文件。 将不应经过代理的内网域名和地址加入 NO_PROXY，多个值用逗号分隔。 代理使用完毕后，可以在当前 shell 中取消临时变量：\n1 unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY no_proxy NO_PROXY ","date":"2026-08-21T00:00:00+08:00","permalink":"https://blog.mn00.net/p/linux_http_proxy/","title":"Linux 配置 HTTP 代理"},{"content":"在完全隔离的内网中，Windows 首次访问使用自签名证书的 HTTPS 服务时，可能因为自动根证书联网查询而等待十几秒。可通过以下两种方案解决：\n将指定证书永久导入本地计算机的受信任根证书库； 关闭整台工控机的 Windows 自动根证书更新。 优先推荐方案一。\n方案一：永久信任指定证书（推荐） 该方案只信任指定的内网证书，不改变系统对其他证书的处理策略，影响范围最小。\n如果程序可能由 Windows 服务、开机自启账户或不同用户运行，应将证书安装到：\n1 2 本地计算机 └─ 受信任的根证书颁发机构 不要只安装到“当前用户”。\n1. 检查证书文件 以管理员身份打开 PowerShell：\n1 2 3 $certPath = \u0026#34;C:\\certs\\intranet-server.crt\u0026#34; Test-Path -LiteralPath $certPath 返回 True 后，检查证书主题、有效期和指纹：\n1 certutil -dump $certPath 导入前应通过安全渠道核对证书的 SHA-256 或 SHA-1 指纹，确认该文件确实来自目标服务器。\n2. 导入本地计算机根证书库 1 2 3 Import-Certificate ` -FilePath $certPath ` -CertStoreLocation \u0026#34;Cert:\\LocalMachine\\Root\u0026#34; 也可以使用：\n1 certutil -addstore -f Root $certPath 3. 验证安装结果 将下面的值替换为证书的 SHA-1 指纹，不要包含空格：\n1 2 3 4 5 $thumbprint = \u0026#34;替换为证书的SHA1指纹\u0026#34; Get-ChildItem \u0026#34;Cert:\\LocalMachine\\Root\u0026#34; | Where-Object { $_.Thumbprint -eq $thumbprint } | Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint 安装完成后，应关闭原来的应用进程，再启动一个全新进程测试第一次 HTTPS 请求。\n4. 删除当前用户中的临时证书 如果此前曾将证书临时导入 CurrentUser\\Root，可删除该临时副本，只保留 LocalMachine\\Root 中的正式配置：\n1 2 3 Get-ChildItem \u0026#34;Cert:\\CurrentUser\\Root\u0026#34; | Where-Object { $_.Thumbprint -eq $thumbprint } | Remove-Item 5. 回滚永久信任 需要撤销时，只按精确指纹删除：\n1 2 3 Get-ChildItem \u0026#34;Cert:\\LocalMachine\\Root\u0026#34; | Where-Object { $_.Thumbprint -eq $thumbprint } | Format-List Subject, Issuer, Thumbprint 确认无误后执行：\n1 2 3 Get-ChildItem \u0026#34;Cert:\\LocalMachine\\Root\u0026#34; | Where-Object { $_.Thumbprint -eq $thumbprint } | Remove-Item 不要使用模糊主题匹配，也不要批量清理根证书库。\n6. 过期证书的处理 不要把已经过期的旧证书作为长期方案。应重新生成或签发一张新证书，并满足：\n证书处于有效期内； EKU 包含 Server Authentication； SAN 包含实际使用的 DNS 名称； 如果通过 IP 访问，SAN 还应包含对应的 IP； 服务器绑定的新证书与工控机导入的证书一致。 对于多台域内工控机，可以通过组策略统一分发：\n1 2 3 4 5 计算机配置 → Windows 设置 → 安全设置 → 公钥策略 → 受信任的根证书颁发机构 方案二：关闭 Windows 自动根证书更新 该方案影响整台计算机，仅适用于完全隔离、专机专用，并且所有可信根证书都由管理员离线维护的环境。\n普通联网电脑不建议使用。\n一键脚本 下载：Set-RootCertificateAutoUpdate.ps1\n查看当前状态：\n1 2 3 powershell.exe -NoProfile -ExecutionPolicy Bypass ` -File \u0026#34;.\\Set-RootCertificateAutoUpdate.ps1\u0026#34; ` -Action Status 全局关闭自动根证书更新：\n1 2 3 powershell.exe -NoProfile -ExecutionPolicy Bypass ` -File \u0026#34;.\\Set-RootCertificateAutoUpdate.ps1\u0026#34; ` -Action Disable 恢复执行脚本前的原始策略：\n1 2 3 powershell.exe -NoProfile -ExecutionPolicy Bypass ` -File \u0026#34;.\\Set-RootCertificateAutoUpdate.ps1\u0026#34; ` -Action Restore 脚本第一次执行 Disable 时，会将原注册表状态保存到：\n1 C:\\ProgramData\\RootCertificateAutoUpdatePolicy\\original-state.json 重复执行 Disable 不会覆盖第一次备份；执行 Restore 时会恢复原值，并在成功后删除备份文件。如果找不到备份，脚本会拒绝恢复，避免覆盖机器原有策略。\n方法一：通过组策略配置 运行：\n1 gpedit.msc 进入：\n1 2 3 4 5 6 计算机配置 → 管理模板 → 系统 → Internet 通信管理 → Internet 通信设置 → 关闭自动根证书更新 将“关闭自动根证书更新”设置为“已启用”。\n注意：启用该策略，代表关闭自动根证书更新。\n应用策略：\n1 gpupdate /force 建议重启工控机后，再用全新应用进程验证。\n方法二：通过注册表策略配置 以管理员身份执行：\n1 2 3 4 5 6 7 8 9 10 11 12 13 $policyPath = \u0026#34;HKLM:\\SOFTWARE\\Policies\\Microsoft\\SystemCertificates\\AuthRoot\u0026#34; New-Item -Path $policyPath -Force | Out-Null New-ItemProperty ` -Path $policyPath ` -Name \u0026#34;DisableRootAutoUpdate\u0026#34; ` -PropertyType DWord ` -Value 1 ` -Force gpupdate /force 验证策略：\n1 2 Get-ItemProperty -Path $policyPath | Select-Object DisableRootAutoUpdate 结果含义：\n1 2 3 1 关闭自动根证书更新 0 开启自动根证书更新 值不存在 Windows 默认开启自动根证书更新 恢复默认行为 1 2 3 4 5 6 Remove-ItemProperty ` -Path $policyPath ` -Name \u0026#34;DisableRootAutoUpdate\u0026#34; ` -ErrorAction SilentlyContinue gpupdate /force 恢复后建议重启工控机。\n方案选择 方案 影响范围 适用场景 建议 导入指定证书到 LocalMachine\\Root 仅指定证书 固定内网服务、少量工控机 推荐 内部 CA + 组策略分发根证书 企业内部全部受控服务 多服务器、多工控机 最佳长期方案 关闭自动根证书更新 整台计算机 完全离线、证书统一离线维护 谨慎使用 验证 修复后必须启动一个全新的应用进程，不能只在原进程中重复请求。\n如果证书已续签、SAN 正确并完成信任配置，可以使用不带 -k 的 curl 验证：\n1 2 3 curl.exe -sS -o NUL ` -w \u0026#34;HTTP=%{http_code} TCP=%{time_connect}s TLS=%{time_appconnect}s TTFB=%{time_starttransfer}s TOTAL=%{time_total}s`n\u0026#34; ` $url 预期结果：\n1 2 3 4 5 HTTP=200 不再出现 SEC_E_UNTRUSTED_ROOT 不再出现证书过期 不再出现名称不匹配 第一次请求不再等待十几秒 参考资料 Microsoft Learn：Import-Certificate Microsoft Learn：配置可信根和不允许的证书 Microsoft Learn：关闭自动根证书更新策略 ","date":"2026-06-17T16:40:00+08:00","permalink":"https://blog.mn00.net/p/windows-intranet-trust-self-signed-certificate/","title":"Windows 内网 HTTPS 自签名证书：永久信任与关闭根证书查询"},{"content":"在 Linux 系统中，程序运行时可能依赖于多个共享库（Shared Libraries）。有时我们需要将这些依赖库拷贝到特定目录，以确保程序能够正确运行。以下是一个简单的教程，介绍如何查找并拷贝这些依赖库。\n假设我们有一个可执行文件 MyProject，我们希望将它依赖的所有共享库拷贝到 dist/lib/ 目录下。可以使用以下命令：\n1 2 3 4 ldd MyProject \\ | awk \u0026#39;{print $3}\u0026#39; \\ | grep \u0026#39;^/\u0026#39; \\ | xargs -I{} cp -v {} dist/lib/ 注意事项 不要对来源不可信的可执行文件直接运行 ldd；部分实现可能触发目标程序的动态加载逻辑。 上述命令只复制 ldd 输出第三列为绝对路径的库，动态加载器等条目可能需要单独处理。 如果系统安装了 pax-utils，可以使用 lddtree 更清晰地查看递归依赖： 1 lddtree MyProject 打包完成后，建议通过 LD_LIBRARY_PATH 指向私有库目录进行验证：\n1 LD_LIBRARY_PATH=\u0026#34;$PWD/dist/lib\u0026#34; ./MyProject ","date":"2026-01-27T13:43:02+08:00","permalink":"https://blog.mn00.net/p/linux_shared_lib1/","title":"Linux 可执行程序依赖库收集"},{"content":"本文是 Git 命令行常用操作速查，适合在终端直接进行版本控制操作。\n1. 基础配置 1 2 3 4 5 6 # 设置用户名和邮箱 git config --global user.name \u0026#34;Your Name\u0026#34; git config --global user.email \u0026#34;your.email@example.com\u0026#34; # 查看当前配置 git config --list 💡 建议在每台新机器上先做一次全局配置，确保提交信息正确。\n2. 创建与克隆仓库 1 2 3 4 5 6 # 创建新仓库 git init my-repo cd my-repo # 克隆远程仓库 git clone https://github.com/user/repo.git ⚡ 注意：git clone 会自动创建本地仓库并关联远程 origin。\n3. 基本操作 1 2 3 4 5 6 7 8 9 10 11 # 查看工作区状态 git status # 添加文件到暂存区 git add file.txt # 提交更改 git commit -m \u0026#34;Commit message\u0026#34; # 查看提交历史 git log 🔹 tip：git log --oneline --graph --all 可以以图形化形式查看分支和提交历史。\n4. 分支管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # 创建新分支（不切换） git branch new-branch # 切换分支 git checkout new-branch # 查看本地分支 git branch # 创建并切换到新分支（常用） git checkout -b new-branch # 合并分支到 main git checkout main git merge new-branch # 查看所有分支及提交图谱 git log --oneline --graph --all # 查看分支及其远程跟踪信息 git branch -vv ⚡ 建议：功能分支（feature）单独开发，完成后再 merge 到主分支，可保持主分支历史清晰。\n5. 远程操作 1 2 3 4 5 6 7 8 9 10 11 # 添加远程仓库 git remote add origin https://github.com/user/repo.git # 推送本地分支到远程（第一次推送） git push -u origin main # 拉取远程更新并合并到当前分支 git pull origin main # 如果希望保持历史线性，可使用 rebase git pull --rebase origin main 💡 tip：使用 -u（或 --set-upstream）绑定本地分支与远程分支，以后可直接 git push / git pull。\n6. 常用技巧 1 2 3 4 5 6 7 8 9 10 11 12 # 查看本地与暂存区或工作区的差异 git diff # 撤销修改（回退到上一次 commit 的状态） git checkout -- file.txt # 删除文件并提交删除 git rm file.txt git commit -m \u0026#34;Remove file.txt\u0026#34; # 查看提交历史图谱 git log --oneline --graph --all ⚡ tip：操作前多用 git status 检查当前状态，避免误操作。\n7. 冲突解决 合并分支时遇到冲突 文件中会出现标记：\n1 2 3 4 5 \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD 远程代码 ======= 本地代码 \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; commit 解决步骤：\n1 2 3 4 5 6 7 8 9 10 11 # 1. 手动编辑冲突文件，解决冲突后添加 git add file.txt # 2. 如果是 merge，直接 commit git commit # 3. 如果是 rebase，解决冲突后继续 git rebase --continue # 查看当前冲突状态 git status Push 被拒绝时的处理 1 2 3 4 5 6 # 如果 push 被拒绝（远程有新提交） git pull --rebase origin main # 解决冲突后继续 rebase git add file.txt git rebase --continue ⚠️ 重要提示：\n冲突出现时，Git 会标记 \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;, =======, \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;，必须手动处理 rebase 可以保持历史线性，但不要在公共分支上强制 rebase 先 commit 或 stash 再 pull，避免丢失修改 8. 总结 功能分支开发 → 保持主分支清晰 先 commit / stash 再 pull → 避免丢失修改 冲突手动解决 + git add + git rebase --continue push 时使用 -u 设置 upstream → 后续操作更方便 ","date":"2026-01-27T08:38:08+08:00","permalink":"https://blog.mn00.net/p/git-cli-guide/","title":"Git常用命令行教程"},{"content":"本文是 CMake 命令行常用操作速查，适合在终端直接配置、构建、测试与安装。\n1. 基础配置与构建（单配置生成器） 适用于 Ninja、Unix Makefiles 等“单配置”生成器。\nDebug 构建 1 2 3 4 cmake -S . -B build/debug \\ -DCMAKE_BUILD_TYPE=Debug cmake --build build/debug Release 构建 1 2 3 4 cmake -S . -B build/release \\ -DCMAKE_BUILD_TYPE=Release cmake --build build/release 建议 Debug / Release 使用不同的 build 目录，避免缓存和配置相互污染。\n2. 选择生成器与并行构建 1 2 3 4 5 # 指定生成器 cmake -S . -B build -G Ninja # 并行构建（具体参数取决于构建工具） cmake --build build -j 8 3. 多配置生成器（Visual Studio / Xcode） 多配置生成器不使用 CMAKE_BUILD_TYPE，需在构建时指定配置：\n1 2 3 cmake -S . -B build -G \u0026#34;Visual Studio 17 2022\u0026#34; cmake --build build --config Release 4. 预设（CMakePresets.json） 当项目提供预设时，推荐使用：\n1 2 cmake --preset debug cmake --build --preset debug 5. 常用变量与路径配置 1 2 3 4 5 6 7 8 9 # 指定安装前缀 cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local # 查找第三方库路径（通用） cmake -S . -B build -DCMAKE_PREFIX_PATH=\u0026#34;/path/to/lib;/path/to/other\u0026#34; # 指定工具链（交叉编译或 vcpkg） cmake -S . -B build \\ -DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake 6. 构建、测试与安装 1 2 3 4 5 6 7 8 # 构建 cmake --build build # 运行测试（需要启用 CTest） ctest --test-dir build # 安装 cmake --install build 7. 生成编译数据库（供 clangd/IDE 使用） 1 cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 8. 查看与调整缓存 1 2 3 4 5 # 交互式修改缓存（终端 UI） ccmake -S . -B build # GUI 版本 cmake-gui 9. 常见排错与清理 1 2 3 4 5 # 显示详细构建命令 cmake --build build --verbose # 仅清理生成目录（安全做法：删除 build 目录） rm -rf build 10. 快速模板（含 vcpkg） 1 2 3 4 5 cmake -S . -B build/debug \\ -DCMAKE_BUILD_TYPE=Debug \\ -DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build build/debug -j 8 如果你需要针对特定项目（比如多平台、交叉编译、CUDA）扩展命令行模板，告诉我目标平台和依赖即可。\n","date":"2026-01-23T09:38:01+08:00","permalink":"https://blog.mn00.net/p/cmake-cli-guide/","title":"CMake 常用命令行教程"},{"content":"概述 PyTorch 和 torchvision 之间存在版本依赖关系。选择兼容的版本组合对于项目的稳定性至关重要。\n版本对应表 PyTorch torchvision Python 2.9.x 0.24.x 3.10-3.14 2.8.x 0.23.x 3.9-3.13 2.7.x 0.22.x 3.9-3.13 2.6.x 0.21.x 3.8-3.12 2.5.x 0.20.x 3.8-3.12 2.4.x 0.19.x 3.8-3.12 2.3.x 0.18.x 3.8-3.12 2.2.x 0.17.x 3.8-3.12 2.1.x 0.16.x 3.8-3.11 2.0.x 0.15.x 3.8-3.11 1.13.x 0.14.x 3.7-3.11 1.12.x 0.13.x 3.7-3.10 1.11.x 0.12.x 3.7-3.10 1.10.x 0.11.x 3.6-3.10 1.9.x 0.10.x 3.6-3.10 安装建议 使用 pip 安装 cpu版本安装 1 pip install torch==2.5.0 torchvision==0.20.0 gpu版本安装 可根据需求更改版本 1 pip3 install torch==2.5.0+cu118 torchvision==0.20.0+cu118 --index-url https://download.pytorch.org/whl/cu118 验证安装 1 python -c \u0026#34;import torch; import torchvision; print(f\u0026#39;PyTorch: {torch.__version__}\u0026#39;); print(f\u0026#39;torchvision: {torchvision.__version__}\u0026#39;)\u0026#34; 测试gpu是否可用 1 2 import torch torch.cuda.is_available() 官方资源 PyTorch 官方文档 torchvision 版本历史 ","date":"2026-01-14T10:02:11+08:00","permalink":"https://blog.mn00.net/p/torch_v1/","title":"PyTorch,torchvision版本对应关系"},{"content":"各版本 YOLO 模型输出结构详细解析（YOLOv5 / YOLOv8 / YOLOv9 / YOLO11 / YOLO12 / YOLOv10） 在不同版本的 YOLO 模型中，网络结构不断演进，因此输出张量的维度和含义也有所变化。下面按版本总结其典型 ONNX 输出格式，便于开发者在部署时解析模型结果。\nYOLOv5 输出格式 输出维度：float[1, 25200, 85]\n1：batch size\n25200：总预测框数量\n来自三尺度输出：\n80×80 网格 → 6400 个 40×40 网格 → 1600 个 20×20 网格 → 400 个 合计 8400 个网格，每个网格 3 个 anchor： → 8400 × 3 = 25200 个预测框 85：每个框的属性\n4 个边界框参数：x, y, w, h 1 个 objectness 置信度 80 个类别置信度（COCO） ✔ YOLOv5 采用“anchor-based”预测，因此输出较长。\nYOLOV5U/YOLOv8/YOLOv9/YOLO11/YOLO12 输出格式 Ultralytics 系列从 YOLOv8 开始，在模型导出时可以选择是否 内置 NMS（Non-Maximum Suppression）： 不带 NMS（默认）：输出原始预测，需要你自己写 NMS。 带 NMS（export nms=True）：输出已经 NMS 处理过的最终检测框，适合部署。 下面分别说明两种输出格式。\n② 不带 NMS 的输出格式（默认） 这是最常用、最通用的版本，输出为 anchor-free 三尺度预测特征。 输出维度：float[1, 84, 8400](不带nms)\n1：batch size\n84：每个预测包含\n4 个边界框参数：x, y, w, h 80 个类别置信度（无 objectness） 8400：总候选框数量\n来自 80×80 + 40×40 + 20×20 三尺度 每个网格只生成 1 个预测（anchor-free，因此不 ×3） ✔ YOLOV5U/YOLOv8/9/11/12 采用完全无 anchor 的 Decoupled Head，输出更紧凑。\n② 带 NMS 的输出格式（export nms=True） 输出维度：float[1, 300, 6] 模型直接输出 最终 300 个候选框（经过排序筛选后的 top 300）：\n每行 6 个数值： 索引 含义 0 x1（左上角横坐标） 1 y1（左上角纵坐标） 2 x2（右下角横坐标） 3 y2（右下角纵坐标） 4 score（置信度） 5 label（类别 ID） YOLOv10 输出格式 YOLOv10 完全使用 Dynamic Head + NMS-free 设计，因此输出格式与传统 YOLO 不同。 不可导出不带nms版本的模型 输出维度：float[1, 300, 6]\n模型直接输出 最终 300 个候选框（经过排序筛选后的 top 300）：\n每行 6 个数值： 索引 含义 0 x1（左上角横坐标） 1 y1（左上角纵坐标） 2 x2（右下角横坐标） 3 y2（右下角纵坐标） 4 score（置信度） 5 label（类别 ID） ✔ YOLOv10 的输出已经是筛选好的框，是 NMS-free 推理方式\n总结对比表 版本 输出张量大小 Anchor? 是否需要 NMS 说明 YOLOv5 [1, 25200, 85] ✔ 有 ✔ 需要 anchor-based，预测框最多 YOLOv5U/8/9/11/12 [1, 84, 8400] ✘ 无 ✔ 需要（可导出带有nms的模型） anchor-free，更紧凑 YOLOv10 [1, 300, 6] ✘ 无 ✘ 不需要 直接输出最终框，更适合部署 ","date":"2025-12-08T09:00:23+08:00","permalink":"https://blog.mn00.net/p/yolo_s1/","title":"各版本 YOLO 目标检测模型输出结构详细解析（YOLOv5/YOLOv8/YOLOv10/YOLO11/YOLO12/）"},{"content":"在本项目中，你可以一键生成 自签名 SSL 证书，支持：\n自动提示输入域名 自动生成 SAN（含主域名 + www 域名） 自动创建私钥、CSR、CRT 自动生成 OpenSSL 配置文件 随着现代浏览器对 HTTPS 的支持逐渐严格，即使是自签证书，也建议为域名配置 SAN。本脚本可以一键完成所有操作。 🧩 功能特点 ✔ 一键运行，无需安装\n✔ 自动提示输入域名\n✔ 自动生成完整证书链文件：\n私钥 .key CSR .csr 自签名证书 .crt OpenSSL 配置文件 openssl.cnf ✔ SAN 自动包含：\nyourdomain.com www.yourdomain.com ✔ 证书有效期默认 10 年（3650 天）\n✔ 证书文件自动归档到 certs/域名/ 目录\n⚡ 一键运行脚本（推荐方式） 你无需下载脚本，只需在服务器执行一条命令：\n1 bash \u0026lt;(curl -sSL https://raw.githubusercontent.com/topqaz/gen_cert/main/gen_cert.sh) 或使用 wget：\n1 bash \u0026lt;(wget -qO- https://raw.githubusercontent.com/topqaz/gen_cert/main/gen_cert.sh) 执行命令后会看到：\n1 请输入要生成证书的域名（例如 example.com）: 输入你的域名即可自动生成证书。\n📂 生成文件目录结构 生成的证书文件将存放在：\n1 2 3 4 5 6 certs/ └── example.com/ ├── example.com.key # 私钥 ├── example.com.csr # 证书请求文件 ├── example.com.crt # 自签名证书 ├── openssl.cnf # 自动生成的 OpenSSL 配置（含 SAN） 1 realpath certs/example.com/example.com.crt 🔒 自签名证书说明 自签名证书可用于：\n内网 HTTPS 本地开发调试 内部系统环境（无需公网 CA） 测试环境的 HTTPS ⚠️ 注意：浏览器会提示“不受信任”，需要手动信任证书。\n如需正式证书，可以使用：\nLet’s Encrypt（免费） Cloudflare SSL（半托管） 📜 脚本地址 源代码地址： https://github.com/topqaz/gen_cert\n一键运行入口： https://raw.githubusercontent.com/topqaz/gen_cert/main/gen_cert.sh\n","date":"2025-11-27T15:31:31+08:00","permalink":"https://blog.mn00.net/p/gen_cert/","title":"自签名 SSL 证书一键生成脚本（支持 SAN）"},{"content":"LiteMark：方便易用的书签导航系统 引言 LiteMark：把杂乱的书签，整理成真正可用的知识入口\n你大概也有过这种时刻：收藏了很多网页，真要找的时候却翻半天都找不到。浏览器自带书签能存，但不够好看，不够好找，也不够适合长期管理。\n所以我做了 LiteMark。\nLiteMark 是一款轻量易用的个人书签管理系统，目标很简单：让书签不再只是\u0026quot;存起来\u0026quot;，而是能被整理、搜索、分类、备份，甚至交给 AI 协助管理。\n它解决了什么问题 很多人的书签系统最后都会变成三种状态之一：\n越存越多，最后找不到 分类越来越乱，自己也记不清放哪了 想迁移、备份、导出时，发现工具根本不顺手 LiteMark 就是为了解决这些问题而设计的。它不是一个单纯的收藏夹，而是一个可以长期维护的书签系统。\nLiteMark 的核心特点 书签管理更顺手\n支持书签的新增、编辑、删除、隐藏和排序，分类顺序与分类内顺序都可以拖拽调整。对经常整理收藏的人来说，这一点非常省心。\n分类清晰，检索更快\n书签可以按分类组织，配合搜索和筛选，基本能把\u0026quot;找书签\u0026quot;这件事从翻箱倒柜变成快速定位。\nAI 辅助整理\nLiteMark 内置了 AI 能力，可以帮你做智能分类推荐、摘要生成、标签提取，甚至支持快速添加书签。对于那种\u0026quot;先收藏，后整理\u0026quot;的使用习惯，这个功能特别实用。\n导入导出和备份都很完整\n支持 JSON、CSV、HTML 多种格式导入导出，也支持 WebDAV 定时备份。也就是说，你既可以把现有书签迁过来，也不用担心数据只锁死在系统里。\n还能接入 AI 客户端\nLiteMark 内置 MCP Server，支持通过 AI 客户端直接整理、添加、修改、隐藏、删除书签，并管理分类顺序。对想把\u0026quot;书签管理\u0026quot;接进自己 AI 工作流的人来说，这一点很加分。\n部署也很轻\n项目基于 Vue 3 + FastAPI 构建，支持 Docker 一键部署，个人自建或者放到自己的服务器上都比较方便。\n适合谁 LiteMark 比较适合这些人：\n经常收藏网页、资料、教程的人 想把浏览器书签从\u0026quot;临时堆放区\u0026quot;变成\u0026quot;个人知识库\u0026quot;的人 需要分类、排序、备份的重度收藏用户 对 AI 辅助整理感兴趣的人 想要一套自己可控、可部署的书签系统的人 我为什么做它 我一直觉得，工具最好的状态不是\u0026quot;功能很多\u0026quot;，而是\u0026quot;功能刚好够用，而且每一个都能真的落地\u0026quot;。\nLiteMark 的出发点就是这个：让书签管理回到简单、清晰、可维护的状态。你不用为了保存一个链接而忍受混乱，也不用为了找回一个资料而在无穷列表里来回翻。\n它应该像一个安静、可靠的入口，把你真正想保留的信息放在最顺手的位置。\n结尾 如果你也在找一套更像样的书签管理方式，LiteMark 也许会合你的胃口。\n它足够轻量，但不是简陋；足够实用，也留给了 AI 和自动化足够的空间。对于希望把浏览器收藏真正整理成资产的人来说，它是一个很自然的选择。\n你可以把它当作书签管理器，也可以把它当作自己的个人导航站。\nGitHub链接\n快速开始 Docker 部署（推荐） 1 2 3 4 5 6 7 8 9 10 11 12 # 使用 docker-compose curl -O https://raw.githubusercontent.com/topqaz/LiteMark/main/docker-compose.yml docker-compose up -d # 或直接使用 docker run docker run -d -p 8080:80 \\ -v litemark-data:/app/data \\ -e JWT_SECRET=your-secret-key \\ -e DEFAULT_ADMIN_USERNAME=admin \\ -e DEFAULT_ADMIN_PASSWORD=admin123 \\ --name litemark \\ topqaz/litemark:amd64 或 ARM64 架构使用： topqaz/litemark:arm64 访问地址：http://localhost:8080，后台入口：http://localhost:8080/admin\n更新 1 2 3 4 # 拉取最新镜像 docker-compose pull # 启动新容器 docker-compose up -d docker-compose.yml 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 services: litemark: image: topqaz/litemark:latest container_name: litemark restart: unless-stopped ports: - \u0026#34;8080:80\u0026#34; volumes: - litemark-data:/app/data environment: - JWT_SECRET=change-this-to-a-secure-random-string - DATABASE_URL=sqlite+aiosqlite:///./data/litemark.db - DEFAULT_ADMIN_USERNAME=admin - DEFAULT_ADMIN_PASSWORD=admin123 - DEBUG=false - CORS_ORIGINS=* volumes: litemark-data: LiteMark，方便易用的书签导航系统\nLiteMark 是一款基于 Vue 3 + FastApi 的个人书签管理应用,支持ai接入 浏览器插件，融入ai操作，mcp支持。\n功能亮点 📚 书签管理：支持添加、编辑、删除、隐藏与排序；分类顺序与分类内顺序均可拖拽调整。 🔐 后台面板：位于 /admin，含登录校验、缓存刷新、站点设置等管理动作。 🚀 极佳体验：友好的 API、响应式布局与移动端操作优化。 💾 WebDAV 定时备份：支持配置 WebDAV 服务器，实现定时自动循环备份（每天/每周/每月）。 项目演示 主页展示 MCP演示 登录页面 管理后台 后续更新计划 \u0026hellip;\u0026hellip;. ","date":"2025-11-21T16:43:07+08:00","permalink":"https://blog.mn00.net/p/litemark/","title":"LiteMark-方便易用的书签导航系统"},{"content":"TrafficMonitor Nezha 插件 这是一个 TrafficMonitor 的扩展插件，\n用于通过 哪吒监控 (Nezha Monitoring) API 获取服务器信息，并在 TrafficMonitor 界面显示。 ✨ 功能 支持配置 服务器地址 / 用户名 / 密码 自动从哪吒监控 API 获取服务器数据 在 TrafficMonitor 界面显示服务器状态 配置保存在插件目录下，可持久化 ","date":"2025-11-21T11:01:02+08:00","permalink":"https://blog.mn00.net/p/trafficmonitor_nezha/","title":"任务栏显示服务器状态TrafficMonitor Nezha 插件"}]