puplogo

 How Puppy works


Page updated: 9 Sept 2006

In January 2006 I commenced work on a total redesign of the underlying structure of Puppy, nicknamed "puppy2". If you have a Puppy with version number 0.x or 1.x then you have the previous architecture -- let's call it "puppy1". As puppy1 is widely in use, the explanation of how he works is retained and I have split this page into two: the top half is for puppy2 and the bottom half for puppy1.

How Puppy works, take 2

Puppy2 is revolutionary, and the question on your mind if you have studied Puppy1 is "Will I have to relearn everything?", or "Is it more complicated than puppy1?"

The answer to the first question is no, you will find much in common. From a users point of view, it is still the same Puppy, apparently unchanged. Under the hood, from the developers point of view, the basic ideas, such as running in RAM, are still there -- just some implementation details have changed.

The answer to the second question is no, puppy2 is actually simpler. The startup and shutdown scripts are simpler. Options required in the isolinux.cfg or syslinux.cfg files are simpler.

Is puppy2's underlying architecture based on another distro? In other words, did I get the idea from somewhere? No, it gradually crystallised in my mind. There is a slight resemblance to Noppenlinux, but I found that out afterward.

The rationale for puppy2

As puppy1 was chugging along quite nicely, why make the change?

I can give several reasons, but I guess what it comes down to is the change just had to happen. The improvements of puppy2 are just too good. Um, let me have a go at itemising how puppy2 is better:

  1. Works with any size Flash drive (minimum 128M).
  2. Saves ramdisk (your working files) to Flash drive every 30 minutes, so extending lifetime of flash media (by restricting the number of writes).
  3. Works on PCs with very little RAM, probably as little as 32M.
  4. Boots very fast.
  5. Defaults to running totally in ramdisk on first boot.
  6. The entire filesystem, that is, "/", is writable and is saved.
  7. Much simpler structure.
  8. Simplified boot params. Ex: "PMEDIA=usbflash" is all that is needed to boot from usb pen drive.
  9. "image.gz size problem" eliminated.
  10. Iso file can now grow from 60-65M to 65-70M.
  11. Improved security.
  12. Simplified, more reliable multisession CD/DVD management.
  13. One iso for normal and multisession.

1. Works with any size Flash drive

USB and CF (CF card may be plugged into USB or IDE interfaces by a suitable adapter) Flash drives are getting bigger. 1G is now common and dropping in price. The way that we have been using Puppy1 is to create a "pup100" file (the name of the personal storage file in Puppy1) on the USB drive, which has a ext2 filesystem. This file is copied into RAM at bootup, if there is enough RAM, thus avoiding writes to the Flash drive during a session. Then the files are copied back at shutdown.

One of the problems is that we find it difficult to make the pup100 file bigger than about 750M. Feedback on the Forum is that there are problems if we try to put a ext2 f.s. into anything bigger. Also, to avoid writes to Flash during a session, you would need to copy all of the pup100 files contents into RAM, meaning that you will need a massive amount of RAM.

Puppy2 has two solutions.

Solution 1:
One solution is not to use a "pup100" file at all. When you purchase a Flash drive, it is invariably pre-formatted with a FAT16 filesystem, and we normally leave that as-is. However, if we replace that with a ext2, ext3 or reiserfs filesystem (a Linux filesystem), then the entire partition can be used for personal data storage.
Meaning that you have the entire drive available for personal storage, nothing to resize later. Furthermore, writes to Flash during a session are eliminated, regardless of how big the partition is, and regardless how small your PC's RAM.

Oh, a little note. The ext2/3 personal storage file "pup100" is renamed as "pup_save.3fs". Previously, we used a naming scheme "pupxxx" where the "xxx" was an arbitrary number. To simplify the naming, puppy2 has just one name, "pup_save.3fs", where the ".3fs" means that the file has a ext3 filesystem.

The downside of reformatting the Flash drive with a Linux filesystem is it becomes more difficult to share files with Windows. That is, if you boot Windows, plug in the Flash drive, Windows won't recognise it. However, there is a native ext2/3 driver for NT/XP, and there are also some Windows applications that allow viewing and reading files from a ext2/3 filesystem.

Solution 2:
If you want to stay with the puppy1 approach and retain a personal storage file, now named pup_save.3fs, no problems. Now, you can make it any size, up to 750M (or bigger maybe), and writes to Flash during a session are eliminated, regardless of how big the pup_save.3fs file is, and regardless how small your PC's RAM.

2. Eliminates writes to Flash drive

Notice the bold text above. If you have, say, a 750M pup_save.3fs file and the PC has only 256M total RAM, maybe not even any swap partition or swap file, how on earth does puppy2 avoid writing to the Flash drive during a session?

This is one of the key architectural points of puppy2. At bootup, pup_save.3fs is mounted read-only from where it is on the Flash drive, and it's contents are not copied into RAM. Instead, a tmpfs filesystem in RAM holds all new and changed files. This is still actually very fast, as all the "working files" are in RAM.

Periodically and at end of session, those "working files" are written back to the pup_save.3fs file. Or to the partition if you chose the "solution 1" described above.

What this means in practice is you could have a PC with maybe as little as 64M RAM, booting off a 1G Flash drive, and all writes to Flash are eliminated during a session, and it runs fast.
Seems like magic, hey?

3. Works on PCs with very little RAM

Puppy2 will take advantage of more RAM, but if your PC is RAM-challenged, no problem.

The key point here is that the personal storage (I also refer to this as the persistent storage) partition or pup_save.3fs file is not loaded into RAM, only mounted read only, and only the "working files" are in RAM.

What you will have in a RAM-challenged PC is the kernel, initrd.gz (uncompressed) and the "working files" in RAM. Those "working files" are only new and changed files, so there will hardly be anything in RAM, meaning that we are going to find our Pup running on very minimal systems ....we have yet to find out how little RAM will work.

Note though, the set of "working files" will grow during a session. Any new or modified file will get added to it. However, there is a daemon (a background program) that will warn if RAM space is running low, then there is a simple fix -- flush the "working files" to the persistent storage (which currently requires a reboot).

Now, you may think, isn't that still going to be slow? Okay, having the "working files" in RAM is good, but when you want to start a big application, such as Mozilla, all of the Moz libraries will have to load off the Flash drive.
Yes, true, so the RAM-challenged PC will run a bit slower.

If your PC does have more RAM, say 128M or more, puppy2 will automatically see that and will load the "pup_xxx.sfs" file into RAM. This file is the same as the "usr_cram.sfs" and "image.gz" files in puppy1 combined. Puppy1 also loaded image.gz and if enough RAM also loaded usr_cram.fs into RAM.

So, if the PC has enough RAM then all the library files will be preloaded into RAM, so you will get the advantage of fast application startup for which Puppy is so famous.

4. Boots very fast

Booting from USB Flash is now much faster.

The puppy1 boot sequence is the file image.gz is loaded into a ramdisk, then the file usr_cram.fs, which has most of the Puppy files, is copied into the ramdisk (if there is enough RAM) and then mounted on /usr. The image.gz file is about 6.5M, and this takes awhile to load. If the USB interface is USB2 (rather than the older USB1) then the usr_cram.fs file should load quite fast, although being about 53M.

Puppy2 has a much smaller initial ramdisk file, named initrd.gz (instead of image.gz), only about 1.1M, and this accounts for a significantly faster boot time.

5. Defaults to running totally in ramdisk on first boot

Puppy1 users will know about a problem, a "catch 22" situation. The very first time that you boot the Puppy live-CD on a computer, a personal storage file named "pup001" is created automatically. This is a problem if it gets created on, say, /dev/hda1, the "C: drive", but you want to install Puppy to that partition. Our workaround was a menu with boot option to not create pup001 and just run in ramdisk.

I'm trying to get Puppy2 away from needing any menu (and subsequent pause) at bootup. Puppy2 tackles the problem the other way round, by always booting up in ramdisk-only the first time you boot on a PC, then at shutdown you are asked if you want to create a personal storage file. If you answer yes, it will get created and automatically used next time you boot Puppy.

Note, some people considered that Puppy should boot up only in ramdisk by default, for "ethical" reasons. Or maybe, "stealth" or "non-invasive" reasons. Whatever, your wish is now reality.

6. The entire filesystem is writable

Puppy1 has a rather complicated system whereby the home directory (/root) is writable, and /etc and /usr are made writable by actually saving the files inside /root.
The persistent storage file pup001 mounts on /root. The original files of /usr are in a compressed read only file called usr_cram.fs.

Puppy2 mounts the persistent storage file pup_save.sfs at the top level, that is, on "/". The read only compressed file with all the Puppy files, pup_xxx.sfs, mounts on "/" also.

Having only part of the directory hierarchy writable is a hassle when installing packages. Puppy2 solves this problem.

7. Mucher simpler structure

