Open11

ローカルMQTTブローカーの開発

masafuromasafuro

GMKTech NucBox5(中古)

hostnamectl

Operating System: Ubuntu 24.04.2 LTS
Kernel: Linux 6.8.0-53-generic
Architecture: x86-64
Hardware Vendor: GMKtec
Hardware Model: NucBox5
Firmware Version: KB05_GMKTEC_1_02_AUTO001
Firmware Date: Tue 2022-07-26

masafuromasafuro

dpkg -l | grep lubuntu-desktop

ii lubuntu-desktop 24.04.10 amd64 Lubuntu Desktop environment

masafuromasafuro

システム安定化設定

  • コア 0が現在、仕事が少ないみたい→ コア0をMQTTブローカーの設定する。
  1. Intel P-Stateを無効化する
  2. performanceガバナーを設定する。
  3. CPUクロックが最大クロックで固定されているかを確認する。

Intel P-State無効化し、CPUをperformanceへ設定、周波数固定化

sudo nano /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_pstate=disable cpufreq.default_governor=performance processor.max_cstate=0 idle=poll hwp_off=1"
sudo update-grub
sudo reboot

確認

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

performance

C-Stateが無効化されているか?

sudo cat /sys/module/processor/parameters/max_cstate

0

HWPが無効化されているか?

ring@ring-nucbox5:~$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_driver

acpi-cpufreq

周波数が安定しているか?

watch -n 1 "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq"

周波数の値が安定しているか?
Ctrl +cで終了

masafuromasafuro

負荷テストの実施

stress -c 4 -t 600
したら周波数が2.8GHzから2.6GHzまで落ちた。

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq

最大周波数を落として、安定化させる。

対応周波数の確認

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies

2000000にする。

sudo nano /etc/systemd/system/cpu-freq-limit.service

[Unit]
Description=Set CPU Max Frequency to 2.0GHz for Stability
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do echo 2000000 > $i; done'
ExecStart=/bin/bash -c 'for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > $i; done'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl enable cpu-freq-limit.service
sudo systemctl start cpu-freq-limit.service

確認

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq

2000000

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

performance

masafuromasafuro
検査

温度

watch -n 1 "sensors"

負荷

stress -c 4 -t 600

クロック

watch -n 1 "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq"

masafuromasafuro

負荷試験とデータ収集

nano cpu_logging.sh

600 → 10分ごとに計測

#!/bin/bash

while true; do 
  echo "$(date +%s),$(sensors | grep 'Tctl' | awk '{print $2}' | tr -d '+°C'),$(cat /sys/class/powercap/intel-rapl:0/energy_uj),$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)" >> cpu_data.csv
  sleep 600
done

権限付与

chmod +x cpu_logging.sh

実行

./cpu_logging.sh & LOG_PID=$! && stress -c 4 -t 86400 && kill $LOG_PID


電源確認

cat /sys/power/state

2025/02/15 12:53 電源設定確認中&負荷試験実行はまだ。

masafuromasafuro

電源管理:スリープ無効化

  1. systemd-logind
  2. xfc4-power-manager
systemd-logind

現在の設定の確認

cat /etc/systemd/logind.conf | grep -E 'HandleLidSwitch|IdleAction'

設定の修正

sudo nano /etc/systemd/logind.conf

HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
IdleAction=ignore
IdleActionSec=0

設定を適用

sudo systemctl restart systemd-logind
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
sudo reboot

設定されたか確認

systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target

全部maskされていること。

masafuromasafuro
LXQtのスリープ無効化

mkdir -p ~/.config/autostart
cp /etc/xdg/autostart/lxqt-powermanagement.desktop ~/.config/autostart/
echo 'Hidden=true' >> ~/.config/autostart/lxqt-powermanagement.desktop

cat /etc/systemd/logind.conf | grep HandlePowerKey
sudo nano /etc/systemd/logind.conf

HandlePowerKey=poweroff
masafuromasafuro

仮テスト

10分

sudo ./cpu_logging.sh & LOG_PID=$! && stress -c 4 -t 600 && kill $LOG_PID

20:52

データの確認

less cpu_data.csv

masafuromasafuro

cpu_logging.py

sudo python3 cpu_logging.py

ctrl+Cで停止するまで、データを収集して保存する。
stressを実行し、温度やCPUクロック周波数などを取得する。

import time
import csv
import os
import psutil
import subprocess

# 設定値
SLEEP_INTERVAL = 60  # データ取得間隔(秒単位、10分)
CSV_FILE = "cpu_data.csv"  # 出力CSVファイル名
ENERGY_UJ_PATH = "/sys/class/powercap/intel-rapl:0/energy_uj"  # 消費エネルギーファイル
CPU_FREQ_PATH = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"  # CPUクロック周波数ファイル
SENSORS_KEYWORD = "Package id 0"  # `sensors` コマンドで取得する温度のキー
STRESS_DURATION = 600  # 負荷テストの実行時間(秒)
STRESS_CORES = 4  # 使用するCPUコア数

def get_cpu_temp():
    """CPUの温度を取得する"""
    try:
        result = subprocess.run(["sensors"], capture_output=True, text=True, check=True)
        for line in result.stdout.split("\n"):
            if SENSORS_KEYWORD in line:
                parts = line.split()
                if len(parts) >= 4:  # インデックスエラーを防ぐ
                    try:
                        return float(parts[3].replace("+", "").replace("°C", ""))
                    except ValueError:
                        return -1.0  # 異常値として -1.0 を返す
    except subprocess.CalledProcessError:
        return -1.0  # `sensors` コマンドの実行失敗時

    return -1.0  # `SENSORS_KEYWORD` が見つからなかった場合



def get_cpu_freq():
    """CPUのクロック周波数を取得する"""
    with open(CPU_FREQ_PATH, "r") as f:
        return int(f.read().strip()) / 1000  # kHz → MHz

def get_cpu_usage():
    """CPU使用率を取得する"""
    return psutil.cpu_percent(interval=1)

def get_energy_uj():
    """消費エネルギー(microjoules)を取得する"""
    try:
        result = subprocess.run(["sudo", "cat", ENERGY_UJ_PATH], capture_output=True, text=True)
        return int(result.stdout.strip())
    except Exception:
        return None

def run_stress():
    """負荷テストを実行する"""
    print(f"Starting stress test: {STRESS_CORES} cores for {STRESS_DURATION} seconds")
    subprocess.Popen(["stress", "-c", str(STRESS_CORES), "-t", str(STRESS_DURATION)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def main():
    """メイン処理:データを定期的に記録し、負荷テストを実行"""
    prev_energy = get_energy_uj()
    prev_time = time.time()

    with open(CSV_FILE, "a", newline="") as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(["timestamp", "cpu_temp", "power", "cpu_freq", "cpu_usage"])

        while True:
            run_stress()  # 負荷テストを実行

            time.sleep(SLEEP_INTERVAL)  # 設定した間隔でデータ取得

            curr_energy = get_energy_uj()
            curr_time = time.time()
            energy_diff = (curr_energy - prev_energy) / 1_000_000  # µJ → J
            time_diff = curr_time - prev_time
            power = round(energy_diff / time_diff, 2)

            cpu_temp = get_cpu_temp()
            cpu_freq = get_cpu_freq()
            cpu_usage = get_cpu_usage()

            writer.writerow([int(curr_time), cpu_temp, power, cpu_freq, cpu_usage])
            csvfile.flush()

            prev_energy = curr_energy
            prev_time = curr_time

if __name__ == "__main__":
    main()