在 Linux(树莓派)上强制关闭外部硬盘

在 Linux(树莓派)上强制关闭外部硬盘

我目前正在使用 Raspberry Pi 设置家庭服务器,并通过 USB 连接外部硬盘。但是,我的硬盘在空闲时永远不会停止旋转。

我已经尝试了提供的提示raspberrypi.org ...没有任何成功。

1.)

sudo hdparm -S5 /dev/sda

返回

/dev/sda:
 setting standby to 5 (25 seconds)
SG_IO: bad/missing sense data, sb[]:  70 00 04 00 00 00 00 0a 00 00 00 00 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

2.)

sudo hdparm -y /dev/sda

返回

/dev/sda:
 issuing standby command
SG_IO: bad/missing sense data, sb[]:  70 00 04 00 00 00 00 0a 00 00 00 00 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

...和 ​​3.)

sudo sdparm --flexible --command=stop /dev/sda

返回

/dev/sda: HDD         1234

...无需降低驱动器的转速。

我使用以下硬件:

  • Inateck FDU3C-2 双端口 USB 3.0 HDD 扩展坞
  • 西部数据 WD10EZRX 绿色 1TB

发送的旋转减速信号有可能在某处被覆盖/丢失/忽略吗?

答案1

我对 hd-idle 不太满意;它运行了但没有起作用。我最终写了下面的脚本:

#!/bin/bash
# This script looks for recent disk access, and if nothing has changed, puts /dev/"drive" into spindown mode.
# This should be used only is the hdparm power management function is not working.
# Call this script with cron or manually as desired
#
#
#
# Change which drive this script looks at by changing the drive variable below:
drive="sda"
#
#
current=`date`
caller=$(ps ax | grep "^ *$PPID" | awk '{print $NF}')
filename="/tmp/diskaccess.txt"
if [ -f "$filename" ]; then
    stat_old=`cat "$filename" | tr -dc "[:digit:]"`
    stat_new=`cat /sys/block/"$drive"/stat | tr -dc "[:digit:]"`
    if [ "$stat_old" == "$stat_new" ]; then
        stat="0"
        echo "The disk hasn't been used; spinning down /dev/$drive"
        echo $stat_old
        hdparm -y /dev/$drive > /dev/null
    else
        stat="1"
        echo $stat_old
        echo $stat_new
        echo "The drive has been used..."
        echo $stat_new > $filename
    fi
else
    echo "/tmp/diskaccess.txt file does not exist; creating it now."
    echo $stat_new > $filename
fi
echo $stat " - " $drive " - " $current " - by: " $caller >> /tmp/diskaccesslog.txt

答案2

是的,这是可能的,但需要一些定制的开发工作,而且并非易事,并且代码将特定于外壳内部的 USB->SATA 桥接芯片。

问题是,USB 桥接器的作用不只是电气转换器。USB 连接的 HDD 模拟具有不同命令集的 SCSI 驱动器。虽然标准读/写/寻道命令始终会转换,但更奇特的启动/停止命令则不会转换。大多数芯片不会这样做。此外,没有通用的芯片级 API。所以如果我编写代码,我必须有 USB 桥接器芯片的编程手册。

底线是,除非您了解芯片的编程细节,并且熟悉 ATA 和 SCSI 指令集以及封装直通命令,否则您只能放弃。工作量太大,而且没有标准。

答案3

您发送的信号完全有可能被忽略。您没有提供

sudo hdparm -I /dev/sdX

它会告诉我们磁盘的功能,但是许多磁盘根本不响应这些命令。

幸运的是,有一个非常方便的实用程序,hd-idle,你可以从这里下载,允许您在指定的一段时间后强制磁盘旋转停止。该程序是专门为 Debian 开发的(但它通常适用于 Linux),因此它的安装对您来说应该非常容易。我只是希望它也能在 ARM 架构上运行,这是我无法测试的。

编辑:它在 raspbian 上正确编译并安装。

答案4

我对 user300457 发布的很好的脚本做了一些修改。

变化:

  1. 调用脚本时,添加驱动器作为参数(例如“sdb”)。这使脚本可以控制多个驱动器(只需将具有不同驱动器的脚本多次添加到 crontab 即可)。
  2. 我需要能够检查驱动器是否处于待机模式(在另一个不相关的应用程序中),但在检查状态时,原始脚本认为驱动器已发生变化。这实际上意味着,如果定期检查状态,驱动器将永远不会停止旋转。因此...脚本已更新,以便能够处理检查当前状态。
  3. /var/log/hdd_spindown.log 中会生成更清晰的日志文件,仅跟踪驱动器旋转和停止的时间

下面包含该脚本,并附有描述如何使用它的注释。

我希望这对某些人有用:-)

#!/bin/bash
# This script looks for recent disk access, and if nothing has changed, puts the drive into spindown mode
#
# 1. Copy the script to /root:
#      sudo cp hdd_spindown.sh /root
#
# 2. Add the script to crontab:
#      sudo crontab -e
#
# 3. Add the following line to the crontab:
#      */30 *   *   *   *  /root/hdd_spindown.sh >> /var/log/hdd_spindown.log <drive name> 2>&1
#    where <drive name> could be "sda", "sdb" etc. 
#
# 4. Reload crontab:
#      sudo service cron reload
#
# This will run the script every 30 minutes - i.e. spin down the drive when not being accessed for 30 to 60 minutes
#
# A file logging the spinups/downs is created in /var/log/hdd_spindown.log

drive="$1"
current_spin_state=$(/usr/sbin/hdparm -C /dev/$drive | awk '/state/{print $NF}')

# If the drive is already in standby mode, there is no need to do more
if [ "$current_spin_state" != "standby" ]; then
    current_time=$(date +"%Y-%m-%d %T")
    script_name=$(basename -- "$0")
    filename="/tmp/${script_name}_${drive}_status.txt"
    current_sectors=$(cat /sys/block/"$drive"/stat | awk '{print $3,$7}')

    if [ -f "$filename" ]; then
        previous_drive_status=$(cat "$filename")
        previous_spin_state=$(echo $previous_drive_status | awk '{print $1}')
        previous_sectors=$(echo $previous_drive_status | awk '{print $2,$3}')

        # If the drive has just spun up store new state and add to log
        if [ "$current_spin_state" != "$previous_spin_state" ]; then
            echo "$current_time /dev/$drive has spun up since the last check"
            echo "$current_spin_state $current_sectors" > $filename
        else 
            # Check if drive sectors have been read or written to 
            if [ "$current_sectors" == "$previous_sectors" ]; then
                # If nothing changed, spin down the drive
                echo "$current_time Spinning down /dev/$drive"
                /usr/sbin/hdparm -y /dev/$drive > /dev/null
                echo "standby $current_sectors" > $filename
            else
                # If something changed, just store the new drive status
                echo "$current_spin_state $current_sectors" > $filename
            fi
        fi
    else
        echo "$current_time File $filename does not exist - creating it now"
        echo "$current_spin_state $current_sectors" > $filename
    fi
fi

相关内容