Point 6 above also means that the directory hierarchy is now "normal", like other Linux distros. Puppy1 had /etc as a symbolic link to /root/.etc, and writes to /usr saved in /root/.usr. Puppy2 does not have /root/.etc or /root/.usr.

In fact, simplification can be seen everywhere. Especially the bootup and shutdown scripts. Look inside /etc/rc.d and you will find less scripts and they are also smaller and simpler.

8. Simplified boot params

Puppy1 has various parameters that can be placed in the isolinux.cfg, syslinux.cfg, Grub or Lilo boot configuration files. These are kernel boot paramters, that get passed through to the bootup scripts. Some of them are PHOME, PROOTFS, PSLEEP, PFILE, PKEYS (see here for details).

Puppy2 has retained only two Puppy-specific boot parameters, PMEDIA and PKEYS, greatly simplifying the procedure of setting up a bootable Puppy installation.

Puppy users will already know about PKEYS, which specifies a keyboard layout, for example "PKEYS=us". PMEDIA provides a hint to Puppy as to what media you are booting off, for example "PMEDIA=usbflash", however even this is optional, and if there is no PMEDIA parameter Puppy will ransack the entire PC and find the Puppy files.

9. "image.gz size problem" eliminated

People who have remastered Puppy1 may have encountered this problem. The kernel is configured with a 12288K maximum ramdisk (increased to 13824K for Puppy 1.0.8), and image.gz once expanded has to fit into this. If bigger, the boot parameter "ramdisk_size=" has to be used.

Puppy2 uses a tiny initial ramdisk, initrd.gz, that is only 0.9M compressed.
Installing extra applications, such as DotPups that install into /root/my-applications, does not add to initrd.gz. The initial ramdisk file remains fixed in size, and everything under "/" goes into pup_save.3fs, the squashfs file that gets attached later in the boot process.

A technical detail here: it is only the initial ramdisk file that loads into the fixed-size-limit ramdisk. The boot sequence then creates a tmpfs ramdisk, which is variable in size, and will use as much RAM and swap space as available.

Puppy1's image.gz has another problem: most of its files are "dead weight". It has about 5M of kernel modules, of which only a few get loaded at bootup, and then they just sit there in RAM taking up space. On a RAM-challenged PC, 5M is a lot to throw away.

10. Iso file size can grow from 60-65M to 65-70M

The live-CD version 1.0.x is about 55M - 60M, and that size is very deliberate. One of the mission statements was that Puppy will run "fast" in a PC with only 128M RAM. On a PC with less RAM, Puppy will still run, but slower. To run "fast", the RAM must hold both image.gz and usr_cram.fs. Less RAM, and usr_cram.fs has to be left on the CD or wherever. Or there may be a swap file/partition, but that is going to be slow also.
image.gz is 6.5M compressed, about 12.5M uncompressed, and it is the latter that occupies the ramdisk in RAM.

Puppy2's initrd.gz is 0.9M compressed and 1.6M uncompressed, and it is the latter that occupies the ramdisk in RAM. Calculate the freed-up RAM: 12.5 - 1.6 gives about 11M.

Well, I won't torture you with laborious calculations, just get to the point: as much of what was previously in the uncompressed initial ramdisk has moved to the squashfs compressed pup_xxx.sfs file, it turns out that the Puppy2 live-CD iso file can now grow to 70M and still run "fast" in a 128M PC.

Yippee! 5M compressed is a lot of extra breathing space.

11. Imroved security

To be written

12. Simplified, more reliable multisession CD/DVD

Puppy 1.0.x has a complex multisession management mechanism. It also has reliability problems.

Puppy2 leverages off the unionfs layer management mechanism. Basically, the top layer (pup_rw), is a tmpfs filesystem in ramdisk, containing all the new and changed directories and files. At shutdown, the top layer is saved to a new folder on the CD/DVD -- that's it, session saved.
At bootup, the saved folders are read off the DVD in reverse order (latest first), writing the latest of each file to the second layer (pup_ro1). Thus, all the previous personal data is loaded into the second layer. The top layer once again will become the next saved folder. (these "layers" are explained below)

Note, unionfs has a mechanism for handling deleted directories and files, and Puppy's multisession management utilises this. A file named, say, /bin/testfile in the second layer, is deleted at the top layer by creation of /bin/.wh.testfile. At shutdown, this "deletion marker" file is saved to a the latest session folder on the DVD. At bootup, Puppy reads the folders in reverse order and is able to determine what directories and files in lower folders have been deleted and hence must not be loaded back to the second layer, pup_ro1.

13. One iso for normal and multisession

There is only the one iso file that is burnt to CD or DVD. At first bootup, Puppy runs totally in RAM. The difference comes at shutdown. Assuming that the user has burnt the iso file to CD/DVD as "open" or multisession, at shutdown the user can choose to either save the session in the normal way as a pup_save.3fs file in a partition of choice, or there is the option of saving the session back to the CD/DVD.

At the second bootup, Puppy will recognise that a session has been saved on the CD/DVD media and from then on will behave as a multisession CD/DVD.

Summary

In summary, puppy2 is faster at bootup and shutdown, works better on older hardware, more flexible and expandable in the future, more standardised, and considerably simpler.

Architecture Overview

This diagram (created in Sodipodi) is the big picture:

The way to understand the diagram is to view each of those layers as a complete filesystem, that is, a complete directory hierarchy from "/" down. These layers are laid one on top of the other, which is achieved by the unionfs filesystem. The way it works is very simple: say that the "off-pink" layer has a file /usr/lib/libgdkxft.so. This file will be visible at the top layer. If the "off-blue" layer has the same file, it will not be visible, as it is overlaid by the same file on a higher layer.
Anyway, here is a description of each layer:

ramdisk
This is the tmpfs filesystem running in RAM, with new and changed files.
pup_save.3fs
This is the persistent storage, where all your data, settings, email, installed packages, etc., get saved permanently. The ".3fs" means that the file contains a ext3 filesystem.
pup_xxx.sfs
This is Puppy. The builtin applications, window manager, scripts, everything. The ".sfs" means the file contains a squashfs compressed filesystem. The "xxx" is the Puppy version number without the dots, for example "200".
*_xxx.sfs
These are additional squashfs files. The "*" can be anything. For example, devx_xxx.sfs is the complete environment for compiling C/C++ applications.

What needs to be emphasised however is that all of this is "under the hood". While running Puppy, all you see is one filesystem, which is the top layer. Thus you see /usr/lib/libgdkxft.so and you don't care what layer it is actually on.

An exciting alternative to the squashfs extensions is to use an existing installed Linux distro as the bottom layer:

What the above diagram is intended to convey is that the bottom layer is a partition, not the "underdog.lnx" file itself. File underdog.lnx is just a text file, containing the name of a partition, for example "hda1".
At bootup Puppy will read underdog.lnx and will mount the partition as the bottom layer. If that partition happens to have a Linux distro installed in it, then the entire distro filesystem will "show through" on the top layer of Puppy's unionfs.

It will look like a normal Puppy, running JWM window manager or whatever, same desktop, but everything in the underlying distro is available to be executed. All the applications, compile environment, package manager, etc.

So, you have seen from the above two diagrams that the unionfs layers can vary. There are other variations, and Puppy has a "state variable" named PUPMODE that shows what state (configuration of layers) Puppy is currently using.
There is a file, /etc/rc.d/PUPSTATE that has the PUPMODE variable defined in it, for example, "12", and this is the current layer-configuration. Each PUPMODE value needs its own description...

PUPMODE 5

This is the configuration the very first time that Puppy is booted from live-CD or USB Flash drive. I'll concentrate the description for now on the live-CD, as that will most likely be your first exposure to Puppy.


The first time that you plug in the live-CD and bootup, there is no persistent storage, and the "union" consists of only two layers, the top "working files" and the pup_xxx.sfs squashfs filesystem that has all the Puppy files. These two layers appear overlaid at "/", however they can be viewed individually, at their respective mount points. The tmpfs ramdisk is mounted read-write on /initrd/pup_rw and pup_xxx.sfs is mounted read-only on /initrd/pup_ro2.

So, you are using Puppy but not touching the hard drive at all. You can run applications, configure, download, install packages, but it is all happening in the tmpfs ramdisk, so not getting saved. The amount of space you have in the ramdisk depends on how much RAM is in the PC (and whether there is a Linux swap partition).

The really interesting part is when you decide to end the session and shutdown the PC. The shutdown script, which is actually /etc/rc.d/rc.shutdown, will execute and will bring up a dialog window asking you where you want to save the session to. That is, whatever directories and files that have been created in the ramdisk can now be saved. At this point you have many choices...

Okay, if this is a live-CD that you have booted off, you would choose to save to a hard drive partition normally, but you could also choose a floppy drive, a USB drive, or even back to the CD/DVD (as long as it was burned "open" or "multisession"). It's very simple: a GUI dialog window offers a list of partitions and you choose the one you want.
Not only does the shutdown dialog offer a wide range of devices to save to, but it also gives a choice of saving to a file or to a partition. That is, you can have a file named pup_save.3fs with a ext3 filesystem inside it, nominally 512M in size (actually, the size is also selectable), or Puppy can take over an entire partition in which to save. The choice of file or partition depends on whether a partition is a Linux filesystem -- if only a FAT filesystem, then only a pup_save.3fs file can be created.

