我简要地写了 Sleep.bat 来让电脑休眠 10 秒,以便对我的 ssd 进行电源循环。(如果有人好奇的话,下面会详细介绍用途。)brieflySleep 执行“SCHTASKS /change”命令来安排现有任务,该任务已启用“唤醒计算机以运行此任务”条件,然后等待到计划唤醒时间前 10 秒,然后执行“psshutdown -d -t 0”立即使 PC 进入睡眠状态。 电脑进入睡眠状态,但无法唤醒。
SCHTASKS /change 命令显示“成功”消息。计划任务将在我手动唤醒 PC。(但我不想手动唤醒 PC。我最终的目标是安排 briefSleep 每隔几天在晚上自动运行一次。)如果我修改 briefSleep 以跳过 psshutdown 命令,则计划任务将在正确的时间执行,但这违背了 briefSleep 的目的。
活动电源计划中启用了唤醒定时器。电源计划中禁用了休眠和混合睡眠,电源设置中未选中快速启动。BIOS 配置为启用操作系统来控制唤醒事件。(假设我没有忽略其他必要的 BIOS 设置。主板是 MSI X470 Gaming Plus,BIOS 是 American Megatrends 版本 A.B0,日期为 2019/07/04。)
是否有我忽略的相关设置?
使用 Gibson Research 的 wizmo.exe 而不是 psshutdown 时也会出现同样的问题。
我想知道在 SCHTASKS “成功”执行后,Windows 任务计划程序是否需要更多时间,才能真正使用新安排的时间更新主板的 RTC。briefingSleep 在使 PC 进入睡眠状态前约 20 秒运行 SCHTASK 命令,也许 20 秒不足以让 Windows 更新 RTC。
更新2020-11-14:我做了两个实验:(1)我测试了允许 Windows 有更多时间对 RTC 进行编程是否有所不同,结果发现 20 分钟没有帮助。 (2)我使用 Windows 任务计划程序手动设置唤醒任务的计划时间(而不是使用“schtasks /change”命令让 briefSleep 设置计划时间),并且运行正常:PC 在预定的时间唤醒。 实验 2 证明唤醒定时器工作正常且 BIOS 设置正确。 问题似乎是 SCHTASKS 命令未能导致主板的 RTC 更新... 好像 SCHTASKS 忽略了任务的“唤醒计算机以运行此任务”条件已设置的事实。[2020-11-14更新结束]
更新2020-11-15:通过谷歌搜索,我找到了一个页面(https://ss64.com/nt/schtasks.html) 表示 SCHTASKS 命令不足以完成“唤醒计算机以运行此任务”等属性,而 PowerShell 则足够。所需的 PowerShell 代码如下所示:
$Taskname = <arg1>
$Time = <arg2>
$Settings = New-ScheduledTaskSettingsSet -WakeToRun
$Trigger = New-ScheduledTaskTrigger -At $Time -Once
Set-ScheduledTask -TaskName $Taskname -Trigger $Trigger -Settings $Settings
我还没有测试过这是否能成功唤醒 PC,但它至少会更新 Windows 任务计划程序中的任务属性,我对此持乐观态度。假设它能工作,我仍然需要学习如何让 briefSleep 运行 PowerShell 代码。我通过谷歌搜索找到了两种方法,一种是将 PowerShell 代码放入 .ps1 脚本文件 (https://blog.danskingdom.com/allow-others-to-run-your-powershell-scripts-from-a-batch-file-they-will-love-you-for-it/)并从 .bat 启动 .ps1,或者将 PowerShell 代码嵌入 .bat 文件中(https://stackoverflow.com/questions/2609985/how-to-run-a-powershell-script-within-a-windows-batch-file)。后者似乎适合少量简单的 PowerShell 代码。[2020-11-15更新结束]
以下是简要的Sleep.bat:
@echo off
rem Must be "run as Administrator" else the SCHTASKS command will fail.
rem For Notes and Changelog, see bottom.
rem If command line parameter is DEBUG, the pc will not be put to sleep and
rem the wakeup task will be a task that does not launch an ssd selftest.
if [%1]==[DEBUG] (
set "_DEBUG=TRUE"
) else (
set "_DEBUG=FALSE"
)
rem ------------------------------------------------------------------
rem EDIT THESE CONSTANTS IF NECESSARY TO CUSTOMIZE TO YOUR SYSTEM.
call %~dp0%setCONSTANTS.bat
set /A "SLEEPSECS=10"
set "SLEEPPROG=%PROGDIR%\psshutdown.exe"
set "SLEEPCMD=%SLEEPPROG% -d -t 0 -e p:22:22"
rem set "SLEEPPROG=%PROGDIR%\wizmo.exe"
rem set "SLEEPCMD=%SLEEPPROG% standby"
set "WAKETASK=\Fix_SSD\Wake and launch ssd selftest"
if %_DEBUG%==TRUE set "WAKETASK=\Fix_SSD\Wake and launch cmd"
rem ------------------------------------------------------------------
echo Debug parameter: %_DEBUG%
echo WakeTask: "%WAKETASK%"
echo Sleep duration: %SLEEPSECS% seconds
echo Sleep command: "%SLEEPCMD%"
echo ____
cd %PROGDIR%
setlocal EnableDelayedExpansion
rem Verify existence of utility used to put pc to sleep:
if not exist %SLEEPPROG% (
echo Missing utility "%SLEEPPROG%" which is needed to put pc to sleep.
echo Aborting.
pause
EXIT /B
)
rem Verify existence of task needed to wake pc:
schtasks /query /tn "%WAKETASK%" >NUL 2>&1
if %errorlevel% NEQ 0 (
echo Missing task "%WAKETASK%" which is needed to wake pc.
echo Aborting.
pause
EXIT /B
)
rem Calculate the time of day hh:mm to wake the pc. Because it can't
rem be hh:mm:ss, and because the desired amount of sleep is 10 seconds,
rem this .bat will delay up to a minute or two before putting the
rem pc to sleep, so that the pc can be put to sleep for 10 seconds.
rem To calculate hh:mm we first need to get the current time:
for /F "tokens=1-3 delims=:." %%a in ("!time!") do (
set /A "_HH=%%a, _MM=1%%b-100, _SS=1%%c-100"
)
rem If current time is nearly some hh:mm:00, wait until after hh:mm before proceeding,
rem to ensure enough time remains in the minute to schedule the wakeup task and
rem to sleep %SLEEPSECS% seconds:
set /A "_SecsToMin=60-_SS"
set /A "_Margin=30+SLEEPSECS"
if !_SecsToMin! LEQ !_Margin! (
set /A "SecsToWait=1+_SecsToMin"
echo !date! !time! Waiting !SecsToWait! seconds until the next minute...
TIMEOUT /t !SecsToWait! >nul
for /F "tokens=1-3 delims=:." %%a in ("!time!") do (
set /A "_HH=%%a, _MM=1%%b-100, _SS=1%%c-100"
) )
rem If current time is nearly some hh:00:00, wait until after hh:00 before proceeding,
rem to avoid timing error that would be caused by a a midnight rollover or
rem by a daylight savings time change:
set /A "_SecsToHour=3600-(60*_MM+_SS)"
set /A "_Margin=60+_Margin"
if !_SecsToHour! LEQ !_Margin! (
set /A "SecsToWait=1+_SecsToHour"
echo !date! !time! Waiting !SecsToWait! seconds until the next hour...
TIMEOUT /t !SecsToWait! >nul
for /F "tokens=1-3 delims=:." %%a in ("!time!") do (
set /A "_HH=%%a, _MM=1%%b-100, _SS=1%%c-100"
) )
set "MMDDYYYY=!date:~4,2!/!date:~7,2!/!date:~10,4!!"
set "_HHnow=0!_HH!"
set "_MMnow=0!_MM!"
rem Calculate when the next minute will start:
set /A "_MM=_MM+1"
if !_MM! GEQ 60 set /A "_MM=0, _HH=_HH+1"
rem Since the schtasks command expects time in the format hh:mm, ensure hours
rem and minutes are each two digits by prepending a leading zero if necessary:
set "_HH=0!_HH!"
set "_MM=0!_MM!"
set "HHMM=!_HH:~-2!:!_MM:~-2!"
echo !date! !time! Scheduling "%WAKETASK%" to run at !HHMM!:00
SCHTASKS /change /tn "%WAKETASK%" /ru SYSTEM /rp "" /sd %MMDDYYYY% /st %HHMM%
if %errorlevel% NEQ 0 (
echo SCHTASKS returned an errorlevel that was not 0. Aborting.
pause
EXIT /B
) else (
echo The wakeup task has been scheduled to run at !HHMM!:00.
)
rem Wait until SLEEPSECS seconds before the scheduled wake time, then put the pc to sleep:
set /A "_SleepSS=60-SLEEPSECS"
set /A "SecsToWait=_SleepSS-_SS"
set "_SleepSS=0!_SleepSS!"
echo !date! !time! Waiting !SecsToWait! seconds until !_HHnow:~-2!:!_MMnow:~-2!:!_SleepSS:~-2! before putting pc to sleep...
TIMEOUT /T !SecsToWait! >nul
if !_DEBUG!==TRUE (
echo !date! !time! Since command-line parameter is DEBUG, skipping the sleep.
) else (
echo !date! !time! Putting pc to sleep now using command "%SLEEPCMD%"...
%SLEEPCMD%
)
EXIT /B
------------------------
NOTES
This .bat will put the pc to sleep for about 10 seconds, in order to power
cycle the ssd, to mitigate the Crucial ssd's excessive write amplification
that gets worse the longer the ssd has been powered up.
This .bat relies on the existence of a task in Windows Task Scheduler
that will wake the pc. This .bat uses the SCHTASKS command to schedule that
task to run 10 seconds after this .bat puts the pc to sleep. That task
merely launches an ssd selftest, because power cycling the ssd apparently
aborts a selftest that may have been in progress.
This .bat must be run as Administrator, else the SCHTASKS command will fail.
Assumptions: (1) The wakeup task's property "Wake the pc to run this task"
is set. (2) "Wake Timers" are enabled in the Power Plan. (3) The pc BIOS is
set to let the OS manage the motherboard's RTC (real time clock) wakeup alarm.
This .bat relies on Microsoft's free PSSHUTDOWN.exe utility.
A future version of this .bat may arrange for the brief sleep to happen
in the middle of the night.
CHANGELOG
Initial version, so no changes yet.
这就是我想偶尔关闭 SSD 电源的原因:Crucial MX500 SSD 显然存在一个导致过度写入放大的漏洞,在关闭 SSD 电源后的最初几天内,这种情况并不那么严重。这个漏洞并不为人所知,但在我的 SSD 上,它与众所周知的“虚假当前待处理扇区”漏洞完全相关,这种完美关联暗示大多数使用 MX500 SSD 的人都存在过度写入放大,但他们却没有意识到这一点。人们不会意识到这一点有两个原因:(1) 他们的计算机写入 SSD 的数据比我的计算机多得多,因此他们的过度写入放大与他们必要的写入放大相比并不大,(2) 有些人经常关闭或休眠他们的计算机(不像我的计算机每天 24 小时运行),而他们的电源关闭会有所帮助。
(我为缓解写入放大错误所做的另一件事是几乎不间断地运行 SSD 自检。显然,负责偶尔出现的大量写入突发的 SSD 内部进程的优先级低于自检,因为在自检运行时不会发生写入突发。)