主程序无法自行更新自身,因Windows和Linux均锁定运行中的可执行文件,导致覆盖或删除失败;必须由独立守护进程(如updaterd.exe)完成下载、校验、原子替换与重启,主程序仅触发后优雅退出。

为什么不能让主程序自己更新自身
Windows 和 Linux 下,正在运行的可执行文件无法被直接覆盖或删除。updater.exe 如果是主程序(如 app.exe)自身调起的子进程,它大概率会因权限、文件占用或路径锁定而失败。真正可行的方案是:主程序启动一个**独立、轻量、无 GUI 的守护进程**(比如 updaterd.exe),由它负责下载、校验、替换、重启——主程序只管“喊人干活”,然后优雅退出。
如何用 C++ 启动并可靠托管 updaterd 进程
关键不是“启动”,而是“确保它活下来”。主程序退出后,updaterd 必须脱离父进程生命周期,且避免被系统误杀(如 Windows 的 Session 0 隔离、Linux 的 systemd 用户 session 超时)。
- Windows:调用
CreateProcess时设DETACHED_PROCESS+CREATE_NO_WINDOW,再用SetThreadExecutionState防止休眠;更稳妥的做法是注册为 Windows Service(用sc create),但需管理员权限 - Linux:用
fork()+setsid()+chdir("/")+umask(0)完全 daemonize;若使用 systemd,则写updaterd.service文件,设Restart=on-failure和RestartSec=5 - 跨平台注意:
updaterd启动后,主程序应立即调用exit(0)(而非return),避免析构器干扰或资源残留
下载+替换阶段必须做的三件事
自动更新最常崩在“换文件”这一步——不是下载失败,而是替换了旧文件却没清空缓存、没重载 DLL、没通知系统服务更新。
- 下载前先校验远程 manifest:比对
version、sha256、min_app_version(防止低版本 updaterd 错装高兼容要求包) - 下载到临时目录(如
./update_temp/),解压后用原子操作替换:Windows 用MoveFileEx带MOVEFILE_REPLACE_EXISTING;Linux 用rename(2)(保证目录项切换是原子的) - 替换后不直接
execv新主程序,而是发信号或写标记文件(如.needs_restart),由新启动的主程序自己检查并清理旧进程(例如通过GetProcessIdOfThread或/proc/[pid]/cmdline判断是否为旧实例)
updaterd 怎么知道该不该干活、干到哪一步了
它不能靠“定时轮询”这种低效方式,也不能依赖主程序每次唤醒它——得有状态持久化 + 明确触发机制。
- 用轻量级状态文件(如 JSON)记录:
last_check_time、current_version、update_status("idle"/"downloading"/"ready_to_apply"/"applied")、error_message - 触发时机分两类:主程序退出前写
trigger_update = true并 kill 自己;或updaterd自身按 schedule 检查(如每 4 小时 HTTP HEAD 请求/latest.json) - 避免竞态:所有状态读写必须加文件锁(Windows 用
LockFileEx,Linux 用flock),否则主程序和updaterd同时写状态会导致错乱
真正的难点不在代码行数,而在状态机设计和边界条件处理——比如断电后重启,updaterd 怎么判断上次是卡在下载中还是已写完新二进制但没删旧进程?这类逻辑必须在状态文件里留足够上下文,不能靠内存变量猜。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