For now, let's go for the most common choice, saving to a pup_save.3fs file in a hard drive partition...

PUPMODE 12

The second time that you boot your Puppy live-CD, assuming that you chose to save the previous session to a pup_save.3fs file in a Linux, FAT or NTFS partition, Puppy will be in PUPMODE 12.


What has happened here is that at bootup Puppy found the pup_save.3fs file is on a fast hard drive partition, so decided to mount it directly on the top layer. Thus, there is no tmpfs ramdisk intermediary. File pup_save.3fs is directly read and written to.

This scenario is very good for RAM-challenged PCs. The initial ramdisk, that is, the file initrd.gz that gets loaded first when Puppy boots, is still there in ram, mounted at /initrd, however it only uses about 1.9M.

Okay, let's change to a different situation. Say that you installed Puppy to a USB Flash drive...

PUPMODE 13

If you install Puppy to a USB Flash drive, perhaps by using the Puppy Universal Installer program, or manually, you will have a bootable drive with the files vmlinuz (the Linux kernel), initrd.gz (the initial ramdisk), pup_xxx.sfs (squashfs filesystem with all the Puppy files) and syslinux.cfg (Syslinux config file). The situation is just like booting from a live-CD -- on first boot Puppy will be in PUPMODE 5, as no persistent storage has yet been created. On first shutdown, as described in the PUPMODE 5 section above, you will create a persistent storage -- either a pup_save.3fs file or an entire partition.

On the second boot, Puppy will discover the persistent storage, but in this case will realise that it is on a media for which writes need to be severely constrained. Therefore, Puppy will come up in PUPMODE 13.


In the above diagram, the top layer is a tmpfs ramdisk, into which all new and modified directories go. It is the working area, and has the potential limitation of the amount of RAM available. But of course, if the hard drive has a Linux swap partition that will be used to increase the effective size of the ramdisk.

In the case of the persistent storage on Flash memory, which is the second layer (off-orange color), Puppy will save everything from the top layer to the second layer every 30 minutes. From the "unionfs" point of view, the second layer is mounted read-only, it is only the top layer that is written to, however Puppy is able to "flush" the top layer down to the next layer at periodic intervals.

Actually, a technical detail here. "Flush" is not quite the right word. Ideally, I would have liked to do that, save everything down to the persistent storage layer, then the top layer would be empty thus giving you a completely empty ramfisk. However, when I tried to do that, unionfs crashed. So, the compromise is that the contents of the top layer is copied down to the next layer -- that is officially supported by unionfs.
it does mean however, that the ramdisk never gets really flushed, so if you download lots of stuff, or install a big package, you can fill it up. To get around this problem, Puppy has a background program (a daemon) that will warn if RAM space is getting low. Perchance you do run low on RAM, the cure is simple: reboot.

A note about the second layer. It is the persistent storage, but as mentioned in the PUPMODE 5 section above, at the first shutdown you are offered to create persistent storage in either a file (pup_save.3fs) or a partition. In the latter case, the session can be saved to an entire partition, but only if it is a Linux partition. In that case, what gets mounted on the second layer (off-orange layer) will be a partition, not a file.

In the case of booting off a USB Flash device, to use the entire partition for personal storage is real nice. The pup_save.3fs file is a limited size, nominally 512M or smaller if the partition does not have enough room. There are also reports on the Puppy forum that it can only be increased to 750M - 1G. ...well, I guess that's okay for most USB Flash drives!
If you did choose to save sessions directly to the USB partition, it has to be a Linux partition, that is, have a ext2, ext3 or reiserfs filesystem. Whereas, saving to a pup_save.3fs file, the USB partition can be anything -- it's factory setting is a FAT16 filesystem, which is fine.

PUPMODE 2

Many Puppy users will know that there are two ways to install Puppy to hard drive: what we called "option1" or "option2" installations.

Option1 is the less-invasive choice, which just copies the files vmlinuz, initrd.gz and pup_xxx.sfs to a chosen partition. The Puppy Universal Installer offers to create a floppy boot disk to boot Puppy installed in this way (but currently the boot floppy will only boot Puppy installed on a FAT partition). It is also possible to configure Grub or Lilo to boot Puppy. The advantage of this option is that it doesn't impact on the partiton in any way and whatever is already in the partition, like Windows or another Linux distro, is unaffected.
For an option1 h.d. installation, Puppy will bootup in PUPMODE 12.

Option2 is to install Puppy to an entire partition, which has to be a Linux partition (ext2, ext3, or reiserfs). This is recommended for developers or anyone wanting to compile applications, as it gives you lots of room.


This is the absolute simplest configuration. There is no ramdisk, the partition itself is mounted directly on the top "layer". In fact, if there are no ".sfs" extensions to load then Puppy will not use unionfs at all, so there are no layers.

PDEV1 is actually a variable in the file /etc/rc.d/PUPSTATE, that contains the name of a partition, for example "hda1", which is the partition to mount. The partition is mounted directly on "/".

Heh heh, but of course, how to know what PDEV1 is at bootup? Saying it is in /etc/rc.d/PUPSTATE in the partition itself is putting the egg before the chicken. That file is really just for scripts, so that they know what Puppy has booted off.

The way Puppy is booted in this case is from a floppy disk or USB Flash drive "boot disk", or via Grub or Lilo. The Puppy Universal Installer allows you to create any or all of these three: floppy, USB, Grub.

PUPMODE 77

This is for the multisession CD or DVD. In this case, the persistent storage is composed of folders on the CD/DVD. At each shutdown, the session is saved to a folder, so the CD/DVD accumulates folders. At bootup, these folders are read in reverse order and loaded into ramdisk.


At bootup, the folders are read from the CD/DVD and loaded into the second layer (off-orange). During a session, new and changed directories and files will be written to the top layer (off-green). At shutdown, the top layer is written as a new folder on the CD/DVD.

The rationale behind PUPMODE numbering

I was duscussing PUPMODEs recently with "Phil" on the Puppy Forum. He booted Puppy2 using GRUB, where initrd.gz (initial ramdisk file), vmlinuz (the kernel) and pup_xxx.sfs (all the Puppy files) are in a hard drive partition. He then saved the session direct to the boot partition.
Say the partition is /dev/hda2, then the situation looks like this:

 partition hda2
all saved sessions here, including file /etc/puppyversion.
/initrd.gz
/pup_xxx.sfs
/vmlinuz

I mention file /etc/puppyversion as that always gets saved at every session, and Puppy looks for it at bootup to determine that the partition has saved Puppy files in it.
Note, the files initrd.gz, pup_xxx.sfs and vmlinuz could be in a folder, say /boot, as long as GRUB is configured appropriately.

In Phil's case, Puppy will run in PUPMODE 7, but how is that determined? What is the logic behind it?

The PUPMODE is a number where each bit ( ... 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) is a flag:

BIT-SET DECIMAL-WEIGHT MEANING
0 1 using tmpfs top layer (/initrd/pup_rw) of union.
1 2 boot partition (PDEV1) has puppy sessions saved in it.
2 4 pup_xxx.sfs file exists on boot partition (PDEV1)
3 8 pup_save.3fs file exists (sessions saved to file).
4 16
5 32
6 64 multisession flag.
For example, the multisession CD/DVD runs with PUPMODE=77, which is 1+4+8+64. You see, it is quite logical!
In Phil's case, Puppy will run in PUPMODE=6 (2+4), as the saved sessions are on a fast media with unlimited write capability so no need for a tmpfs top layer in ram. Note, a tmpfs top layer is an intermediary to hold the working and new files & directories, and is only really needed where we have to restrict writes (ex: flash memory) or where the session is to be saved to very slow media or at specific times (burning a track to multisession CD), or we deliberately want to run totally in ram (as in the very first bootup of Puppy).
A full hard drive installation ("option 2") will run in PUPMODE 2.
The decision which mode to run in is made in the first boot script, /initrd/sbin/init. Though, in the case of a full h.d. install, the initrd.gz is not used at all and the first script that runs is /etc/rc.d/rc.sysinit -- in that case, Puppy assumes PUPMODE=2.

The initial ramdisk

At bootup, the first thing that happens is the Linux kernel, vmlinuz, is loaded into RAM, followed by the initial ramdisk, which is file initrd.gz. Note, the exception to this is PUPMODE 2, which is the full hard drive installation, which does not have a separate initrd.gz file.
When the initial ramdisk has loaded, the first script that executes is /sbin/init. Inserted below is a simplified content of this script. This is for explanation purposes only and the latest version of the script will be different.

