procfs_core/meminfo.rs
1use super::{expect, from_str, ProcResult};
2#[cfg(feature = "serde1")]
3use serde::{Deserialize, Serialize};
4use std::{collections::HashMap, io};
5
6fn convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64> {
7 match unit {
8 "B" => Ok(num),
9 "KiB" | "kiB" | "kB" | "KB" => Ok(num * 1024),
10 "MiB" | "miB" | "MB" | "mB" => Ok(num * 1024 * 1024),
11 "GiB" | "giB" | "GB" | "gB" => Ok(num * 1024 * 1024 * 1024),
12 unknown => Err(build_internal_error!(format!("Unknown unit type {}", unknown))),
13 }
14}
15
16/// This struct reports statistics about memory usage on the system, based on
17/// the `/proc/meminfo` file.
18///
19/// It is used by `free(1)` to report the amount of free and used memory (both
20/// physical and swap) on the system as well as the shared memory and
21/// buffers used by the kernel. Each struct member is generally reported in
22/// bytes, but a few are unitless values.
23///
24/// Except as noted below, all of the fields have been present since at least
25/// Linux 2.6.0. Some fields are optional and are present only if the kernel
26/// was configured with various options; those dependencies are noted in the list.
27///
28/// **Notes**
29///
30/// While the file shows kilobytes (kB; 1 kB equals 1000 B),
31/// it is actually kibibytes (KiB; 1 KiB equals 1024 B).
32///
33/// All sizes are converted to bytes. Unitless values, like `hugepages_total` are not affected.
34///
35/// This imprecision in /proc/meminfo is known,
36/// but is not corrected due to legacy concerns -
37/// programs rely on /proc/meminfo to specify size with the "kB" string.
38///
39/// New fields to this struct may be added at any time (even without a major or minor semver bump).
40#[derive(Debug, Clone)]
41#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
42#[allow(non_snake_case)]
43#[non_exhaustive]
44pub struct Meminfo {
45 /// Total usable RAM (i.e., physical RAM minus a few reserved bits and the kernel binary code).
46 pub mem_total: u64,
47 /// The sum of [LowFree](#structfield.low_free) + [HighFree](#structfield.high_free).
48 pub mem_free: u64,
49 /// An estimate of how much memory is available for starting new applications, without swapping.
50 ///
51 /// (since Linux 3.14)
52 pub mem_available: Option<u64>,
53 /// Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
54 pub buffers: u64,
55 /// In-memory cache for files read from the disk (the page cache). Doesn't include SwapCached.
56 pub cached: u64,
57 /// Memory that once was swapped out, is swapped back in but still also is in the swap
58 /// file.
59 ///
60 /// (If memory pressure is high, these pages don't need to be swapped out again
61 /// because they are already in the swap file. This saves I/O.)
62 pub swap_cached: u64,
63 /// Memory that has been used more recently and usually not reclaimed unless absolutely
64 /// necessary.
65 pub active: u64,
66 /// Memory which has been less recently used. It is more eligible to be reclaimed for other
67 /// purposes.
68 pub inactive: u64,
69 /// [To be documented.]
70 ///
71 /// (since Linux 2.6.28)
72 pub active_anon: Option<u64>,
73 /// [To be documented.]
74 ///
75 /// (since Linux 2.6.28)
76 pub inactive_anon: Option<u64>,
77 /// [To be documented.]
78 ///
79 /// (since Linux 2.6.28)
80 pub active_file: Option<u64>,
81 /// [To be documented.]
82 ///
83 /// (since Linux 2.6.28)
84 pub inactive_file: Option<u64>,
85 /// [To be documented.]
86 ///
87 /// (From Linux 2.6.28 to 2.6.30, CONFIG_UNEVICTABLE_LRU was required.)
88 pub unevictable: Option<u64>,
89 /// [To be documented.]
90 ///
91 /// (From Linux 2.6.28 to 2.6.30, CONFIG_UNEVICTABLE_LRU was required.)
92 pub mlocked: Option<u64>,
93 /// Total amount of highmem.
94 ///
95 /// Highmem is all memory above ~860MB of physical memory. Highmem areas are for use by
96 /// user-space programs, or for the page cache. The kernel must use tricks to access this
97 /// memory, making it slower to access than lowmem.
98 ///
99 /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
100 pub high_total: Option<u64>,
101 /// Amount of free highmem.
102 ///
103 /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
104 pub high_free: Option<u64>,
105 /// Total amount of lowmem.
106 ///
107 /// Lowmem is memory which can be used for every thing that highmem can be used for,
108 /// but it is also available for the kernel's use for its own data structures.
109 /// Among many other things, it is where everything from Slab is allocated.
110 /// Bad things happen when you're out of lowmem.
111 ///
112 /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
113 pub low_total: Option<u64>,
114 /// Amount of free lowmem.
115 ///
116 /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
117 pub low_free: Option<u64>,
118 /// [To be documented.]
119 ///
120 /// (since Linux 2.6.29. CONFIG_MMU is required.)
121 pub mmap_copy: Option<u64>,
122 /// Total amount of swap space available.
123 pub swap_total: u64,
124 /// Amount of swap space that is currently unused.
125 pub swap_free: u64,
126 /// Memory which is waiting to get written back to the disk.
127 pub dirty: u64,
128 /// Memory which is actively being written back to the disk.
129 pub writeback: u64,
130 /// Non-file backed pages mapped into user-space page tables.
131 ///
132 /// (since Linux 2.6.18)
133 pub anon_pages: Option<u64>,
134 /// Files which have been mapped into memory (with mmap(2)), such as libraries.
135 pub mapped: u64,
136 /// Amount of memory consumed in tmpfs(5) filesystems.
137 ///
138 /// (since Linux 2.6.32)
139 pub shmem: Option<u64>,
140 /// In-kernel data structures cache.
141 pub slab: u64,
142 /// Part of Slab, that can be reclaimed on memory pressure.
143 ///
144 /// (since Linux 2.6.19)
145 pub s_reclaimable: Option<u64>,
146 /// Part of Slab, that cannot be reclaimed on memory pressure.
147 ///
148 /// (since Linux 2.6.19)
149 pub s_unreclaim: Option<u64>,
150 /// Amount of memory allocated to kernel stacks.
151 ///
152 /// (since Linux 2.6.32)
153 pub kernel_stack: Option<u64>,
154 /// Amount of memory dedicated to the lowest level of page tables.
155 ///
156 /// (since Linux 2.6.18)
157 pub page_tables: Option<u64>,
158 /// Amount of memory allocated for seconary page tables. This currently includes KVM mmu
159 /// allocations on x86 and arm64.
160 ///
161 /// (since Linux 6.1)
162 pub secondary_page_tables: Option<u64>,
163 /// [To be documented.]
164 ///
165 /// (CONFIG_QUICKLIST is required. Since Linux 2.6.27)
166 pub quicklists: Option<u64>,
167 /// NFS pages sent to the server, but not yet committed to stable storage.
168 ///
169 /// (since Linux 2.6.18)
170 pub nfs_unstable: Option<u64>,
171 /// Memory used for block device "bounce buffers".
172 ///
173 /// (since Linux 2.6.18)
174 pub bounce: Option<u64>,
175 /// Memory used by FUSE for temporary writeback buffers.
176 ///
177 /// (since Linux 2.6.26)
178 pub writeback_tmp: Option<u64>,
179 /// This is the total amount of memory currently available to be allocated on the system,
180 /// expressed in bytes.
181 ///
182 /// This limit is adhered to only if strict overcommit
183 /// accounting is enabled (mode 2 in /proc/sys/vm/overcommit_memory). The limit is calculated
184 /// according to the formula described under /proc/sys/vm/overcommit_memory. For further
185 /// details, see the kernel source file
186 /// [Documentation/vm/overcommit-accounting](https://www.kernel.org/doc/Documentation/vm/overcommit-accounting).
187 ///
188 /// (since Linux 2.6.10)
189 pub commit_limit: Option<u64>,
190 /// The amount of memory presently allocated on the system.
191 ///
192 /// The committed memory is a sum of all of the memory which has been allocated
193 /// by processes, even if it has not been "used" by them as of yet. A process which allocates 1GB of memory (using malloc(3)
194 /// or similar), but touches only 300MB of that memory will show up as using only 300MB of memory even if it has the address space
195 /// allocated for the entire 1GB.
196 ///
197 /// This 1GB is memory which has been "committed" to by the VM and can be used at any time by the allocating application. With
198 /// strict overcommit enabled on the system (mode 2 in /proc/sys/vm/overcommit_memory), allocations which would exceed the Committed_AS
199 /// mitLimit will not be permitted. This is useful if one needs to guarantee that processes will not fail due to lack of memory once
200 /// that memory has been successfully allocated.
201 pub committed_as: u64,
202 /// Total size of vmalloc memory area.
203 pub vmalloc_total: u64,
204 /// Amount of vmalloc area which is used.
205 pub vmalloc_used: u64,
206 /// Largest contiguous block of vmalloc area which is free.
207 pub vmalloc_chunk: u64,
208 /// [To be documented.]
209 ///
210 /// (CONFIG_MEMORY_FAILURE is required. Since Linux 2.6.32)
211 pub hardware_corrupted: Option<u64>,
212 /// Non-file backed huge pages mapped into user-space page tables.
213 ///
214 /// (CONFIG_TRANSPARENT_HUGEPAGE is required. Since Linux 2.6.38)
215 pub anon_hugepages: Option<u64>,
216 /// Memory used by shared memory (shmem) and tmpfs(5) allocated with huge pages
217 ///
218 /// (CONFIG_TRANSPARENT_HUGEPAGE is required. Since Linux 4.8)
219 pub shmem_hugepages: Option<u64>,
220 /// Shared memory mapped into user space with huge pages.
221 ///
222 /// (CONFIG_TRANSPARENT_HUGEPAGE is required. Since Linux 4.8)
223 pub shmem_pmd_mapped: Option<u64>,
224 /// Total CMA (Contiguous Memory Allocator) pages.
225 ///
226 /// (CONFIG_CMA is required. Since Linux 3.1)
227 pub cma_total: Option<u64>,
228 /// Free CMA (Contiguous Memory Allocator) pages.
229 ///
230 /// (CONFIG_CMA is required. Since Linux 3.1)
231 pub cma_free: Option<u64>,
232 /// The size of the pool of huge pages.
233 ///
234 /// CONFIG_HUGETLB_PAGE is required.)
235 pub hugepages_total: Option<u64>,
236 /// The number of huge pages in the pool that are not yet allocated.
237 ///
238 /// (CONFIG_HUGETLB_PAGE is required.)
239 pub hugepages_free: Option<u64>,
240 /// This is the number of huge pages for which a commitment to allocate from the pool has been
241 /// made, but no allocation has yet been made.
242 ///
243 /// These reserved huge pages guarantee that an application will be able to allocate a
244 /// huge page from the pool of huge pages at fault time.
245 ///
246 /// (CONFIG_HUGETLB_PAGE is required. Since Linux 2.6.17)
247 pub hugepages_rsvd: Option<u64>,
248 /// This is the number of huge pages in the pool above the value in /proc/sys/vm/nr_hugepages.
249 ///
250 /// The maximum number of surplus huge pages is controlled by /proc/sys/vm/nr_overcommit_hugepages.
251 ///
252 /// (CONFIG_HUGETLB_PAGE is required. Since Linux 2.6.24)
253 pub hugepages_surp: Option<u64>,
254 /// The size of huge pages.
255 ///
256 /// (CONFIG_HUGETLB_PAGE is required.)
257 pub hugepagesize: Option<u64>,
258 /// Number of bytes of RAM linearly mapped by kernel in 4kB pages. (x86.)
259 ///
260 /// (since Linux 2.6.27)
261 pub direct_map_4k: Option<u64>,
262 /// Number of bytes of RAM linearly mapped by kernel in 4MB pages.
263 ///
264 /// (x86 with CONFIG_X86_64 or CONFIG_X86_PAE enabled. Since Linux 2.6.27)
265 pub direct_map_4M: Option<u64>,
266 /// Number of bytes of RAM linearly mapped by kernel in 2MB pages.
267 ///
268 /// (x86 with neither CONFIG_X86_64 nor CONFIG_X86_PAE enabled. Since Linux 2.6.27)
269 pub direct_map_2M: Option<u64>,
270 /// (x86 with CONFIG_X86_64 and CONFIG_X86_DIRECT_GBPAGES enabled. Since Linux 2.6.27)
271 pub direct_map_1G: Option<u64>,
272
273 /// needs documentation
274 pub hugetlb: Option<u64>,
275
276 /// Memory allocated to the per-cpu alloctor used to back per-cpu allocations.
277 ///
278 /// This stat excludes the cost of metadata.
279 pub per_cpu: Option<u64>,
280
281 /// Kernel allocations that the kernel will attempt to reclaim under memory pressure.
282 ///
283 /// Includes s_reclaimable, and other direct allocations with a shrinker.
284 pub k_reclaimable: Option<u64>,
285
286 /// Undocumented field
287 ///
288 /// (CONFIG_TRANSPARENT_HUGEPAGE is requried. Since Linux 5.4)
289 pub file_pmd_mapped: Option<u64>,
290
291 /// Undocumented field
292 ///
293 /// (CONFIG_TRANSPARENT_HUGEPAGE is required. Since Linux 5.4)
294 pub file_huge_pages: Option<u64>,
295
296 /// Memory consumed by the zswap backend (compressed size).
297 ///
298 /// (CONFIG_ZSWAP is required. Since Linux 5.19)
299 pub z_swap: Option<u64>,
300
301 /// Amount of anonymous memory stored in zswap (original size).
302 ///
303 /// (CONFIG_ZSWAP is required. Since Linux 5.19)
304 pub z_swapped: Option<u64>,
305}
306
307impl super::FromBufRead for Meminfo {
308 fn from_buf_read<R: io::BufRead>(r: R) -> ProcResult<Self> {
309 let mut map = HashMap::new();
310
311 for line in r.lines() {
312 let line = expect!(line);
313 if line.is_empty() {
314 continue;
315 }
316 let mut s = line.split_whitespace();
317 let field = expect!(s.next(), "no field");
318 let value = expect!(s.next(), "no value");
319 let unit = s.next(); // optional
320
321 let value = from_str!(u64, value);
322
323 let value = if let Some(unit) = unit {
324 convert_to_kibibytes(value, unit)?
325 } else {
326 value
327 };
328
329 map.insert(field[..field.len() - 1].to_string(), value);
330 }
331
332 // use 'remove' to move the value out of the hashmap
333 // if there's anything still left in the map at the end, that
334 // means we probably have a bug/typo, or are out-of-date
335 let meminfo = Meminfo {
336 mem_total: expect!(map.remove("MemTotal")),
337 mem_free: expect!(map.remove("MemFree")),
338 mem_available: map.remove("MemAvailable"),
339 buffers: expect!(map.remove("Buffers")),
340 cached: expect!(map.remove("Cached")),
341 swap_cached: expect!(map.remove("SwapCached")),
342 active: expect!(map.remove("Active")),
343 inactive: expect!(map.remove("Inactive")),
344 active_anon: map.remove("Active(anon)"),
345 inactive_anon: map.remove("Inactive(anon)"),
346 active_file: map.remove("Active(file)"),
347 inactive_file: map.remove("Inactive(file)"),
348 unevictable: map.remove("Unevictable"),
349 mlocked: map.remove("Mlocked"),
350 high_total: map.remove("HighTotal"),
351 high_free: map.remove("HighFree"),
352 low_total: map.remove("LowTotal"),
353 low_free: map.remove("LowFree"),
354 mmap_copy: map.remove("MmapCopy"),
355 swap_total: expect!(map.remove("SwapTotal")),
356 swap_free: expect!(map.remove("SwapFree")),
357 dirty: expect!(map.remove("Dirty")),
358 writeback: expect!(map.remove("Writeback")),
359 anon_pages: map.remove("AnonPages"),
360 mapped: expect!(map.remove("Mapped")),
361 shmem: map.remove("Shmem"),
362 slab: expect!(map.remove("Slab")),
363 s_reclaimable: map.remove("SReclaimable"),
364 s_unreclaim: map.remove("SUnreclaim"),
365 kernel_stack: map.remove("KernelStack"),
366 page_tables: map.remove("PageTables"),
367 secondary_page_tables: map.remove("SecPageTables"),
368 quicklists: map.remove("Quicklists"),
369 nfs_unstable: map.remove("NFS_Unstable"),
370 bounce: map.remove("Bounce"),
371 writeback_tmp: map.remove("WritebackTmp"),
372 commit_limit: map.remove("CommitLimit"),
373 committed_as: expect!(map.remove("Committed_AS")),
374 vmalloc_total: expect!(map.remove("VmallocTotal")),
375 vmalloc_used: expect!(map.remove("VmallocUsed")),
376 vmalloc_chunk: expect!(map.remove("VmallocChunk")),
377 hardware_corrupted: map.remove("HardwareCorrupted"),
378 anon_hugepages: map.remove("AnonHugePages"),
379 shmem_hugepages: map.remove("ShmemHugePages"),
380 shmem_pmd_mapped: map.remove("ShmemPmdMapped"),
381 cma_total: map.remove("CmaTotal"),
382 cma_free: map.remove("CmaFree"),
383 hugepages_total: map.remove("HugePages_Total"),
384 hugepages_free: map.remove("HugePages_Free"),
385 hugepages_rsvd: map.remove("HugePages_Rsvd"),
386 hugepages_surp: map.remove("HugePages_Surp"),
387 hugepagesize: map.remove("Hugepagesize"),
388 direct_map_4k: map.remove("DirectMap4k"),
389 direct_map_4M: map.remove("DirectMap4M"),
390 direct_map_2M: map.remove("DirectMap2M"),
391 direct_map_1G: map.remove("DirectMap1G"),
392 k_reclaimable: map.remove("KReclaimable"),
393 per_cpu: map.remove("Percpu"),
394 hugetlb: map.remove("Hugetlb"),
395 file_pmd_mapped: map.remove("FilePmdMapped"),
396 file_huge_pages: map.remove("FileHugePages"),
397 z_swap: map.remove("Zswap"),
398 z_swapped: map.remove("Zswapped"),
399 };
400
401 if cfg!(test) {
402 assert!(map.is_empty(), "meminfo map is not empty: {:#?}", map);
403 }
404
405 Ok(meminfo)
406 }
407}