Wednesday, February 18, 2009

Auto-pausing Virtual Machine

I've been using VirtualBox for a while to run Windows-only programs without restarting into one of my Windows installs. Lately I've been playing games (especially NES) in the virtual machine, with a result of high CPU usage, and plenty of heat.

I've started scaling back my CPU to half speed while using the virtual machine which solved the heat problem, but I was still at a loss about the CPU usage. I knew I could pause the machine when I'm not using it, but there are plenty of times where I go do other things and forget. I decided to find a way to make it automatically pause.

The best way I could think of to detect when I'm not using the virtual machine was to use the screen saver in Windows. A simple test confirmed it was possible to run any exe as a screen saver. I have directories from my host machine mounted inside Windows, so with a simple C program I could edit (or create) a file on my host machine that signals I'm not using the machine.

The next part is to make some kind of script check the file on a regular basis and pause the machine when needed. This script should also only run when the machine is running.

First the C code:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
FILE *outfile;

outfile = fopen("G:/virtual_box_pause.txt", "w");
if (NULL == outfile)
{
printf("ERROR: Cannot open (output) file)\n");
exit(EXIT_FAILURE);
}

fputc('1', outfile);
fputc('\n', outfile);

fclose(outfile);

exit(EXIT_SUCCESS);
}

This code writes a "1" into a file on the host machine, "virtual_box_pause.txt" when run. I compiled the code, renamed it with a .scr extension, and installed. That was step one.

Step two, a perl script to monitor the same file for changes:

#!/usr/bin/perl
use warnings;
use strict;

print "CPU scaling to 1.6GHz\n";
`cpufreq-selector -f 1600000`;

print "Starting Windows XP\n";
`VBoxManage startvm "Windows XP"`;

sleep 20;
my $state = `VBoxManage showvminfo "Windows XP" | grep State`;

print "Monitoring for paused state\n";

while ( !($state =~ /saved/) )
{
open PAUSE, "<", "~/virtual_box_pause.txt"; my $pause_value = <PAUSE>;
close PAUSE;
chomp $pause_value;

if ($pause_value == 1)
{
`VBoxManage controlvm "Windows XP" pause`;
open PAUSE, ">", "~/virtual_box_pause.txt";
print localtime() . " Pause command Sent\n";
print PAUSE "0";
close PAUSE;
}

sleep 5;
$state = `VBoxManage showvminfo "Windows XP" | grep State`;
}

print "Virtual machine no longer running\n";
print "CPU scaling to OnDemand\n";
`cpufreq-selector -g OnDemand`;

print "Exiting\n";

This code automatically scales back the CPU, watches to see if I shut down the machine, and pauses when need. When I shut down the machine, it brings the CPU back to regular speed before exiting.

After making these, I simply had to run the perl script when ever I wanted to use windows. I made a launcher on my desktop to do just that:

gnome-terminal -t "Windows XP" -x ~/virtual_box_pause.pl

Now I have a virtual machine which automatically pauses when not in use. To adjust the time delay, simply adjust your screen saver settings.

No comments:

Post a Comment