#!/bin/ash
#(c) Copyright 2006 Barry Kauler, www.puppylinux.com
PATH="/bin:/sbin"
KERNVER="`uname -r`"

#Boot param PMEDIA= usbflash|usbhd|usbcd|ideflash|idehd|idecd|idezip|satahd|scsihd|scsicd
#unionfs layers: RW RO1 RO2 PUPMODE
#PUPSFS only: tmpfs pup_xxx.sfs 5
#PDEV1 has puppy: tmpfs PDEV1* 3
#PDEV1 has puppy, no tmpfs: PDEV1 2
#PDEV1, PUPSFS: tmpfs PDEV1* pup_xxx.sfs 7
#unionfs layers: RW RO1 RO2 PUPMODE
#found PUPSFS, PUPSAVE: tmpfs pup_save.3fs* pup_xxx.sfs 13
#PUPSAVE(multi), PUPSFS: tmpfs folders(tmpfs2) pup_xxx.sfs 13+64 = 77
#PUPSAVE, PUPSFS, no tmpfs: pup_save.3fs pup_xxx.sfs 12

fsfunc() #f.s. type param passed in
{
FSTYPE="$1"
[ "$FSTYPE" = "Ext2" ] && FSTYPE="ext2"
[ "$FSTYPE" = "ReiserFS" ] && FSTYPE="reiserfs"
[ "$FSTYPE" = "Ext3" ] && FSTYPE="ext3"
[ "$FSTYPE" = "FAT16" ] && FSTYPE="vfat"
[ "$FSTYPE" = "FAT32" ] && FSTYPE="vfat"
[ "$FSTYPE" = "NTFS" ] && FSTYPE="ntfs"
[ "$FSTYPE" = "ISO9660" ] && FSTYPE="iso9660"
}

guesspmediafunc() {
code to guess PMEDIA if not provided as boot param
}

ispupfunc() #fstype partition
{
code to find persistent storage
}

searchsavefunc() {
code to find pup_save.3fs persistent storage file.
}

findpupfunc() #list of drives (not partitions) to check passed in.
{
code to find boot partition and puppy files
}

loadfolders() { #source folder, dest. folder passed in.
code to load saved session folders off multisession CD/DVD
}

mount -t proc none /proc
PUPPYVERSION="`cat /PUPPYVERSION`"

####LOAD MODULES###########################################

code to load kernel modules here

###########################################################

PDEV1=""
case $PMEDIA in
idecd)
CDDRIVES="`test-eide | grep '\- cdrom \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
findpupfunc $CDDRIVES
#...return with PDEV1=hdc FSTYPE=iso9660 PUPSFS=pup_001.sfs (for example)
[ ! "$PUPSAVE" ] && searchsavefunc #search for PUPSAVE.
;;
idehd)
HDDRIVES="`test-eide | grep '\- disk \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
findpupfunc $HDDRIVES
#...return with PDEV1=hda3 FSTYPE=ext3 PUPSFS=pup_001.sfs (for example)
;;
usbflash)
#note, this could be a "superfloppy" with no mbr and no partitions.
USBDRIVES="`cat /proc/partitions | grep "sd[a-z]$" | tr -s " " | cut -f 5 -d " " | tr "\n" " "`"
findpupfunc $USBDRIVES
#...return with PDEV1=sda1 FSTYPE=ext2 PUPSFS=pup_001.sfs DEV1PUP="" (for example)
;;
ideflash)
#this looks like a normal ide drive. Note, may require boot param "ide=nodma"
HDDRIVES="`test-eide | grep '\- disk \-' | cut -f 1 -d " " | cut -f 3 -d "/" | tr "\n" " "`"
findpupfunc $HDDRIVES
#...return with PDEV1=hda1 FSTYPE=ext3 PUPSFS=pup_001.sfs (for example)
;;
idezip) code here
;;
satahd) code here
;;
satacd) code here
;;
usbhd) code here
;;
*) #ransack the entire PC looking for puppy!
#load usb-storage driver...
usbstoragefunc #loads usb-storage.o
ALLDRIVES="`probedisk | grep '^/dev/' | cut -f 1 -d '|' | cut -f 3 -d '/' | tr "\n" " "`"
findpupfunc $ALLDRIVES
#...return with PDEV1=hdc FSTYPE=iso9660 PUPSFS=pup_001.sfs (for example)
guesspmediafunc
[ ! "$PUPSAVE" ] && searchsavefunc #search for PUPSAVE.
;;
esac
#...exit with PDEV1= full device name.

#total ram, less any shared video...
PCRAMSIZE=`free | head -n 2 | tail -n 1 | tr -s " " | cut -f 3 -d " "`
SIZEFILLK=`expr $PCRAMSIZE \/ 2` #half of ram.
#for 128M PC, have to tweak this to get pup_xxx.sfs to load into ram...
if [ $SIZEFILLK -gt 50000 ];then
[ $SIZEFILLK -lt 70000 ] && SIZEFILLK=71680 #70M.
fi
PHYSICALFILLK="$SIZEFILLK"

#want to know if there is a swap partition available...
SWAPINFO="`fdisk -l | grep "Linux swap" | head -n 1`"
if [ ! "$SWAPINFO" = "" ];then
#we can make the ramdisk real big now...
SWAPPART="`echo "$SWAPINFO" | cut -f 1 -d " "`"
SWAPSIZE=`fdisk -s $SWAPPART`
SWAPSIZ4=`expr $SWAPSIZE \/ 4`
SWAPSIZ2=`expr $SWAPSIZE \/ 2`
SWAPSIZE=`expr $SWAPSIZ2 + $SWAPSIZ4` #3/4 of original
SIZEFILLK=`expr $SIZEFILLK + $SWAPSIZE`
echo "Loading swap partition $SWAPPART..."
swapon $SWAPPART
fi

#mount the main puppy f.s....
PUPMODE=1 #using tmpfs.
[ "$DEV1PUP" = "yes" ] && PUPMODE=`expr $PUPMODE + 2` #PDEV1 has puppy installed.
[ "$PUPSFS" ] && PUPMODE=`expr $PUPMODE + 4` #pup_xxx.sfs exists (on PDEV1).
[ "$PUPSAVE" ] && PUPMODE=`expr $PUPMODE + 8` #pup_save.3fs exists.
if [ $PUPMODE -eq 13 ];then
#multisession cd, run in PUPMODE 13+64=77
[ ! "`echo -n "$PUPSAVE" | grep '/20[0-9][0-9]'`" = "" ] && PUPMODE=77
fi

