Mac OS X Internals: A Systems Approach

8.11. The Dynamic Pager Program

The dynamic pager program (/sbin/dynamic_pager) is a user-level process that creates and deletes backing store (swap) files in the designated directory /var/vm/. Notwithstanding its name, dynamic_pager is not a Mach pager and is not involved otherwise in actual paging operations. It is only a manager of swap space for use by the kernel.

By default, Mac OS X uses dynamically created, variable-size paging files instead of dedicated swap partitions. The kernel writes data to these paging files in groups of pages (clusters).

dynamic_pager can be instructedthrough the -S command-line optionto use a fixed size for the paging files.

In its typical mode of operation, dynamic_pager works with two byte limits: a high-water mark and a low-water mark. When there are fewer bytes free in the swap files than allowed by the high-water mark, dynamic_pager creates a new file and adds it to the swap pool by notifying the kernel. When there are more bytes free in the paging files than allowed by the low-water mark, the kernel sends a notification to dynamic_pager to trigger deletion of a swap file (only one swap file is deleted per notification). Note that the swap file being deleted is likely to have some paged-out pages in itsuch pages are brought into the kernel and eventually paged out to another swap file. The high- and low-water marks can be specified to dynamic_pager through its -H and -L command-line options, respectively. If these limits are not explicitly specified, dynamic_pager calculates them when it starts. The startup script /etc/rc launches dynamic_pager, instructing it whether to encrypt the paging file data (the -E option) and specifying the path prefix of the swap files (/private/var/vm/swapfile by default).

The free page level in the kernel must remain below the maximum_pages_free threshold for at least PF_LATENCY (10) intervals of PF_INTERVAL (3) seconds each before the kernel will send a notification for swap file deletion.

# /etc/rc ... if [ ${ENCRYPTSWAP:=-NO-} = "-YES-" ]; then encryptswap="-E" else encryptswap="" fi /sbin/dynamic_pager ${encryptswap} -F ${swapdir}/swapfile

When it starts, dynamic_pager determines the high- and low-water marks and other limits based on command-line arguments, free file system space, installed physical memory, and built-in hard limits. Specifically, it establishes the following limits and rules.

  • The absolute minimum and maximum swap file sizes are 64MB and 1GB, respectively.

  • The maximum swap file size must not be greater than 12.5% of the free space available on the volume that contains the swap file. Moreover, the maximum swap file size must not be greater than the amount of physical memory on the system.

  • At most, eight swap files can be created.

  • The first two swap files have the same size: the minimum swap file size (64MB). Subsequent files double in size, up to the maximum swap file size.

  • The default high-water mark is 40,000,000 bytes (approximately 38MB).

dynamic_pager uses the macx_swapon() system call [bsd/vm/dp_backing_store_file.c] to add a file to the backing store. The corresponding removal call is macx_swapoff(), which removes a file from the backing store. The files themselves are created and deleted from the file system by dynamic_pager. Note that dynamic_pager passes a swap file's pathnamerather than a file descriptorto the kernel. The kernel (or rather, dynamic_pager's thread running in the kernel) internally looks up the pathname through namei() to acquire the corresponding vnode.

dynamic_pager uses another system callmacx_triggers()to enable or disable swap encryption and to set callbacks for high- and low-water marks.

kern_return_t macx_triggers(int hi_water, int low_water, int flags, mach_port_t alert_port);

The kernel processes an invocation of macx_triggers() based on the flags argument as follows.

  • If either SWAP_ENCRYPT_OFF or SWAP_ENCRYPT_ON is set in flags, the kernel instructs the default pager to disable or enable, respectively, swap encryption.

  • If HI_WAT_ALERT is set and alert_port contains a valid port to which the caller (dynamic_pager) has receive rights, the kernel arranges for an IPC message to be sent to the port when available backing store space falls below the high-water mark.

  • Similarly, if LO_WATER_ALERT is set, the kernel arranges for an IPC message to be sent to alert_port when available backing store space rises above the low-water mark.

Additionally, macx_triggers() elevates the calling thread's status as follows.

  • It marks it as a nontimeshare thread.

  • It sets the thread's importance to the maximum possible value.

  • It designates the thread as a VM-privileged thread, which enables the thread to allocate memory from the reserved pool if necessary.

Mac OS X 10.4 supports encryption of the data written by the kernel to the swap files. This feature can be enabled through the "Use secure virtual memory" checkbox in the Security system preference pane. When enabled, the following line is written to /etc/hostconfig:

ENCRYPTSWAP=-YES-

As we saw earlier, /etc/rc parses /etc/hostconfig, and if the ENCRYPTSWAP variable is set to YES, dynamic_pager is launched with the -E option. The kernel uses a form of the AES encryption algorithm for encrypting swap file data.

Even without swap file encryption, the kernel prevents user-space programs from directly reading swap data by using a special implementation of the read() system call. The vn_read() and vn_rdwr_64() internal functions in the VFS layer check the vnode they are dealing with to see if it corresponds to a swap file. If so, these functions call vn_read_swapfile() [bsd/vfs/vfs_vnops.c] instead of the usual internal read routine.

// bsd/vfs/vfs_vnops.c ... if (vp->v_flag & VSWAP) { // special case for swap files error = vn_read_swapfile(vp, uio); } else { error = VNOP_READ(vp, uio, ioflag, &context); } ...

vn_read_swapfile() reads zero-filled pages[16] instead of the actual contents of the file.

[16] The last byte of each page read is set to the newline character.

Mapping the Swap

It is not possible to map the pages in swap files to the tasks that were using themthe "user" is a VM object. As we have seen, a VM object can be shared between multiple tasks. Moreover, in the case of a copy object, there is no direct connection between any task and that copy object; the latter holds a reference to the original VM object. Normally, it is also not possible to determine which blocks in a swap file are currently being used to hold swapped-out pages. However, the kernel can be compiled with a feature called Mach page map, wherein the VM subsystem maintains a bitmap (called the existence map) for internal objects. The bitmap tracks which pages of the object are currently swapped out to the backing storethe trivial determination of whether the page corresponding to a given VM object/offset pair is available on the backing store can be used as an optimization during page-fault processing.

The existence map for a VM object can be printed through the object command in the built-in kernel debugger (KDB).

Категории