The Binder driver is incredibly fast for sending small messages and method calls between processes. But the system enforces a strict transaction limit. Historically, this limit sits around 1MB per process. Imagine a music app needs to send a 10MB uncompressed audio buffer to the audio server daemon. The application cannot push that much data through a standard Binder transaction. The system throws a transaction too large exception.
Bypassing the Transaction Limit
We need a way to share massive blocks of data without copying them back and forth. Anonymous Shared Memory, or Ashmem, provides this capability. This custom Android kernel driver exposes itself to user space via a character device. This design allows two completely isolated processes to share a block of physical RAM.
When an app needs to share a large buffer, the application asks the Ashmem driver to allocate a block of physical memory. The kernel returns a standard Linux file descriptor pointing to this memory block. Application code then sends only this tiny file descriptor over Binder. Binder safely translates the descriptor between the isolated processes. The receiving process acquires the descriptor and calls a memory map function. Finally, the kernel maps the exact same block of physical RAM into the receiving process's virtual memory space.
Writing data to this mapped memory makes the information instantly available to the receiving app. There are zero copies happening in the background. The following sequence diagram illustrates this memory sharing mechanism. The visual sequence helps clarify how Binder and Ashmem work together. Look for the moment when Binder translates the file descriptor between the two isolated processes.
Application code allocates the Ashmem region and maps the memory locally to write data. Binder handles the secure translation of the file descriptor. The audio server maps the translated descriptor and reads the exact same physical memory. This elegant handoff works perfectly. But standard Linux already has a shared memory system, which raises an interesting question. Why did Google write a custom driver just for Android?
Surviving the Low Memory Killer
Standard Linux provides POSIX shared memory for native processes. The problem with this built-in standard is sticky memory persistence. If an app crashes and forgets to explicitly free the shared memory, the system leaks that memory permanently. The only way to recover the lost space is to reboot the device. Mobile devices operate with severe RAM constraints, making a permanent memory leak unacceptable on a phone.
Ashmem solves this by integrating directly with the kernel's file descriptor reference counting. If the app and the audio server both crash, the kernel instantly detects that all file descriptors pointing to that Ashmem region are closed. The kernel automatically reclaims the physical RAM. The Android Low Memory Killer can now safely terminate processes without worrying about stranded shared memory blocks.
Mobile applications can also mark Ashmem regions as purgeable. This tells the kernel that the data is useful but not critical. You use the pin and unpin control commands to manage this state. When you execute these commands, you tell the kernel to either protect the memory or silently steal the pages during a low-RAM emergency.
int fd = open("/dev/ashmem", O_RDWR);
ioctl(fd, ASHMEM_SET_NAME, "audio_buffer");
ioctl(fd, ASHMEM_SET_SIZE, 10 * 1024 * 1024);
void *buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Mark the memory as purgeable when not actively in use
ioctl(fd, ASHMEM_UNPIN, &pin_struct);
// Lock the memory to prevent the kernel from stealing it
ioctl(fd, ASHMEM_PIN, &pin_struct);
Common Mistake: Forgetting to pin an unpinned region before reading from it can crash your app if the kernel purged the memory. Always verify the pin operation succeeds.
Custom drivers saved Android from fatal memory leaks for over a decade. But maintaining custom kernel modifications carries a heavy maintenance cost for Google.
The Shift to Standard Linux
Platform maintainers carry a massive burden by supporting custom kernel modifications. Every time the mainline Linux kernel updates, Google engineers must port their custom drivers to the new version. Fortunately, the mainline Linux kernel eventually introduced the memory file descriptor creation system call. This modern alternative provides the exact functionality Android needs without custom patches.
These mainline capabilities finally gained the automatic cleanup and file descriptor sealing features that Ashmem provided. By moving to standard Linux calls, Android reduces system technical debt. This shift also makes the platform more compatible with standard Linux tools. Engineers no longer need to worry about maintaining out-of-tree drivers just for memory sharing.
You still write the same high-level application code for zero-copy memory sharing. The Android framework simply translates your shared memory requests to the new standard system calls under the hood. Our fundamental architecture of passing a file descriptor over Binder remains exactly the same. But with sharing memory comes the responsibility of protecting access, which brings up the next challenge. How do we secure these standard kernel interfaces against malicious apps?