UMNT1='/pup_rw=rw'
DEV1MNT="/pup_ro1"
case $PUPMODE in
3) #PDEV1 has puppy, and using tmpfs (maybe).
echo -n "Mounting /dev/${PDEV1}..."
if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idehd|satahd|scsihd"`" = "" ];then
#do not use unionfs at all, just mount on /pup_new instead of /pup_rw...
mount -o rw -t $FSTYPE /dev/$PDEV1 /pup_new
PUPMODE=2
else
mount -o ro -t $FSTYPE /dev/$PDEV1 /pup_ro1
mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
UMNT1='/pup_rw=rw:/pup_ro1=ro'
fi
;;
5) #tmpfs, no persistent storage layer, pup_xxx.sfs
#this will be the case for first boot. ***FIRST BOOT SITUATION***
echo -n "Mounting ${PDEV1}..."
mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1
echo -n "Mounting tmpfs..."
mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
UMNT1='/pup_rw=rw:/pup_ro2=ro'
#if enough room in ramdisk, copy it...
if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
echo -n "Copying $PUPSFS to ramdisk..."
cp -f /mnt/dev_ro1/$PUPSFS /pup_rw/
sync
losetup /dev/loop0 /pup_rw/$PUPSFS
echo "Unmounting ${PDEV1}..."
umount /dev/$PDEV1
else
echo -n "Mounting $PUPSFS directly off /dev/$PDEV1..."
losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
fi
mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
;;
7) #tmpfs, PDEV1 is persistent storage, pup_xxx.sfs.
echo -n "Mounting /dev/${PDEV1}..."
mount -o rw -t $FSTYPE /dev/$PDEV1 /pup_ro1
echo -n "Mounting tmpfs..."
mount tmpfs /pup_rw -t tmpfs -o size=${SIZEFILLK}k
UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
#if enough room in ramdisk, copy it...
if [ $SIZEFILLK -gt `du /pup_ro1/$PUPSFS | cut -f 1` ];then
echo "Copying $PUPSFS to ramdisk..."
cp -f /pup_ro1/$PUPSFS /pup_rw/
sync
losetup /dev/loop0 /pup_rw/$PUPSFS
else
echo "Mounting $PUPSFS directly off /dev/$PDEV1..."
losetup /dev/loop0 /pup_ro1/$PUPSFS
fi
echo -n "Mounting ${PUPSFS}..."
mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
;;
13) #tmpfs, pup_save.3fs is persistent storage, pup_xxx.sfs.
SAVEFS="`echo -n "$PUPSAVE" | cut -f 1 -d ','`" #f.s. and partition where pup_save.3fs is located.
SAVEPART="`echo -n "$PUPSAVE" | cut -f 2 -d ','`" # "
SAVEFILE="`echo -n "$PUPSAVE" | cut -f 3 -d ','`"
echo -n "Mounting ${PDEV1}..."
mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1
TMPFSMNT="/pup_rw" #tmpfs layer on top.
EFSMNT="/pup_ro1" #pup_save.3fs next layer.
UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
#maybe drop down to PUPMODE=12...
if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idecd|usbcd|scsicd"`" = "" ];then
#booting off a cd. if pup_save.3fs exists in a fast media, can load direct...
[ ! "`echo -n "$SAVEPART" | grep '^hd'`" = "" ] && SAVEDIRECT="yes"
#...TODO not detecting sata and scsi fast drives.
fi
if [ ! "`echo -n "$PMEDIA" | grep --extended-regexp "idehd|satahd|scsihd"`" = "" ];then
#assumption that pup_save.3fs is also on the fast media...
SAVEDIRECT="yes"
fi
if [ "$SAVEDIRECT" = "yes" ];then
#pup_save.sfs is on a fast media, allows unlimited writes, so let us not use tmpfs layer...
PUPMODE=12 #rc.shutdown will know nothing to save.
#still need a tmpfs, to load pup_xxx.sfs...
TMPFSMNT="/mnt/tmpfs" #tmpfs created elsewhere, not a layer.
EFSMNT="/pup_rw" #pup_save.3fs top layer.
UMNT1='/pup_rw=rw:/pup_ro2=ro'
fi
echo -n "Mounting tmpfs..."
mount tmpfs $TMPFSMNT -t tmpfs -o size=${SIZEFILLK}k
#if enough room in ramdisk, copy it...
if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
echo -n "Copying $PUPSFS to ramdisk..."
cp -f /mnt/dev_ro1/$PUPSFS $TMPFSMNT/
sync
losetup /dev/loop0 $TMPFSMNT/$PUPSFS
umount /dev/$PDEV1
else
echo -n "Mounting $PUPSFS directly off /dev/$PDEV1..."
losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
fi
mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2
#now do PUPSAVE...
SMNTPT="`mount | grep "/dev/$SAVEPART" | tr -s " " | cut -f 3 -d " "`"
if [ "$SMNTPT" = "" ];then
SMNTPT="/mnt/dev_save"
mount -t $SAVEFS -o noatime,rw /dev/$SAVEPART /mnt/dev_save
fi
#about to mount pup_save.3fs, but before that check if need to resize it...
if [ -f $SMNTPT/pupsaveresize.txt ];then #created by /usr/sbin/resizepfile.sh
KILOBIG=`cat $SMNTPT/pupsaveresize.txt`
rm -f $SMNTPT/pupsaveresize.txt
echo "Increasing $SAVEFILE by $KILOBIG Kbytes, please wait..."
dd if=/dev/zero bs=1k count=$KILOBIG | tee -a $SMNTPT$SAVEFILE > /dev/null
sync
e2fsck -y -f $SMNTPT$SAVEFILE
resize2fs -pf $SMNTPT$SAVEFILE #no size, will fill all of file.
sync
sleep 6 #so we can see result
fi
losetup /dev/loop1 $SMNTPT$SAVEFILE
echo -n "Mounting ${SAVEFILE}..."
FILEFS="ext3"
[ ! "`echo -n "$SAVEFILE" | grep "2fs"`" = "" ] && FILEFS="ext2"
mount -t $FILEFS -o noatime,rw /dev/loop1 $EFSMNT
;;
77) #multisession cd/dvd
SAVEFS="`echo -n "$PUPSAVE" | cut -f 1 -d ','`" #f.s. and partition where pup_save.3fs is located.
SAVEPART="`echo -n "$PUPSAVE" | cut -f 2 -d ','`" # "
SAVEFILE="`echo -n "$PUPSAVE" | cut -f 3 -d ','`"
echo -n "Mounting ${PDEV1}..."
mount -o rw,noatime -t $FSTYPE /dev/$PDEV1 /mnt/dev_ro1;check_status $?
TMPFSMNT="/pup_rw" #tmpfs layer on top.
EFSMNT="/pup_ro1" #folders from cd next layer.
UMNT1='/pup_rw=rw:/pup_ro1=ro:/pup_ro2=ro'
#now do PUPSAVE...
SMNTPT="`mount | grep "/dev/$SAVEPART" | tr -s " " | cut -f 3 -d " "`"
if [ "$SMNTPT" = "" ];then
SMNTPT="/mnt/dev_save"
mount -t $SAVEFS -o noatime,rw /dev/$SAVEPART /mnt/dev_save
fi
#personal data is saved in dated folders.
#create a tmpfs on /pup_ro1 and load folders to it... in physical mem only...
ALMOSTFILLK=`expr $PHYSICALFILLK \/ 4`
ALMOSTFILLK=`expr $PHYSICALFILLK - $ALMOSTFILLK`
mount tmpfs /pup_ro1 -t tmpfs -o size=${ALMOSTFILLK}k;check_status $?
loadfolders $SMNTPT /pup_ro1
ALMOSTFILLK=`free | grep 'Mem:' | tr -s " " | cut -f 5 -d " "`
ALMOSTFILLK=`expr $ALMOSTFILLK \/ 2`
SIZEFILLK=`expr $ALMOSTFILLK + $SWAPSIZE`
echo -n "Mounting tmpfs..."
mount tmpfs $TMPFSMNT -t tmpfs -o size=${SIZEFILLK}k;check_status $?
#if enough room in ramdisk, copy it...
if [ $SIZEFILLK -gt `du /mnt/dev_ro1/$PUPSFS | cut -f 1` ];then
echo -n "Copying $PUPSFS to ramdisk..."
cp -f /mnt/dev_ro1/$PUPSFS $TMPFSMNT/
sync
losetup /dev/loop0 $TMPFSMNT/$PUPSFS
umount /dev/$PDEV1
else
echo -n "Mounting $PUPSFS directly off /dev/$PDEV1..."
losetup /dev/loop0 /mnt/dev_ro1/$PUPSFS
fi
mount -r -t squashfs -o noatime /dev/loop0 /pup_ro2;check_status $?
sync
umount /dev/$SAVEPART 2>/dev/null
;;
*) #incomplete combination.
#nothing to save to and/or no puppy found.
echo "ERROR, cannot find Puppy on $PMEDIA boot media."
echo "PUPMODE=$PUPMODE PDEV1=$PDEV1"
sleep 10
exit
;;
esac

UMNTRO=""
#Look for *_$PUPPYVERSION.sfs (ex: dev_200.sfs)...
[ $PUPMODE -eq 7 ] && EXTRASFS="/pup_ro1"
[ $PUPMODE -eq 13 ] && EXTRASFS="`mount | grep "/dev/$SAVEPART" 2>/dev/null | tr -s " " | cut -f 3 -d " "`"
[ $PUPMODE -eq 77 ] && EXTRASFS="`mount | grep "/dev/$SAVEPART" 2>/dev/null | tr -s " " | cut -f 3 -d " "`"
if [ "$EXTRASFS" ];then
cd $EXTRASFS
if [ -f underdog.lnx ];then
#this is an idea to overlay Puppy on top of an existing Linux distro.
UNDERDOGPART="`cat underdog.lnx`"
UDMNTPT="`mount | grep "/dev/$UNDERDOGPART" | tr -s " " | cut -f 3 -d " "`"
if [ "$UDMNTPT" = "" ];then
UNDERDOGFS="`disktype /dev/$UNDERDOGPART | grep 'file system' | head -n 1 | cut -f 1 -d " "`"
[ "$UNDERDOGFS" = "Ext2" ] && UNDERDOGFS="ext2"
[ "$UNDERDOGFS" = "Ext3" ] && UNDERDOGFS="ext3"
[ "$UNDERDOGFS" = "Reiserfs" ] && UNDERDOGFS="reiserfs"
if [ ! "`echo -n "$UNDERDOGFS" | grep --extended-regexp 'ext2|ext3|reiserfs'`" = "" ];then
mount -r -t $UNDERDOGFS /dev/$UNDERDOGPART /pup_ro3
if [ $? -eq 0 ];then
UMNTRO="${UMNTRO}:/pup_ro3=ro"
#fixes to prevent library clashes...
MNTFIX='/pup_rw'
[ ! "`echo -n "$UMNTRO" | grep 'pup_ro1'`" = "" ] && MNTFIX='/pup_ro1'
[ ! -f $MNTFIX/lib/.wh.i686 ] && touch $MNTFIX/lib/.wh.i686 #hides /lib/i686
#puppy needs dir name /usr/lib/qt at bootup (see rc.profile)...
REALQTDIR="`find /pup_ro3/usr/lib -maxdepth 1 -type d -name qt* | tail -n 1 | sed -e 's/\/pup_ro3\/usr\/lib\///g'`"
if [ "$REALQTDIR" ];then
if [ ! -e $MNTFIX/usr/lib/qt ];then
[ "`find /pup_ro3/usr/lib -maxdepth 1 -xtype d -name qt`" = "" ] && ln -s $REALQTDIR $MNTFIX/usr/lib/qt
fi
fi
fi
fi
else
UMNTRO="${UMNTRO}:${UDMNTPT}=ro"
fi

