rustc-wrapper(linker)解决cargo并行链接OOM问题

·

包转linker的方式

ld_wrapper.sh 并没有验证通过ORZ?

#!/bin/bash
# 通用链接器包装器 - ld_generic_wrapper.sh

PRESSURE_THRESHOLD="0.10"          # 内存压力阈值(10%)
MAX_WAIT_TIME=600                  # 最大等待时间(秒)
MIN_WAIT_TIME=5                    # 最小等待时间(秒) 
MAX_RETRIES=100                    # 最大重试次数
PRESSURE_FILE="/sys/fs/cgroup/user.slice/user-$(id -u).slice/memory.pressure"
REAL_LD="/usr/bin/ld"              # 真实链接器路径,可根据需要修改
LOG_FILE="/tmp/linker_wrapper.log" # 日志文件路径

# 检测真实链接器
detect_real_linker() {
    local wrapper_name=$(basename "$0")
    case "$wrapper_name" in
        *ld*)
            REAL_LD="/usr/bin/ld"
            ;;
        *lld*)
            REAL_LD="/usr/bin/lld"
            ;;
        *gold*)
            REAL_LD="/usr/bin/gold"
            ;;
        *)
            REAL_LD="/usr/bin/ld"
            ;;
    esac
    
    # 检查链接器是否存在
    if [[ ! -x "$REAL_LD" ]]; then
        # 尝试在PATH中查找
        REAL_LD=$(which "${wrapper_name#wrapper_}") || REAL_LD="/usr/bin/ld"
    fi
}

# 日志函数
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

# 其余函数与之前相同...
get_some_avg10() {
    local pressure_file=$1
    if [[ -f "$pressure_file" ]]; then
        local line=$(grep "^some" "$pressure_file")
        if [[ $line =~ avg10=([0-9.]+) ]]; then
            echo "${BASH_REMATCH[1]}"
            return 0
        fi
    fi
    echo "0.00"
}

wait_for_low_memory_pressure() {
    local retry_count=0
    local current_pressure
    
    while [[ $retry_count -lt $MAX_RETRIES ]]; do
        current_pressure=$(get_some_avg10 "$PRESSURE_FILE")
        
        # 使用bc进行浮点数比较
        if command -v bc >/dev/null 2>&1; then
            # 如果有bc,进行精确比较
            if (( $(echo "$current_pressure < $PRESSURE_THRESHOLD" | bc -l) )); then
                log_message "内存压力正常 ($current_pressure < $PRESSURE_THRESHOLD),开始执行链接"
                return 0
            fi
        else
            # 如果没有bc,使用bash内置的数值比较(假设压力值小于10)
            if (( $(echo "${current_pressure} < ${PRESSURE_THRESHOLD}" | tr -d '.' | head -c3) < $(echo "${PRESSURE_THRESHOLD}" | tr -d '.' | head -c3) )); then
                log_message "内存压力正常 ($current_pressure < $PRESSURE_THRESHOLD),开始执行链接"
                return 0
            fi
        fi
        
        # 计算随机等待时间
        local wait_time=$(( MIN_WAIT_TIME + RANDOM % (MAX_WAIT_TIME - MIN_WAIT_TIME + 1) ))
        
        log_message "内存压力过高: $current_pressure (阈值: $PRESSURE_THRESHOLD), 等待 ${wait_time}秒后重试 (尝试: $((retry_count + 1))/$MAX_RETRIES)"
        
        # 等待
        sleep $wait_time
        
        ((retry_count++))
    done
    
    log_message "错误: 达到最大重试次数 ($MAX_RETRIES),内存压力仍过高: $current_pressure"
    return 1
}

main() {
    log_message "链接器包装器被调用,参数: $*"

    detect_real_linker
    log_message "$(date) - 链接器包装器调用: $REAL_LD, 参数: $*" >> "$LOG_FILE"
    
    if ! wait_for_low_memory_pressure; then
        log_message "错误: 内存压力持续过高" >&2
        return 1
    fi
    
    log_message "执行真实链接器: $REAL_LD $*"
    exec "$REAL_LD" "$@"
}

main "$@"

修改cargo编译使用的链接器

# 临时生效: 使脚本可执行
chmod +x /path/to/ld_wrapper.sh

# 配置RUSTFLAGS环境变量
export RUSTFLAGS="-C link-arg=-fuse-ld=/path/to/ld_wrapper.sh"
cargo build
# 或者配置
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=$(realpath /path/to/wrap-ld.sh)

# 创建符号链接
ln -s /path/to/ld_generic_wrapper.sh /path/to/wrapper_ld
ln -s /path/to/ld_generic_wrapper.sh /path/to/wrapper_lld
ln -s /path/to/ld_generic_wrapper.sh /path/to/wrapper_gold

# 在cargo配置中使用
[target.x86_64-unknown-linux-gnu]
linker = "/path/to/wrapper_ld"

# 实时查看日志
tail -f /tmp/ld_wrapper.log

# 监控内存压力变化
watch -n 1 'cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/memory.pressure'

# 检查链接进程
ps aux | grep ld_wrapper

包装rustc进行编译

使用rustc-wrapper.sh

#!/bin/bash
RUSTC_BIN="$1"
shift

PRESSURE_THRESHOLD="0.10"
MAX_WAIT_TIME=60
MIN_WAIT_TIME=5
MAX_RETRIES=60
PRESSURE_FILE="/sys/fs/cgroup/user.slice/user-$(id -u).slice/memory.pressure"

get_some_avg10() {
    if [[ -f "$PRESSURE_FILE" ]]; then
        local line=$(grep "^some" "$PRESSURE_FILE")
        if [[ $line =~ avg10=([0-9.]+) ]]; then
            echo "${BASH_REMATCH[1]}"
            return 0
        fi
    fi
    # 默认返回 0.00,表示没有压力
    echo "0.00"
}

# 检查是否为链接阶段:包含 --crate-type=bin 或 cdylib,且不包含 --print(避免干扰 Cargo 查询)
if [[ "$*" == *"--crate-type bin"* || "$*" == *"--crate-type cdylib"* ]] && [[ "$*" != *"--print"* ]]; then
    echo '****************Linker stage ...****************'
    retry_count=0
    while [[ $retry_count -lt 100 ]]; do
        current_pressure=$(get_some_avg10 "$PRESSURE_FILE")
        # 使用bc进行浮点数比较
        if command -v bc >/dev/null 2>&1; then
            if (( $(echo "$current_pressure < $PRESSURE_THRESHOLD" | bc -l) )); then
                echo "****************内存压力正常 ($current_pressure < $PRESSURE_THRESHOLD),开始执行 rustc****************"
		break
            fi
        else
                echo "****************必须安装bc工具计算浮点运算****************"
		exit 1
        fi

        # 计算随机等待时间
        wait_time=$(( MIN_WAIT_TIME + RANDOM % (MAX_WAIT_TIME - MIN_WAIT_TIME + 1) ))
        echo "****************内存压力过高: $current_pressure (阈值: $PRESSURE_THRESHOLD), 等待 ${wait_time}秒后重试 (尝试: $((retry_count + 1))/$MAX_RETRIES)****************"
        sleep $wait_time
        ((retry_count++))
    done
fi

exec "$RUSTC_BIN" "$@"