The original Model B Rev 2.0 Raspberry Pi had 512 MB of SDRAM (synchronous dynamic random access memory), while the older revisions and the Model A had 256 MB. Modern Pi’s now come equipped with 1 GB of RAM except for the Raspberry Pi Zero, which has 512 MB. Contrast this to the AVR class ATmega328P, which has 2 KB of static RAM!
There isn’t much about the memory hardware that concerns the average Pi developer because the operating system provides a rich facility for managing program use. However, let’s examine some useful Raspbian Linux interfaces that inform us how that memory is utilized. You’ll also examine how to access the memory-mapped ARM peripherals directly from your Linux application.
/proc/meminfo
All of the memory values shown have the units KB to the right of them, indicating kilo (1,024) bytes. These reported values are described in great detail online.7 But let’s summarize some values of interest.
MemTotal
The MemTotal line indicates the total amount of memory available, minus a few reserved binary regions. Note that memory allocated to the GPU is not factored into MemTotal. Some owners may choose to allocate the minimum of 16 MB to the GPU to make more memory available when not using the graphical desktop. The default is to assign 64 MB.
GPU and Main Memory Breakdown
Memory | Model B | Comments |
---|---|---|
SDRAM | 1,048,576 KB | Hardware-equipped memory |
MemTotal | 949,452 KB | /proc/meminfo |
Difference | 99,124 KB | Remaining memory |
gpu_mem | 65,536 KB | /boot/config.txt (gpu_mem=64 by default) |
Difference | 33,588 KB | Unaccounted for |
MemFree
MemFree normally represents the sum of LowFree + HighFree memory in kilobytes on the Intel x86 platform. For ARM, this simply represents the amount of memory available to user space programs.
Buffers
This value represents temporary buffers used within the kernel for raw disk blocks, and so forth. This value should not get much larger than about 20 MB or so.8
Cached
This value represents the read file content that has been cached (page cache). This does not include the content reported for SwapCached.
SwapCached
The value shown for SwapCached represents memory that was swapped out and is now swapped back in. For efficiency, these memory pages are still represented by swap disk space, should they be needed again. The fact that the value is reported as zero is a happy sign that no swapping has occurred or is no longer pertinent.
Active
The Active memory value represents recently used memory that is not reclaimed, unless absolutely necessary.
Inactive
This value represents memory that is not active and is likely to be reclaimed when memory is needed.
Active(anon)
This value represents memory that is not backed up by a file and is active. Active memory is not reclaimed unless absolutely necessary.
Inactive(anon)
This value represents memory that is not backed up by a file and is not active. Inactive memory is eligible to be reclaimed if memory is required.
Active(file)
This value represents file-backed memory, which is active. Active memory is reclaimed only if absolutely required.
Inactive(file)
This value represents inactive memory that is backed by a file. Inactive memory is eligible for reclamation, when memory is required.
Unevictable
This amount reflects the total amount of memory that cannot be reclaimed. Memory that is locked, for example, cannot be reclaimed.
Mlocked
This value reports the amount of locked memory.
SwapTotal
This value reports the total amount of swap space available in kilobytes.
SwapFree
This value reports the remaining amount of swap space available in kilobytes.
Dirty
This value represents the kilobytes of memory that have been modified and are waiting to be written to disk.
Writeback
This value reports the amount of memory in kilobytes being written back to disk.
AnonPages
This represents the non-file-backed pages of memory mapped into user space.
Mapped
This value reports the files that have been mapped into memory. This may include library code.
Shmem
This parameter does not appear to be documented well. However, it represents the amount of shared memory in kilobytes.
Slab
This parameter is described as “in-kernel data structures cache.”
SReclaimable
This parameter is described as “Part of Slab that might be reclaimed, such as caches.”
SUnreclaim
This parameter is described as “Part of Slab that cannot be reclaimed [under] memory pressure.”
KernelStack
This value reports the memory used by the kernel stack(s).
PageTables
This value reports the amount of memory required by the page tables used in the kernel. Clearly, with more memory to manage, there is more memory dedicated to page tables.
NFS_Unstable
This value represents “NFS pages sent to the server, but not yet committed to stable storage.” This example data suggests that NFS is not being used.
Bounce
This reports the memory used for “block device bounce buffers.”
WritebackTmp
This parameter reports the memory used by FUSE for “temporary writeback buffers.”
CommitLimit
Based on the overcommit ratio (vm.overcommit_ratio), this is the total amount of memory currently available to be allocated on the system. This limit is only adhered to if strict overcommit accounting is enabled (mode 2 in vm.overcommit_memory). The CommitLimit is calculated with the following formula:
For example, a system with 1 GB of physical RAM and 7 GB of swap with a vm.overcommit_ratio of 30 would yield a CommitLimit of 7.3 GB. For more details, see the memory overcommit documentation in vm/overcommitaccounting.
C is the overcommit limit.
R is the physical RAM available (MemTotal).
S is the swap space available (SwapTotal).
r is the overcommit ratio percent (expressed as a fraction).
The value 50 is to be interpreted as r = 0.50 (50%).
Committed_AS
The amount of memory presently allocated on the system. The committed memory is a sum of all of the memory which has been allocated by processes, even if it has not been “used” by them as of yet. A process which malloc()’s 1 GB of memory, but only touches 300 MB of it will only show up as using 300 MB of memory even if it has the address space allocated for the entire 1 GB. This 1 GB is memory which has been “committed” to by the VM and can be used at any time by the allocating application. With strict overcommit enabled on the system (mode 2 in vm.overcommit_memory), allocations which would exceed the CommitLimit (detailed above) will not be permitted. This is useful if one needs to guarantee that processes will not fail due to lack of memory once that memory has been successfully allocated.
VmallocTotal
This represents the total amount of allocated virtual memory address space.
VmallocUsed
This is the amount of virtual memory that is in use, reported in kilobytes.
VmallocChunk
This value reports the largest size of a vmalloc area, in kilobytes.
Physical Memory
Normally, physical memory isn’t a concern to application programmers because the operating system and its drivers provide an abstract and often portable way to access it. However, when this support is absent, direct access to a peripheral like the PWM controller is necessary.
With modern Pi models, we cannot assume a constant for the starting peripheral address. Later in the book, a way to determine this value at runtime will be shown.
Peripheral Offsets for the Raspberry Pi Model B
Peripheral | Offset | Address | Description | C Offset Macro |
---|---|---|---|---|
Base | 0x00000000 | 0x20000000 | Starting address | BCM2708_PERI_BASE |
PADS_GPIO | 0x00100000 | 0x20100000 | PADS base | PADS_GPIO_BASE |
GPIO 00..27 | 0x0010002C | 0x2010002C | GPIO 00..27 pads | PADS_GPIO_00_27 |
GPIO 28..45 | 0x00100030 | 0x20100030 | GPIO 28..45 pads | PADS_GPIO_28_45 |
GPIO 46..53 | 0x00100034 | 0x20100034 | GPIO 46..53 pads | PADS_GPIO_46_53 |
Clock | 0x00101000 | 0x20101000 | Clock registers | CLK_BASE |
GPIO | 0x00200000 | 0x20200000 | GPIO registers | GPIO_BASE |
GPPUD | 0x00200025 | 0x20200025 | Pull-up enable | |
GPPUDCLK0 | 0x00200026 | 0x20200026 | Pull-up clock 0 | |
GPPUDCLK1 | 0x00200027 | 0x20200027 | Pull-up clock 1 | |
PWM | 0x0020C000 | 0x2020C000 | PWM registers | PWM_BASE |
Memory Mapping
From the ownership information shown, it is evident that you’ll need root privileges to access it. This is sensible given that a process can cause havoc with direct access to the physical memory. Exercise caution in what the applications do with this memory access.
The first thing performed in this code is to open the device driver node /dev/mem. It is opened for reading and writing (O_RDWR), and the option flag O_SYNC requests that any write(2) call to this file descriptor results in blocking the execution of the caller until it has completed.
Address
Next, the mmap(2) call is invoked. The address argument is provided with NULL (zero) so that the kernel can choose where to map it into the caller’s address space. If the application were to specify a starting address to use and the kernel was not able use it, the system call would fail. The starting address is returned and assigned to the character pointer map in the preceding listing.
Length
Protection
The third mmap(2) argument is supplied with the flags PROT_READ and PROT_WRITE. This indicates that the application wants both read and write access to the memory-mapped region.
Flags
The flags argument is supplied with the value MAP_SHARED. This permits nonexclusive access to the underlying mapping.
File Descriptor
This argument supplies the underlying opened file to be mapped into memory. In this case, we map a region of physical ARM memory into our application by using the opened device driver node /dev/mem.
Offset
This last argument specifies the location in physical memory where we want to start our access. For the GPIO registers, it is the address 0x20200000 on the original Pi Model B.
Return Value
The return value, when successful, will be an application address that points to the physical memory region we asked for. The application programmer need not be concerned with what this address is, except to save and use it for access.
The returned address (pointer) map is cast to a long integer and compared to -1L. This is the magic value that indicates that an error occurred. The error code is found in errno.
Volatile
The data type is an unsigned int (32 bits on the Pi).
The pointed-to data is marked as volatile.
Since the Pi registers are 32 bits in size, it is often more convenient to access them as 32-bit words. The unsigned data type is perfect for this. But be careful with offsets in conjunction with this pointer, since they will be word rather than byte offsets.
The volatile keyword tells the compiler not to optimize access to memory through the pointer variable. Imagine code that reads a peripheral register and reads the same register again later, to see whether an event has occurred. An optimizing compiler might say to itself, “I already have this value in CPU register R, so I’ll just use that because it's faster.” But the effect of this code is that it will never see a bit change in the peripheral’s register because that data was not fetched back into the register. The volatile keyword forces the compiler to retrieve the value even though it would be faster to use the value still found in the register.
Virtual Memory
In the previous section, you looked at how to access physical memory in an application, provided that you had the access (root or setuid). The Broadcom Corporation PDF manual “BCM2835 ARM Peripherals,” page 5, also shows a virtual memory layout on the right. This should not be confused with the physical memory layout that was examined earlier. Virtual memory can be accessed through /dev/kmem driver node using mmap(2).
Summary
Some parameters such as Buffers impact the performance of Raspbian Linux on the Pi. How much memory is allocated to file data that has been read from disk, for example? Other configuration parameters determine how much SDRAM is dedicated to GPU use.
Probably the most important aspect of memory allocation is how much memory is available to developer application programs. The value of MemFree is therefore a useful metric. When exceeding physical memory limits, the swapping parameters then become measurements of interest.
Finally, access to the Raspberry Pi peripherals directly using mmap(2) was introduced. Until Raspbian Linux gains device drivers for peripherals such as PWM, the direct access technique will be necessary. Even with driver support, there are sometimes good reasons to access the peripheral registers directly.