else
CNTLOOP=3
for ONESFS in `ls -1 *_${PUPPYVERSION}.sfs | grep -v "^pup_" | tr "\n" " "`
do
losetup-FULL /dev/loop${CNTLOOP} $EXTRASFS/$ONESFS
mount -r -t squashfs -o noatime /dev/loop${CNTLOOP} /pup_ro${CNTLOOP}
[ $? -eq 0 ] && UMNTRO="${UMNTRO}:/pup_ro${CNTLOOP}=ro"
CNTLOOP=`expr $CNTLOOP + 1`
done
fi
cd /
fi

#now do the unionfs thing...
mkdir -p /pup_rw/etc/rc.d
echo -n "$PUPMODE" > /pup_rw/etc/rc.d/PUPMODE #to be read by rc.shutdown.
echo -n "$PDEV1" > /pup_rw/etc/rc.d/PDEV1 # "
echo -n "$FSTYPE" > /pup_rw/etc/rc.d/DEV1FS # "
echo -n "$PUPSFS" > /pup_rw/etc/rc.d/PUPSFS # "
echo -n "$PUPSAVE" > /pup_rw/etc/rc.d/PUPSAVE # "
echo -n "$PMEDIA" > /pup_rw/etc/rc.d/PMEDIA # "
echo -n "$PUPPYVERSION" > /pup_rw/etc/puppyversion #used to check saved session at bootup (ispupfunc)
mkdir /pup_rw/initrd
sync
echo -n "Creating unionfs..."
mount -t unionfs -o dirs=${UMNT1}${UMNTRO} none /pup_new

#want to launch dnotify from init script as it is pristine...
#/sbin/launchpupsafe &

cd /pup_new
sync
umount /proc
#echo "pivot_root now changing from initial ramdisk..."
pivot_root . initrd
exec chroot . sh -c "exec /bin/busybox init" <dev/console >dev/console 2>&1

Okay, that looks like a lot of stuff happening! Actually though, it is all quite simple.
In a nutshell, the script searches for the boot partition and Puppy persistent storage file or partition, using the PMEDIA boot parameter as a guide. When found, the script then determines the appropriate PUPMODE, then creates the unionfs layers accordingly.

The end of the script executes pivot_root, which switches to folder /pup_new as the new "/" (top of the filesystem). Directory /pup_new is where all the unionfs layers have been merged.

The initial ramdisk is left in memory, and after pivot_root, will be in directory /initrd. That is, everything under "/" before the pivot_root will be in /initrd after pivot_root. It is inside /initrd that you will still be able to access the individual layers of the union -- but this capability is of interest for developers only, not normal users.

After pivot_root, the next script to execute is /etc/rc.d/rc.sysinit. Note that unlike puppy1, everything in /etc/rc.d is editable, as everything under "/" gets saved. The only script not directly editable is /initrd/sbin/init, the initial ramdisk startup script.


How Puppy works, take 1

The level of interest in Puppy is steadily rising, and many people ask questions on the Puppy Discussion Forum about how Puppy works "under the hood".

So, I decided to put this page together to explain a few concepts about the structure of Puppy, version 0.9.8+. One thing to understand is that Puppy is unique. I built Puppy from scratch, file by file, so the architecture is not based on any other distro. Puppy has evolved from my first experiments with the "Boot disk HOWTO" early in 2003, to the amazing phenomenon we have today.

I'm using Amaya to write this web page, and I used Dia to create the images you see below. I could have written this page with Abiword, Composer or Bluefish, and I could have created the image in Xpaint, Figurine, mtPaint or Sodipodi -- this amazing choice of applications is all in the tiny approx 50-60M Puppy distro. However, if not satisfied with the in-built choices, Puppy has the powerful PupGet and DotPup package managers offering a growing choice of packages that you can download and install with just a few clicks of the mouse. You can even rebuild the live-CD with the precise selection of packages that you want.

In fact, with the new CD-remaster program in Puppy 1.0.5, creating a custom live-CD or USB stick has become extremely easy ...anyway, that's another story!

The Puppy live-CD

The main purpose of this page is to explain how Puppy differs from other distros, and why, and therefore what you can and cannot do, that perhaps you can or cannot do in other distros. Take a look at this diagram:

livecd

The above diagram shows the layout of Puppy when booted from a live-CD.

The ramdisk

When the live-CD boots, vmlinuz, the Linux kernel, loads into RAM, followed by image.gz, which loads into a "ramdisk" (a filesystem that is totally in RAM). The ".gz" means that the file is compressed, so it is uncompressed into the ramdisk and then becomes the basic Puppy filesystem, that is, the "/" or top directory and all the subdirectories /bin, /sbin, /lib, /dev, /tmp, etc.

A fundamental objective of Puppy is that everything should load into ramdisk, thus freeing up the CD drive so that you can use the CD drive for any other purpose. Also, if everything is in RAM, application startup and running speed is stunning.

pup001 file

There is a problem if everything runs in the RAM though -- when the power goes off, RAM contents are lost. What is needed is some way to retain your settings, such as configuration changes, email, browser history, and so on. What Puppy does is during bootup look for a suitable hard drive partition, and if one is found, then create a file on it called "pup001". Actually, the name of the file could be anything, but I arbitrarily chose "pupxxx" where the "xxx" is three numeric digits. The three numeric digits have no particular meaning either, except we are currently using pup001 for the live-CD and pup100 for USB installations of Puppy.

pup001 is a single file, but internally is a complete ext2 filesystem. After creating this file, Puppy then mounts it, by means of what is called a "loopback device", onto /root directory. This directory /root is your home directory -- all your personal stuff goes in there. Thus, when you shutdown, it will be there next time you start Puppy.

I do need to explain the bootup steps properly though -- Puppy first has to mount the hard drive partition onto /mnt/home. This must be done before Puppy can get access to pup001 and mount it on /root. That's why you can see two mount points in the diagram.

There has been a great deal of interest on the Forum in having a live-CD that does not need the hard drive at all, yet is still able to save personal data. I therefore developed the "multisession live-CD", introduced with version 1.0.0, that saves the contents of /root back to the CD, thus not requiring a pup001 file at all. This new concept is described further down -- keep reading! Note that the multisession technique does not replace the use of pup001 -- they are both available.

usr_cram.fs file

Note, if reading this page for the first time, these details on usr_cram.fs may get you mentally bogged down, so skim read onto the next heading -- you can always come back later if you want to understand more precisely how the usr_cram.fs file gets placed in memory.

Most of the files in Puppy, in any Linux distro for that matter, are in /usr, and furthermore there is normally no need to write to anything inside /usr (that is, no need to edit, create or delete files). Therefore, the contents of /usr could be compressed, and that is what Puppy does. The entire contents of /usr is compressed as a single file, named usr_cram.fs. This file is not uncompressed, it is mounted as-is on /usr, by means of what is called a "loopback device" -- the actual technique is not the main issue here, but you need to be aware that all of /usr is really only a single file, containing a compressed and read-only filesystem.

You can see that I have shown part of the diagram in an orange color. This is to illustrate the different places where the file usr_cram.fs may be located. Those four numbers, 1, 2, 3, and 4, are the search order. That is, at bootup, Puppy will first look to see if usr_cram.fs is at "/" in the ramdisk (which meant that it was inside image.gz originally). Secondly, Puppy will look in /root (that is, inside pup001), thirdly in /mnt/home (in the hard drive partition), finally Puppy will look on the CD.

Even if Puppy falls through to option 4, Puppy will do his best not to leave usr_cram.fs on the CD. We don't want to leave usr_cram.fs on the CD. We really want to copy it to the ramdisk, or failing that into pup001 in the hard drive. If we left it on the CD and just mounted it on /usr, Puppy will run slowly and the CD drive will not be available for other purposes. So, Puppy looks to see if there is enough RAM, and if so, copies usr_cram.fs to "/" in the ramdisk and then mounts it on /usr.

Update: Puppy 1.0.5 does the above slightly differently. This is just a small technical detail, a deviation from before. usr_cram.fs now gets mounted on directory /.usr_cram and then joined with /usr by UNIONFS. If that makes no sense, don't worry as the end result is the same.

How much RAM is "enough"? Puppy is happiest on a PC with at least 128M RAM. 128M is a good size -- it is enough for image.gz (uncompressed) and usr_cram.fs to both be in the ramdisk. For the normal Puppy that is -- we have some fatter custom Puppies, notably Chubby Puppy (it has OpenOffice) that is 90M and that needs a 256M PC to run in RAM.

