Zum Inhalt

Monat: April 2013

Spindown bei Festplatten verhindern

In meinem Notebook MSI GE60 ist eine Western Digital WD10JPVT Blue als Festplatte verbaut, die leider das tolle Feature besitzt, bereits nach ca. 3 Sekunden einen Spindown durchzuführen. Wenn danach dann wieder auf die Festplatte zugegriffen wird, gibts eine kurze Verzögerung, die nicht wirklich lange dauert, aber bemerkbar ist und nervt. Vom Hersteller Western Digitial gibt es für das Problem leider keine Lösung bzw. ich habe keine gefunden.

Mit ein paar Zeilen Java läßt sich das Problem umgehen: Es werden alle x Sekunden ein paar Zeichen Text in eine Datei geschrieben, die nach x Durchläufen gelöscht und neu erstellt wird. Das kostet kaum Performance, sorgt aber für minimale Last auf der Festplatte.

package de.denniswilmsmann.preventspindown;

import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/*
 * $Log: $
 */

/**
 * <br/>Created: 18.04.2013
 * <br/>Author: Dennis Wilmsmann
 * <br/>Last committed $Date: $
 * @author  $Author: $
 * @version $Revision: $
 */
public class PreventSpindown {

    private static final String PREFIX = "preventspindown_";
    private static PreventSpinDownThread t;
    private static int secondsPerLine = 5;
    private static int linesPerFile = 10;
    private static TrayIcon icon;
    private static Image stopImage;
    private static Image runImage;
    private static String running = "Prevent Spindown - Running";
    private static String stopped = "Prevent Spindown - Stopped";

    /**
     * @param args String[]
     * @throws URISyntaxException
     * @throws IOException
     * @throws AWTException
     */
    public static void main(String[] args) throws URISyntaxException, IOException, AWTException {
        // check command line params
        if (args != null) {
            for (String arg : args) {
                if (arg.startsWith("secondsPerLine")) {
                    secondsPerLine = Integer.parseInt(arg.split("=")[1]);
                } else if (arg.startsWith("linesPerFile")) {
                    linesPerFile = Integer.parseInt(arg.split("=")[1]);
                }
            }
        }

        // at least 1 line per second and 1 line per file
        secondsPerLine = Math.max(1, secondsPerLine);
        linesPerFile = Math.max(1, linesPerFile);

        SystemTray tray = SystemTray.getSystemTray();
        Toolkit toolkit = Toolkit.getDefaultToolkit();

        stopImage = toolkit.getImage("drive.png");
        runImage = toolkit.getImage("drive-green.png");

        PopupMenu menu = new PopupMenu();

        MenuItem startItem = new MenuItem("Start");
        startItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (t == null || !t.isRunning()) {
                    t = new PreventSpinDownThread(secondsPerLine, linesPerFile);
                    t.start();
                    icon.setImage(runImage);
                    icon.setToolTip(running);
                }
            }
        });
        menu.add(startItem);

        MenuItem stopItem = new MenuItem("Stop");
        stopItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (t != null) {
                    t.setRunning(false);
                    icon.setImage(stopImage);
                    icon.setToolTip(stopped);
                }
            }
        });
        menu.add(stopItem);

        MenuItem closeItem = new MenuItem("Close");
        closeItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        menu.add(closeItem);
        icon = new TrayIcon(stopImage, stopped, menu);
        icon.setImageAutoSize(true);

        tray.add(icon);
    }

    public static class PreventSpinDownThread extends Thread {

        private final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
        private int secondsPerLine = 5;
        private int linesPerFile = 10;
        private boolean running = true;

        public void setRunning(boolean running) {
            this.running = running;
        }

        public PreventSpinDownThread(int secondsPerLine, int linesPerFile) {
            this.secondsPerLine = secondsPerLine;
            this.linesPerFile = linesPerFile;
        }

        @Override
        public void run() {
            // remove old files first
            File tmp = new File("");
            File folder = new File(tmp.getAbsolutePath());
            if (folder.isDirectory()) {
                for (File f : folder.listFiles()) {
                    if (f.getName().startsWith(PREFIX) && f.getName().contains("txt")) {
                        System.out.println("remove old file " + f.getAbsolutePath());
                        f.delete();
                    }

                }
            }

            System.out.println("write a new line each " + secondsPerLine + " seconds");
            System.out.println("write a new file each " + linesPerFile + " lines");

            int count = 0;
            File newTextFile = null;
            String filename = null;

            try {
                while (running) {
                    if (newTextFile == null) {
                        filename = PREFIX + df.format(new Date()) + ".txt";
                        newTextFile = new File(filename);
                        System.out.println("create new file " + newTextFile.getAbsolutePath());
                        newTextFile.deleteOnExit();
                    }
                    FileWriter fileWriter = new FileWriter(newTextFile, true);

                    String random = UUID.randomUUID().toString();
                    System.out.println(random);

                    fileWriter.write(random);
                    fileWriter.write("\n");
                    fileWriter.close();
                    count++;

                    icon.setToolTip(PreventSpindown.running + " " + count + "/" + linesPerFile + "\n" + filename);

                    Thread.sleep(1000 * secondsPerLine);

                    if (count >= linesPerFile) {
                        count = 0;
                        System.out.println("remove old file " + newTextFile.getAbsolutePath());
                        newTextFile.delete();
                        newTextFile = null;
                    }
                }
                // delete file after stop
                newTextFile.delete();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public boolean isRunning() {
            return running;
        }
    }
}

Update vom 18.04.2013: Die Konsole wird per default nicht mehr angezeigt und dafür gibts ein kleines Icon im Systray, um per Rechtsklick zu starten bzw. zu stoppen.

psd1

psd3

psd2

Ich habe mal alles zusammen in ein ausführbares JAR File gepackt mit dazugehöriger start.bat Datei für Windows. Das sollte eigentlich auf jedem System laufen, auf dem Java 6 (oder aktueller) installiert ist: preventspindown.zip

Das ZIP File irgendwo entpacken und via start.bat starten. Wie oft eine neue Zeile geschrieben werden soll und wann eine neue Datei erstellt wird, wird über die Parameter secondsPerLine (default=5) und linesPerFile (default=10) gesteuert. Einfach nach den eigenen Wünschen in der start.bat anpassen.

Wenn in der start.bat wieder mit java.exe anstatt javaw.exe gestartet wird, erscheint auch wieder die Konsole:

preventspindown