Using a ramdisk can significantly improve the performance of many disk-intensive applications. In this post I'll describe how to set up a ramdisk for MacOS X 10.6, which will persist across system sleep and wakeups, and even reboots.
Note: 22-Mar-2011: I've updated this article with some significant improvements.
I was motivated to look into this while running unit tests for Drupal development. This is a very database intensive operation, and running a full suite of tests takes many hours on a typical laptop. This is a perfect situation for deploying a ramdisk.
A little bit of googling led to this page, which describes how to set up a ramdisk for MacOS 10.6 (and 10.4/10.5, which I won't discuss here). This worked beautifully. Here's how to set up the Ramdisk:
diskutil erasevolume HFS+ "ramdisk" `hdiutil attach -nomount ram://120000`
Note the backticks around the hdiutil command. This will create a 120,000 block ramdisk (approximately 60MB) and make it available as a device (e.g., /dev/disk1). It then formats the disk as an HFS+ file system and mounts it on /Volumes/ramdisk.
That 's it! You might want to put the command above into a shell script, if you expect to create ramdisks frequently.
Notes: You can unmount and then re-mount the ramdisk, and the device (in this case, /dev/disk1) and files on it will persist across unmounts and mounts, until the system goes to sleep or reboots.
It appears that the behaviour with ramdisks not surviving sleep mode is new with Snow Leopard. (Can anyone confirm this?)
We have some more work to do if we want our ramdisk to "just work" across sleep/wake cycles and reboots. The solution is to use a utility called sleepwatcher, which lets us run scripts on sleep and wake events.
Here's what I did to set up sleepwatcher:
/usr/local/sbin. sudo chgrp admin /usr/local/sbin/sleepwatcher
sudo chmod 750 /usr/local/sbin/sleepwatcher
We now must arrange for the sleepwatcher binary to be executed on system startup. It is important to run sleepwatcher as a local user LaunchDaemon, as opposed to a local user LaunchAgent, since it must execute some commands as root whenever the system wakes from sleep. To have sleepwatcher launched as a daemon, copy the following plist file into ~/Library/LaunchDaemons/de.bernhard-baehr.sleepwatcher.plist. On each subsequent reboot, sleepwatcher will be started.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>de.bernhard-baehr.sleepwatcher</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/sleepwatcher</string>
<string>-V</string>
<string>-s /etc/sleepwatcher/sleep.sh</string>
<string>-w /etc/sleepwatcher/wakeup.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
If you examine the above file, you'll see that there are two scripts mentioned: sleep.sh and wakeup.sh, each in the directory /etc/sleepwatcher which you should create. Copy the following files into it, make changes as appropriate.
Here is sleep.sh:
#!/bin/sh
#Adapted from http://hints.macworld.com/article.php?story=20080329201951648
#dump the database
sqlite3 /Volumes/ramdisk/sqlite/d7test01 .dump > /Volumes/ramdisk/sqlite/d7test01.dump
rm /Volumes/ramdisk/sqlite/d7test01
# Save everything from /Volumes/ramdisk to a backup directory /var/spool/rdimage
rsync -av --delete /Volumes/ramdisk/ /var/spool/rdimage/
diskutil unmount /dev/disk1
diskutil eject /dev/disk1
And here is wakeup.sh.
#!/bin/sh
#Adapted from http://hints.macworld.com/article.php?story=20080329201951648
# Create a 60MB ramdisk, mount on /Volumes/ramdisk
diskutil erasevolume HFS+ "ramdisk" `hdiutil attach -owners on -nomount ram://122000`
diskutil enableOwnership /Volumes/ramdisk
#restore ramdisk image from disk
rsync -a /var/spool/rdimage/ /Volumes/ramdisk/
sqlite3 /Volumes/ramdisk/sqlite/d7test01 < /Volumes/ramdisk/sqlite/d7test01.dump
chgrp _www /Volumes/ramdisk/sqlite
chmod 775 /Volumes/ramdisk/sqlite
chgrp _www /Volumes/ramdisk/sqlite/d7test01
chmod 664 /Volumes/ramdisk/sqlite/d7test01
The directory ~/.sleepwatcher should be protected from prying eyes, and it is imperative that the directory and its files be not writable by anyone other than root.
These examples are somewhat specialized, since I set this up to speed up Drupal unit testing. You will want to tweak the files sleep.sh and wakeup.sh to suit your own purposes.
My version of sleep.sh dumps the database to a dump file, removes the database, and copies the files on the ramdisk to /var/spool/rdimage. Finally, it unmounts and ejects the ramdisk, which destroys the contents of the ramdisk, and removes the associated devices.
On wakeup, the system creates a new ramdisk, enables ownership, and restores the ramdisk from the files in /var/spool/rdimage. It creates a new database from the database dump, and assigns appropriate ownership and permissions so that the database is readable/writeable by the webserver process.
Sleep.sh and wakeup.sh each take about a second to execute.
That's it! You can now put your mac to sleep and your ramdisk and files will be there when it wakes up. There's a few hacks here that should probably be cleaned up. For example, I don't halt Drupal before I do the DB backup. I don't check for errors. I don't check for multiple external disks or ramdisks. If you extend this, add any error checking, or come up with another nice application for sleepwatcher and/or ramdisks, please add a comment.
Comments
Post new comment