But, what if your PC has only 64M, 48M, or even only 32M RAM? Well, usr_cram.fs is not going to fit in the ramdisk. Just ball-park figures, image.gz uncompressed is about 10M, usr_cram.fs is about 50M.

Puppy does have a card up his sleeve, so to speak -- if the PC has a Linux swap partition Puppy will automatically use that, which is used to increase the effective size of the ramdisk. If you have ever installed another Linux distro on the PC, chances are it created a swap partition, so it is already sitting there ready for Puppy to use. If your PC has 128M of RAM, Puppy 0.9.8 will allocate 62M of that to ramdisk (version 1.0.2+ allocates 70M), however if the PC also has a 250M (for example) swap partition, then the effective size of the ramdisk becomes 62+250 = 312M!

However, I made a decision with the startup logic of Puppy, only to copy usr_cram.fs into ramdisk if there is at least 62M of physical RAM allocated to the ramdisk. So, for PCs with less physical RAM than 128M, usr_cram.fs will never be copied into ramdisk, even if there is a swap partition.

Another thing to think about is the search order, those four numbers 1, 2, 3, and 4. When you have booted up Puppy from the CD, if you copy usr_cram.fs from the CD or /, to /root (ie., inside pup001), that will be the second place Puppy looks next time he boots. Or, copy it onto "/" on the hard drive -- that's the 3rd place Puppy will look. So even on a PC with only 32M RAM, you can bootup from a live-CD and have the CD drive freed up for other purposes.

However, PCs with less than 128M really do need a swap partition for Puppy to work properly. Even though Puppy can boot up from live-CD on a PC with only 32M RAM, some of the applications are memory hogs -- Mozilla for example. Mozilla is not viable on a PC with less than 128M RAM. However, as mentioned above, a swap partition increases the effective size of the ramdisk, so you can get Mozilla to work on PCs with very little RAM, albiet slowly. It is also a good idea to add a swap file, if you have some spare space on the hard drive.

Think about that search order a bit more... #3 is interesting. Puppy looks on the hard drive for usr_cram.fs, but how would the file get there? If you put it there, it would, but also if you run the install-to-hard-drive-option-1 script.
...in that case, there's a "gotcha" as when you bootup a new version of Puppy on the CD, Puppy will find the old usr_cram.fs. Argh! ...not so good. Or, an old usr_cram.fs if you put it in /root, for that matter.
To get around that problem Puppy has a technique for checking that usr_cram.fs is the correct version. Puppy checks this, and will ignore any usr_cram.fs files it finds that have the wrong version number. The technique used is when usr_cram.fs is created, its size, in bytes, is recorded in /etc/sizeusrcram, which can then be used to check against usr_cram.fs at boot-time.

The explanation above states that /usr is read-only, as it has a compressed read-only usr_cram.fs mounted on it. However, Puppy version 1.0.1 has changed all that. /usr is now writable. Once again, keep reading!

Package installation

A most important point about this architecture is that /usr is read-only. Therefore, if you download a .tar.gz "tarball" or RPM package and you want to install it into Puppy, you can't! At least, most packages want to install part of themselves into /usr.

However, Puppy version 1.0.1 has revolutionised package management by using UNIONFS to make /usr read-write. Any files written to /usr are actually stored in /root/.usr, however as far as the user is concerned, they are in /usr. This picture illustrates:

