diff --git a/vps_init.sh b/vps_init.sh index a991f61..4085dc5 100644 --- a/vps_init.sh +++ b/vps_init.sh @@ -589,43 +589,84 @@ if [ "${SWAP_DIFF#-}" -le 5 ]; then # 显示交换空间信息 free -h | tee -a $LOG_FILE else - # 如果不存在交换空间或大小不同,则进行处理 + # 交换空间大小不符或不存在,需要重建 + # 关键修复:旧版直接对正在使用的交换执行 swapoff,会在内存紧张时把换出页 + # 强行搬回内存,触发 OOM Killer 杀死 swapoff(exit 137),进而导致后续 + # rm/dd/mkswap/swapon 连锁失败。 + # 解决方案:先创建并启用一个新的交换文件作为“缓冲”,再关闭旧交换, + # 全程保证始终有足够的交换空间容纳换出页,避免 OOM。 if [ "$CURRENT_SWAP_TOTAL" -gt "0" ]; then - log "${YELLOW}系统已有交换空间但大小不符(${CURRENT_SWAP_TOTAL}MB),准备清理现有交换空间...${NC}" - - # 获取所有交换设备 - SWAP_DEVICES=$(swapon --show=NAME --noheadings) - - # 清理所有活跃的交换空间 - for DEVICE in $SWAP_DEVICES; do - log "${YELLOW}关闭交换空间: $DEVICE${NC}" - swapoff "$DEVICE" - done - - # 从fstab中移除所有交换空间条目(保留备份) - cp /etc/fstab /etc/fstab.bak - log "${GREEN}备份了/etc/fstab文件${NC}" - sed -i '/swap/d' /etc/fstab - - # 删除交换文件 - if [ -f /swapfile ]; then - log "${YELLOW}删除现有交换文件...${NC}" - rm -f /swapfile - fi - - log "${GREEN}所有现有交换空间已清理${NC}" + log "${YELLOW}系统已有交换空间但大小不符(${CURRENT_SWAP_TOTAL}MB),准备安全重建...${NC}" else log "${YELLOW}系统未配置交换空间,准备创建...${NC}" fi - # 创建新的交换文件 - log "${GREEN}创建${SWAP_SIZE}MB大小的交换文件...${NC}" - dd if=/dev/zero of=/swapfile bs=1M count=$SWAP_SIZE status=progress - chmod 600 /swapfile - mkswap /swapfile - swapon /swapfile + # 备份fstab + cp /etc/fstab /etc/fstab.bak + log "${GREEN}备份了/etc/fstab文件${NC}" - # 设置开机自动挂载 + # 尽量释放可回收内存,降低需要搬回的换出页数量 + sync + echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true + + # 工具函数:创建指定大小(MB)的交换文件并启用 + make_swapfile() { + local path="$1" size="$2" + rm -f "$path" 2>/dev/null || true + # 优先使用 fallocate(更快),失败则退回 dd + if ! fallocate -l "${size}M" "$path" 2>/dev/null; then + dd if=/dev/zero of="$path" bs=1M count="$size" status=progress + fi + chmod 600 "$path" + mkswap "$path" >/dev/null + swapon "$path" + } + + if swapon --show=NAME --noheadings 2>/dev/null | grep -qx "/swapfile"; then + # /swapfile 正在使用:先用临时缓冲文件接管换出页,再安全重建 /swapfile + log "${YELLOW}检测到 /swapfile 正在使用,建立临时缓冲交换以安全重建...${NC}" + make_swapfile /swapfile.tmp "$SWAP_SIZE" + + # 关闭旧的 /swapfile(换出页会迁移到 /swapfile.tmp,从而避免 OOM) + log "${YELLOW}关闭旧的 /swapfile...${NC}" + if swapoff /swapfile; then + rm -f /swapfile + else + log "${RED}关闭旧 /swapfile 失败,已保留临时缓冲交换,请手动检查${NC}" + fi + + # 关闭其它可能存在的旧交换设备/文件 + for DEVICE in $(swapon --show=NAME --noheadings 2>/dev/null); do + [ "$DEVICE" = "/swapfile.tmp" ] && continue + log "${YELLOW}关闭其它交换空间: $DEVICE${NC}" + if swapoff "$DEVICE" 2>/dev/null; then + [ -f "$DEVICE" ] && rm -f "$DEVICE" + fi + done + + # 创建最终的 /swapfile(临时缓冲里的换出页可迁移到这里) + log "${GREEN}创建${SWAP_SIZE}MB大小的交换文件 /swapfile...${NC}" + make_swapfile /swapfile "$SWAP_SIZE" + + # 关闭并删除临时缓冲(其页面会迁移到新的 /swapfile,仍不会 OOM) + log "${YELLOW}清理临时缓冲交换...${NC}" + swapoff /swapfile.tmp 2>/dev/null || true + rm -f /swapfile.tmp + else + # /swapfile 未使用:先创建新 /swapfile,再关闭其它旧交换 + log "${GREEN}创建${SWAP_SIZE}MB大小的交换文件 /swapfile...${NC}" + make_swapfile /swapfile "$SWAP_SIZE" + for DEVICE in $(swapon --show=NAME --noheadings 2>/dev/null); do + [ "$DEVICE" = "/swapfile" ] && continue + log "${YELLOW}关闭其它交换空间: $DEVICE${NC}" + if swapoff "$DEVICE" 2>/dev/null; then + [ -f "$DEVICE" ] && rm -f "$DEVICE" + fi + done + fi + + # 从fstab移除旧的 swap 条目(匹配 swap 文件系统类型或 /swapfile 路径),再写入新条目 + sed -i '/[[:space:]]swap[[:space:]]/d; /swapfile/d' /etc/fstab if ! grep -q "/swapfile" /etc/fstab; then echo "/swapfile swap swap defaults 0 0" >> /etc/fstab log "${GREEN}已添加到fstab,开机将自动挂载${NC}"