Linux中基于Python2.7.5实现的CPU压力工具

脚本作用

由于某些特殊原因,需要保持服务器的CPU使用率在一个水平线以上,因此制作了这个脚本,目标是在不依赖其他组件的情况下使用Python脚本增加服务器的CPU压力。

 

使用要求

  • Linux
  • Python 2.7.5

 

创建脚本

首先使用创建脚本文件

/usr/local/cpu-loader/cpu_loader.py

授予脚本运行权限

chmod +x /usr/local/cpu-loader/cpu_loader.py

编辑脚本文件内容 vim /usr/local/cpu-loader/cpu_loader.py 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
CPU 负载维持服务 - Python 2.7.5 
"""

from __future__ import print_function
import os
import sys
import time
import signal
import math
import random
import logging.handlers
import errno

PID_FILE = "/tmp/cpu_loader.pid"
LOG_FILE = "/tmp/cpu_loader.log"
TARGET = 6.0          # 目标CPU%
TOLERANCE = 1.0       # 容差
MAX_LOG_BYTES = 2 * 1024 * 1024
MAX_LOG_BACKUPS = 3

class Monitor(object):
    def __init__(self):
        self.prev = None
        
    def get(self):
        try:
            with open('/proc/stat') as f:
                fields = map(int, f.readline().split()[1:])
                fields = list(fields)
            idle = fields[3] + fields[4]
            total = sum(fields)
            
            if self.prev is None:
                self.prev = (idle, total)
                return 0.0
                
            pidle, ptotal = self.prev
            self.prev = (idle, total)
            diff_idle = idle - pidle
            diff_total = total - ptotal
            
            if diff_total == 0:
                return 0.0
            return 100.0 * (1.0 - float(diff_idle) / diff_total)
        except:
            return 0.0

class CPULoader(object):
    def __init__(self):
        self.running = False
        self.monitor = Monitor()
        self.logger = None
        self.work_ratio = 0.0    # 工作时长占比 (0.0-1.0)
        self.idle_mode = False
        
    def setup_logging(self, foreground=False):
        self.logger = logging.getLogger('CPULoader')
        self.logger.setLevel(logging.INFO)
        self.logger.handlers = []
        
        handler = logging.handlers.RotatingFileHandler(
            LOG_FILE, maxBytes=MAX_LOG_BYTES, backupCount=MAX_LOG_BACKUPS
        )
        handler.setFormatter(logging.Formatter(
            '%(asctime)s [%(levelname)s] %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        ))
        self.logger.addHandler(handler)
        
        if foreground:
            console = logging.StreamHandler(sys.stdout)
            self.logger.addHandler(console)
            
    def log(self, msg, level=logging.INFO):
        if self.logger:
            self.logger.log(level, msg)
            
    def read_pid(self):
        try:
            if os.path.exists(PID_FILE):
                with open(PID_FILE, 'r') as f:
                    return int(f.read().strip())
        except:
            return None
            
    def write_pid(self, pid):
        with open(PID_FILE, 'w') as f:
            f.write(str(pid))
            
    def remove_pid(self):
        if os.path.exists(PID_FILE):
            os.remove(PID_FILE)
            
    def do_work(self, duration_sec):
        """高强度计算,优化以最大化CPU占用"""
        if duration_sec <= 0:
            return
        start = time.time()
        # 使用更密集的整数运算(比浮点math函数更消耗CPU)
        x = random.randint(10000, 99999)
        iterations = 0
        
        while time.time() - start < duration_sec:
            # 内层循环减少time.time()调用开销
            for _ in range(5000):
                # 高强度整数运算,确保CPU无法预测优化
                x = (x * 123456789 + 987654321) & 0xFFFFFFFF
                x = ((x << 13) | (x >> 19)) & 0xFFFFFFFF  # 位旋转
                x = (x * x + 12345) & 0xFFFFFFFF
                iterations += 1
                
    def run_service(self):
        self.running = True
        self.log("服务启动 | 目标CPU: {}% | 单线程增强版".format(TARGET))
        
        # 初始化CPU监控
        self.monitor.get()
        time.sleep(1)
        
        cycle_count = 0
        last_check_time = time.time()
        
        try:
            while self.running:
                # 动态调整检测周期:负载低时延长,负载高时缩短
                current_time = time.time()
                elapsed = current_time - last_check_time
                last_check_time = current_time
                
                cpu = self.monitor.get()
                error = cpu - TARGET  # 正误差=过高,负=过低
                
                # 快速上升,缓慢下降策略
                if error < -TOLERANCE:  # CPU过低(低于5%)
                    # 误差越大,增加越快(加速逼近目标)
                    increment = max(0.02, min(0.1, abs(error) * 0.02))
                    self.work_ratio += increment
                elif error > TOLERANCE:  # CPU过高(高于7%)
                    # 缓慢减少,避免震荡
                    self.work_ratio -= 0.015
                else:
                    # 在容差内(5%-7%),微调
                    if error > 0:
                        self.work_ratio -= 0.005
                    else:
                        self.work_ratio += 0.005
                
                # 关键修复:放宽上限到100%,允许满负载运转
                self.work_ratio = max(0.0, min(1.0, self.work_ratio))
                
                # 判断是否空闲模式(work_ratio接近0且CPU远低于目标)
                self.idle_mode = (self.work_ratio < 0.001 and cpu < TARGET * 0.3)
                
                # 自适应周期:根据误差动态调整
                if abs(error) > 3.0:
                    cycle_time = 0.2  # 偏差大时,200ms周期快速调整
                else:
                    cycle_time = 1.0  # 接近目标时,1秒周期稳定运行
                
                work_time = self.work_ratio * cycle_time
                sleep_time = cycle_time - work_time
                
                # 工作阶段
                if work_time > 0.001:
                    self.do_work(work_time)
                
                # 休眠阶段
                if self.idle_mode:
                    # 空闲模式:大幅延长检测周期
                    time.sleep(5.0)
                    self.log("CPU: {:5.1f}% | 空闲模式 | 工作比: {:.1f}%".format(
                        cpu, self.work_ratio * 100))
                else:
                    # 确保最少休眠1ms,避免CPU空转
                    time.sleep(max(0.001, sleep_time))
                    
                cycle_count += 1
                # 每5秒记录一次日志(减少IO)
                if cycle_count % 5 == 0:
                    self.log("CPU: {:5.1f}% | 目标: {}% | 工作比: {:.1f}% | 误差: {:+.1f}%".format(
                        cpu, TARGET, self.work_ratio * 100, error))
                        
        except Exception as e:
            self.log("错误: {}".format(str(e)), logging.ERROR)
        finally:
            self.log("服务停止")
            
    def daemonize(self):
        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError:
            sys.exit(1)
        os.chdir("/")
        os.setsid()
        os.umask(0)
        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError:
            sys.exit(1)
            
        sys.stdout.flush()
        sys.stderr.flush()
        with open(os.devnull, 'r') as f:
            os.dup2(f.fileno(), sys.stdin.fileno())
        with open(os.devnull, 'a+') as f:
            os.dup2(f.fileno(), sys.stdout.fileno())
            os.dup2(f.fileno(), sys.stderr.fileno())
            
    def start(self):
        pid = self.read_pid()
        if pid:
            try:
                os.kill(pid, 0)
                print("服务已在运行 (PID: {})".format(pid))
                return
            except:
                pass
                
        print("正在启动...")
        self.setup_logging(foreground=False)
        self.daemonize()
        self.setup_logging(foreground=False)
        self.write_pid(os.getpid())
        
        try:
            self.run_service()
        finally:
            self.remove_pid()
            
    def stop(self):
        pid = self.read_pid()
        if not pid:
            print("服务未运行")
            return
        try:
            os.kill(pid, signal.SIGTERM)
            time.sleep(0.5)
            for _ in range(10):
                try:
                    os.kill(pid, 0)
                    time.sleep(0.3)
                except OSError:
                    break
            print("服务已停止")
        except OSError:
            print("进程已不存在")
        finally:
            self.remove_pid()
            
    def status(self):
        pid = self.read_pid()
        if pid:
            try:
                os.kill(pid, 0)
                print("● 运行中 | PID: {} ".format(pid))
                return
            except:
                pass
        print("○ 未运行")
        
    def run_foreground(self):
        self.setup_logging(foreground=True)
        print("前台运行 | 目标CPU: {}% | Ctrl+C停止\n".format(TARGET))
        try:
            self.run_service()
        except KeyboardInterrupt:
            self.running = False
            print("\n已停止")

def main():
    if len(sys.argv) < 2:
        print("用法: {} {{start|stop|status|run}}".format(sys.argv[0]))
        return
    
    loader = CPULoader()
    
    if sys.argv[1] == "start":
        loader.start()
    elif sys.argv[1] == "stop":
        loader.stop()
    elif sys.argv[1] == "status":
        loader.status()
    elif sys.argv[1] == "run":
        loader.run_foreground()
    else:
        print("未知命令")

if __name__ == "__main__":
    main()

保存文件后尝试执行

/usr/local/cpu-loader/cpu_loader.py run      #前台运行
/usr/local/cpu-loader/cpu_loader.py start    #后台运行
/usr/local/cpu-loader/cpu_loader.py stop     #停止运行
/usr/local/cpu-loader/cpu_loader.py status   #运行状态

 

注册成系统服务

创建服务文件

touch /etc/systemd/system/cpu-loader.service

在文件中输入以下内容

[Unit]
Description=CPU Loader Service
After=network.target

[Service]
Type=forking
PIDFile=/usr/local/cpu-loader/cpu_loader.pid
ExecStart=/usr/bin/python /usr/local/cpu-loader/cpu_loader.py start
ExecStop=/usr/bin/python /usr/local/cpu-loader/cpu_loader.py stop
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

启动服务并设置开机启动

# 重新加载 systemd
sudo systemctl daemon-reload

# 设置开机自启
sudo systemctl enable cpu-loader

# 立即启动
sudo systemctl start cpu-loader

# 查看状态
sudo systemctl status cpu-loader

 

posted @ 2026-01-29 16:13  安培昌浩  阅读(0)  评论(0)    收藏  举报