"/root/.usr" is just a directory inside pup001, but it is mounted on /usr, and any writes to /usr actually get stored into /root/.usr. So, you see, as /root/.usr is actually inside pup001 then it will be saved at end of session and reloaded next time Puppy is booted. (Heh, heh, I'm drawing lines all over the place, this mounted on that and so on -- but final usage is very simple!)

An outcome of having /usr read-write is that Puppy can have a package management system to install and remove packages. I looked around at what is available, but as usual decided to reinvent the wheel, and the result is PupGet, a package manager with installation, removal and dependency checking.

PupGet offers the packages of the Puppy Unleashed package suite. All of these packages are available for download -- it is simply a matter of going to the Puppy menu, "Setup -> PupGet package manager", and follow the simple steps.

The Unleashed packages are all on the Internet, and PupGet can download them individually to install them, however Linux developers can create their own live-CDs from the complete Unleashed package suite. The live-CD that you are using now was created from Puppy Unleashed.

Puppy Unleashed enables you to build your own custom Puppy from a choice of hundreds of packages (applications). For further information, see the Puppy Unleashed page.

Puppy 1.0.5 adds a new dimension to creating a custom CD, with a program codenamed PCCC, or Puppy Custom CD Creator. This program enables you to use the current live-CD as the starting point, then add or remove packages as you wish. PCCC is tied in with the PupGet package manager, and can also move DotPup packages (Puppy has two packaging systems) to the live-CD. PCCC is much easier to use than it is to explain!

Booting from USB

USB Flash memory drives have become very popular, and the entry-level capacity is now 128M, which is the equivalent of about 80 floppy disks. So, the floppy disk is becoming history, and most PCs these days can be configured to boot from USB, meeting the need for an emergency "boot disk" (or you can boot from a CD of course).

I do need to make a comment on that though. A friend of mine purchased a brand new Compaq laptop, rather expensive model (Presario R3000), in October 2004, and I was amazed that the BIOS setup did not have an option to boot from USB. It did have a "boot from CD" setting, but not USB. That's incredible, and a warning, if you are shopping for a new PC.

Another warning. Most USB Flash drives are generic, that is, they have an interface that conforms to the standard USB mass-storage specification. However there are some that do not conform, thus you probably cannot boot from them, nor use them with Linux. There is an easy way to pick a "conforming" Flash drive -- if it states on the packet that it will work with Windows XP without needing a special driver installed, then it is probably okay. In fact, I purchased one recently that didn't even have a CD and it stated specifically on the packet that it works with XP and Linux without any special driver.

Flash technology

A 128M Flash drive matches perfectly with Puppy. Consider, Puppy is only about 50-60M, so will fit on a 128M USB drive leaving over 70M free for personal data. Yes, a complete portable operating system and personal data, all on the one tiny device!

However, there is a downside to Flash technology, and that is it is not designed for unlimited writes. That is, you can save onto it just so many times, then it will collapse. I did read somewhere that someone installed Windows 95 on a USB Flash drive and it failed after about 6 months of daily use. The number of writes each memory location can sustain depends on what documentation you read -- also, be careful about figures from the vendor, as they may quote something like 1,000,000 write cycles, but that may not mean to each memory cell. I have seen figures as low as 50,000 writes.

Anyway, the good news is that Puppy is especially designed to have no writes to the Flash drive during a session, enormously extending its life span. Take a look at this diagram:

liveusb image

When Puppy boots from USB, the steps are much the same as for the live-CD. The kernel vmlinuz is loaded into RAM, image.gz is uncompressed and loaded into a ramdisk.

usr_cram.fs

Puppy then searches for usr_cram.fs, first looking in "/" in the ramdisk, then in "/" in the USB partition. The first case would only be if Puppy had been created with usr_cram.fs inside image.gz, which is not the normal situation. So it falls back to number 2. Puppy will find usr_cram.fs in the USB partition and will mount it on /usr.

But, Puppy will attempt an optimisation. If there is enough RAM, Puppy will copy usr_cram.fs into the ramdisk and then mount it on /usr. This will slow down bootup slightly, but will improve running speed. However, even if Puppy does leave usr_cram.fs on the USB drive and mounts it from there onto /usr, that is not a problem as /usr is read-only. There will be no writes to /usr, so the lifetime of the Flash drive is not compromised (Puppy version 1.0.1 has UNIONFS which makes /usr read-write, but all writes actually go to /root/.usr, which is in the ramdisk, as described below).

pup100

The very first time that Puppy boots from USB, the file pup100 does not yet exist -- Puppy has to create it. Puppy creates it, and mounts it directly on /root, by means of a loopback device.

So far, the architecture is basically the same as for the live-CD.

The lifetime of the Flash device is compromised by writes to /root, that is, to pup100. This is your home directory, and applications store all kinds of stuff in it. A lot of writing will be happening to /root. The Linux kernel does cache the writes, but even so, the cache will be flushed frequently.

A significant difference occurs the second time that Puppy is booted from USB. If there is enough RAM (remembering that the ramdisk can also use a swap partition if it exists, so the available space is effectively (half) the size of RAM plus size of the swap partition), then Puppy will mount pup100 onto /mnt/pupxxx directory and will then copy all the files from /mnt/pupxxx to /root.

Thus, a complete copy of pup100 is in /root, and no writing will occur to pup100 during a session. At shutdown, all the files in /root are copied back to /mnt/pupxxx, thus updating pup100. We could, in theory, backup to pup100 at regular intervals, rather than only at shutdown, but that is not implemented in the current version of Puppy -- quite frankly, I have very rarely had Puppy crash, and the only situation that would cause backup not to occur would be a power failure.

Puppy is highly intelligent, and the optimisations described above are only implemented if there is enough RAM (+swap). If there is not enough RAM, then obviously pup100 cannot be copied into /root in the ramdisk.

Just to give an idea of what is "enough" RAM, a PC with 256M RAM matches well with a 128M Flash drive. The pup100 file will be about 60M, and there's enough RAM for everything to load into ramdisk. On the otherhand, a 256M Flash drive would have a 180M pup100 file which would be too big. But, if the PC has a swap partition, Puppy will automatically use this to increase the effective maximum size of the ramdisk.

Whatever the situation on your PC, Puppy will work out the best configuration, totally automatically.

Updating and archiving

As you can see, Puppy consists of only 4 files. (actually, it is possible to have usr_cram.fs inside image.gz, reducing Puppy down to just 3 files -- the web download sites may have that configuration available. It is convenient for booting off a network to have just the two files vmlinuz and image.gz to worry about)

To update to the latest version of Puppy, you just need to get the latest vmlinuz, image.gz and usr_cram.fs. Updating is so very simple, but with v0.9.8 I made it even simpler by building an update option into the installation scripts. That is, boot up the latest live-CD and run the install-to-USB, install-to-Zip or install-to-hard-drive script and choose "update" rather than "new installation".

...but, it's so simple, you could do it manually if you wish.

Puppy does have a couple of archive programs, including one that I wrote that backs up only the changed files from /root each time. However, all your personal data, really, a snapshot of the entire the state of the system, is in the pup001 or pup100 file, so you can save a complete system snapshot just by making a copy of the pup001/100 file. Then compress it, like this (assuming the copy is named pup100-12dec04):

# gzip pup100-12dec04

and you will have pup100-12dec04.gz. Or use "zip" or "bzip2", Puppy has them all! Archive that anywhere you want.

You can mount an archived pup100 file at any time, and view its contents:

# losetup-FULL /dev/loop2 pup100-12dec04
# mount -t ext2 /dev/loop2 /mnt/data

You can also view the contents of pup100-12dec04 from Windows, by using the Explore2fs program. There is also a free native ext2 filesystem driver for Windows NT/XP.

Multisession live-CD

This is a most exciting new development. When I introduced this with v1.0.0, I thought it was a world first, but I discovered that the Morphix Linux people had been experimenting with a similar concept (but nowhere near as developed).

With v1.0.2, there is only one ISO file, for example puppy-1.0.2-mozilla.iso, and it becomes multisession if it is burnt that way when writing to CD. The concept here is that there is no pupxxx file at all! No hard drive required. Sessions are saved back to the CD, each session saved as a folder on the CD.

I won't draw another diagram... looking at the diagram at the top of this page, just remove the "pup001" box and the hard drive partition box, and there you have it. Folder /root is totally in ramdisk, and when Puppy boots the saved sessions are copied off the CD into /root in the ramdisk. The downside of this is that lots of RAM is required -- 256M at least, although it will use a Linux swap partition if it exists so 128M of RAM or less will work.

To read more: Puppy multisession live-CD introduction.

Booting steps

This section not finished.

Way back in the early days of Puppy, I did write a page on the steps that Puppy goes through when booting. Way out of date, but you may like to see it: Booting Puppy Linux.

Making a start at writing this section...

Step 1

The first things that happen is the Linux kernel, file "vmlinuz" loads into RAM and file "image.gz" is uncompressed and loaded into /dev/ram0, a 11264K fixed-size ramdisk (increased to 12288K for Pup 1.0.4+).

In the case of a "big image" Puppy, that is, with file usr_cram.fs inside image.gz, there has to be the kernel boot parameter "ramdisk_size=" to specify a suitable size to hold all of image.gz (uncompressed) and usr_cram.fs plus some spare space. For example:

ramdisk_size=63488

For the "big image" situation, /dev/ram0 will be large, in the above example 63488 Kbytes.

Step 2

The program "/sbin/init" is executed. In the case of the "big image" Puppy, /sbin/init is just a link to /bin/busybox, as init is a program built-in to busybox. The normal situation though, is that /sbin/init is a shell script. Here it is:

#!/bin/sh
#v0.9.8 booting up in /dev/ram0, move root / to tmpfs...
INITARGS="`echo -n "$@" | sed -e 's/\/dev\/ram0/tmpfs/g'`"
PATH=/bin:/sbin

mount -t proc none /proc
mount -o remount,rw / #-n option not needed with busybox mount.

STARTUPFSSIZE=`cat /root0/.etc/ramdiskfssize` #currently set to 11264K (file gets updated below)
#specifying ram1 here as it isn't in use...
SIZERAMDISKM=`disktype /dev/ram1 | grep "Block device" | cut -f 4 -d " "`
SIZERAMDISKK=`expr $SIZERAMDISKM \* 1024`

#total ram, less any shared video...
PCRAMSIZE=`free | head -n 2 | tail -n 1 | tr -s " " | cut -f 3 -d " "`
#...note, busybox free output format different from standard free program.
if [ $PCRAMSIZE -gt 220000 ];then #256M
SIZEFILLK=`expr $PCRAMSIZE \/ 2` #half of ram.
else
if [ $PCRAMSIZE -gt 117000 ];then #128M
SIZEFILLK=67584 #63488
else
if [ $PCRAMSIZE -gt 60000 ];then #64M
SIZEFILLK=16384 #leaves about 7M free in f.s.
else
SIZEFILLK=11264 #only about 2M free in f.s.
fi
fi
fi
#precaution...
if [ $SIZEFILLK -lt $STARTUPFSSIZE ];then
SIZEFILLK=$STARTUPFSSIZE
fi

PHYSICALFILLK="$SIZEFILLK"

#want to know if there is a swap partition available...
SWAPPART=""
SWAPINFO="`fdisk -l | grep "Linux swap" | head -n 1`"
if [ ! "$SWAPINFO" = "" ];then
#we can make the ramdisk real big now...
SWAPPART="`echo "$SWAPINFO" | cut -f 1 -d " "`"
SWAPSIZE="`fdisk -s $SWAPPART`"
#SWAPSIZE=`expr $SWAPSIZE \/ 2` #let's use half of the swap partition.
#...nah, grab the whole lot. Puppy can create a swap file later if reqd.
SIZEFILLK=`expr $SIZEFILLK + $SWAPSIZE`
#0.9.8... but have to delay swapon until rc.sysinit running... see below...
fi

mkdir /new_root
mount tmpfs /new_root -t tmpfs -o size=${SIZEFILLK}k;check_status $?
cp -a /bin /new_root/
cp -a /dev /new_root/
cp -a /etc /new_root/
cp -a /lib /new_root/
cp -a /mnt /new_root/
cp -a /root /new_root/
cp -a /root0 /new_root/
cp -a /sbin /new_root/
cp -a /usr /new_root/
cp -a /proc0 /new_root/proc
cp -a /var0 /new_root/var
mkdir /new_root/tmp
chmod 777 /new_root/tmp
sync

#0.9.8R2 rc.sysinit will start the swap partition running...
if [ ! "$SWAPPART" = "" ];then
echo -n "$SWAPPART" > /new_root/root0/.etc/swappartition1
fi

#i want to know the max size if not going to use the swap partition...
echo -n "$PHYSICALFILLK" > /new_root/root0/.etc/ramdiskphysicalfssize

echo -n "$SIZERAMDISKK" > /new_root/root0/.etc/ramdisksize
echo -n "$SIZEFILLK" > /new_root/root0/.etc/ramdiskfssize #size of f.s.
cat /etc/fstab | sed -e 's/\/dev\/ram0/tmpfs/g' > /new_root/etc/fstab
#if ever run install-to-hd script, /sbin/init must be normal symlink...
mv -f /new_root/sbin/init /new_root/sbin/init_ORIG
ln -s /bin/busybox /new_root/sbin/init

cd /new_root
mkdir old_root
umount /proc
#have code in /etc/rc.d/rc.sysinit which will umount /dev/ram0...
pivot_root . old_root
exec chroot . sh -c "exec /bin/busybox init $INITARGS" dev/console 2>&1

In a nutshell, what this script does is copy all of /dev/ram0 to a new tmpfs ramdisk, which has the advantage of variable size and it can use a swap partition to create a very large effective size for the ramdisk. After the swap, /dev/ram0 is no longer being used.

Notice the very last line of the script. After the swap-over, "/bin/busybox init" is executed. This causes the init program built into Busybox to execute, the same as if /sbin/init was a link to /bin/busybox and init had been executed.

Step 3

Coming soon.


(c)Copyright Barry Kauler 2005,2006 Puppy Linux http://www.puppylinux.com/
No part of this page is to be reproduced anywhere else. I have found that there is a problem where parts of my web pages are being inserted at other sites, then not updated, whereas I am updating my pages regularly. This is not a desirable situation, so please just link to my pages.