1 /* Calculate the size of physical memory.
3 Copyright (C) 2000-2001, 2003, 2005-2006, 2009-2025 Free Software
6 This file is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 This file is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 /* Written by Paul Eggert. */
29 #if HAVE_SYS_PSTAT_H /* HP-UX */
30 # include <sys/pstat.h>
33 #if HAVE_SYS_SYSMP_H /* IRIX */
34 # include <sys/sysmp.h>
37 #if HAVE_SYS_SYSINFO_H
38 /* Linux, AIX, HP-UX, IRIX, OSF/1, Solaris, Cygwin, Android */
39 # include <sys/sysinfo.h>
42 #if HAVE_MACHINE_HAL_SYSINFO_H /* OSF/1 */
43 # include <machine/hal_sysinfo.h>
46 #if HAVE_SYS_TABLE_H /* OSF/1 */
47 # include <sys/table.h>
50 #include <sys/types.h>
53 # include <sys/param.h>
56 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__)
57 /* Linux/musl, macOS, *BSD, IRIX, Minix */
58 # include <sys/sysctl.h>
61 #if HAVE_SYS_SYSTEMCFG_H /* AIX */
62 # include <sys/systemcfg.h>
65 #include "full-read.h"
69 # define WIN32_LEAN_AND_MEAN
72 /* Don't assume that UNICODE is not defined. */
73 # undef GetModuleHandle
74 # define GetModuleHandle GetModuleHandleA
76 /* Avoid warnings from gcc -Wcast-function-type. */
77 # define GetProcAddress \
78 (void *) GetProcAddress
80 /* MEMORYSTATUSEX is missing from older windows headers, so define
81 a local replacement. */
86 DWORDLONG ullTotalPhys
;
87 DWORDLONG ullAvailPhys
;
88 DWORDLONG ullTotalPageFile
;
89 DWORDLONG ullAvailPageFile
;
90 DWORDLONG ullTotalVirtual
;
91 DWORDLONG ullAvailVirtual
;
92 DWORDLONG ullAvailExtendedVirtual
;
94 typedef BOOL (WINAPI
*PFN_MS_EX
) (lMEMORYSTATUSEX
*);
98 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
100 /* Return the total amount of physical memory. */
104 #if defined _SC_PHYS_PAGES && defined _SC_PAGESIZE
105 { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin. */
106 double pages
= sysconf (_SC_PHYS_PAGES
);
107 double pagesize
= sysconf (_SC_PAGESIZE
);
108 if (0 <= pages
&& 0 <= pagesize
)
109 return pages
* pagesize
;
113 #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
114 { /* This works on linux. */
116 if (sysinfo(&si
) == 0)
117 return (double) si
.totalram
* si
.mem_unit
;
121 #if HAVE_PSTAT_GETSTATIC
122 { /* This works on hpux11. */
123 struct pst_static pss
;
124 if (0 <= pstat_getstatic (&pss
, sizeof pss
, 1, 0))
126 double pages
= pss
.physical_memory
;
127 double pagesize
= pss
.page_size
;
128 if (0 <= pages
&& 0 <= pagesize
)
129 return pages
* pagesize
;
134 #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
135 { /* This works on irix6. */
136 struct rminfo realmem
;
137 if (sysmp (MP_SAGET
, MPSA_RMINFO
, &realmem
, sizeof realmem
) == 0)
139 double pagesize
= sysconf (_SC_PAGESIZE
);
140 double pages
= realmem
.physmem
;
141 if (0 <= pages
&& 0 <= pagesize
)
142 return pages
* pagesize
;
147 #if HAVE_GETSYSINFO && defined GSI_PHYSMEM
148 { /* This works on Tru64 UNIX V4/5. */
151 if (getsysinfo (GSI_PHYSMEM
, (caddr_t
) &physmem
, sizeof (physmem
),
152 NULL
, NULL
, NULL
) == 1)
154 double kbytes
= physmem
;
157 return kbytes
* 1024.0;
162 #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_PHYSMEM
163 { /* This works on *bsd, kfreebsd-gnu, and darwin. */
164 unsigned int physmem
;
165 size_t len
= sizeof physmem
;
166 static int mib
[2] = { CTL_HW
, HW_PHYSMEM
};
168 if (sysctl (mib
, ARRAY_SIZE (mib
), &physmem
, &len
, NULL
, 0) == 0
169 && len
== sizeof (physmem
))
170 return (double) physmem
;
174 #if HAVE__SYSTEM_CONFIGURATION
175 /* This works on AIX. */
176 return _system_configuration
.physmem
;
180 { /* this works on windows */
182 HMODULE h
= GetModuleHandle ("kernel32.dll");
187 /* Use GlobalMemoryStatusEx if available. */
188 if ((pfnex
= (PFN_MS_EX
) GetProcAddress (h
, "GlobalMemoryStatusEx")))
190 lMEMORYSTATUSEX lms_ex
;
191 lms_ex
.dwLength
= sizeof lms_ex
;
192 if (!pfnex (&lms_ex
))
194 return (double) lms_ex
.ullTotalPhys
;
197 /* Fall back to GlobalMemoryStatus which is always available.
198 but returns wrong results for physical memory > 4GB. */
202 GlobalMemoryStatus (&ms
);
203 return (double) ms
.dwTotalPhys
;
208 /* Guess 64 MB. It's probably an older host, so guess small. */
209 return 64 * 1024 * 1024;
212 #if defined __linux__
214 /* Get the amount of free memory and of inactive file cache memory, and
215 return 0. Upon failure, return -1. */
217 get_meminfo (unsigned long long *mem_free_p
,
218 unsigned long long *mem_inactive_file_p
)
220 /* While the sysinfo() system call returns mem_total, mem_free, and a few
221 other numbers, the only way to get mem_inactive_file is by reading
223 int fd
= open ("/proc/meminfo", O_RDONLY
);
227 size_t buf_size
= full_read (fd
, buf
, sizeof (buf
));
231 char *buf_end
= buf
+ buf_size
;
232 unsigned long long mem_free
= 0;
233 unsigned long long mem_inactive_file
= 0;
235 /* Iterate through the lines. */
240 for (p
= line
; p
< buf_end
; p
++)
246 if (sscanf (line
, "MemFree: %llu kB", &mem_free
) == 1)
250 if (sscanf (line
, "Inactive(file): %llu kB", &mem_inactive_file
) == 1)
252 mem_inactive_file
*= 1024;
256 if (mem_free
> 0 && mem_inactive_file
> 0)
258 *mem_free_p
= mem_free
;
259 *mem_inactive_file_p
= mem_inactive_file
;
269 /* Return the amount of physical memory that can be claimed, with a given
272 physmem_claimable (double aggressivity
)
274 #if defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE
275 # if defined __linux__
276 /* On Linux, sysconf (_SC_AVPHYS_PAGES) returns the amount of "free" memory.
277 The Linux memory management system attempts to keep only a small amount
278 of memory (something like 5% to 10%) as free, because memory is better
279 used in the file cache.
280 We compute the "claimable" memory as
281 (free memory) + aggressivity * (inactive memory in the file cache). */
282 if (aggressivity
> 0.0)
284 unsigned long long mem_free
;
285 unsigned long long mem_inactive_file
;
286 if (get_meminfo (&mem_free
, &mem_inactive_file
) == 0)
287 return (double) mem_free
+ aggressivity
* (double) mem_inactive_file
;
290 { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin. */
291 double pages
= sysconf (_SC_AVPHYS_PAGES
);
292 double pagesize
= sysconf (_SC_PAGESIZE
);
293 if (0 <= pages
&& 0 <= pagesize
)
294 return pages
* pagesize
;
298 #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
299 { /* This works on linux. */
301 if (sysinfo(&si
) == 0)
302 return ((double) si
.freeram
+ si
.bufferram
) * si
.mem_unit
;
306 #if HAVE_PSTAT_GETSTATIC && HAVE_PSTAT_GETDYNAMIC
307 { /* This works on hpux11. */
308 struct pst_static pss
;
309 struct pst_dynamic psd
;
310 if (0 <= pstat_getstatic (&pss
, sizeof pss
, 1, 0)
311 && 0 <= pstat_getdynamic (&psd
, sizeof psd
, 1, 0))
313 double pages
= psd
.psd_free
;
314 double pagesize
= pss
.page_size
;
315 if (0 <= pages
&& 0 <= pagesize
)
316 return pages
* pagesize
;
321 #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
322 { /* This works on irix6. */
323 struct rminfo realmem
;
324 if (sysmp (MP_SAGET
, MPSA_RMINFO
, &realmem
, sizeof realmem
) == 0)
326 double pagesize
= sysconf (_SC_PAGESIZE
);
327 double pages
= realmem
.availrmem
;
328 if (0 <= pages
&& 0 <= pagesize
)
329 return pages
* pagesize
;
334 #if HAVE_TABLE && defined TBL_VMSTATS
335 { /* This works on Tru64 UNIX V4/5. */
336 struct tbl_vmstats vmstats
;
338 if (table (TBL_VMSTATS
, 0, &vmstats
, 1, sizeof (vmstats
)) == 1)
340 double pages
= vmstats
.free_count
;
341 double pagesize
= vmstats
.pagesize
;
343 if (0 <= pages
&& 0 <= pagesize
)
344 return pages
* pagesize
;
349 #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_USERMEM
350 { /* This works on *bsd, kfreebsd-gnu, and darwin. */
351 unsigned int usermem
;
352 size_t len
= sizeof usermem
;
353 static int mib
[2] = { CTL_HW
, HW_USERMEM
};
355 if (sysctl (mib
, ARRAY_SIZE (mib
), &usermem
, &len
, NULL
, 0) == 0
356 && len
== sizeof (usermem
))
357 return (double) usermem
;
362 { /* this works on windows */
364 HMODULE h
= GetModuleHandle ("kernel32.dll");
369 /* Use GlobalMemoryStatusEx if available. */
370 if ((pfnex
= (PFN_MS_EX
) GetProcAddress (h
, "GlobalMemoryStatusEx")))
372 lMEMORYSTATUSEX lms_ex
;
373 lms_ex
.dwLength
= sizeof lms_ex
;
374 if (!pfnex (&lms_ex
))
376 return (double) lms_ex
.ullAvailPhys
;
379 /* Fall back to GlobalMemoryStatus which is always available.
380 but returns wrong results for physical memory > 4GB */
384 GlobalMemoryStatus (&ms
);
385 return (double) ms
.dwAvailPhys
;
390 /* Guess 25% of physical memory. */
391 return physmem_total () / 4;
394 /* Return the amount of physical memory available. */
396 physmem_available (void)
398 return physmem_claimable (0.0);
409 printf ("%12.f %12.f\n", physmem_total (), physmem_available ());
417 compile-command: "gcc -DDEBUG -g -O -Wall -W physmem.c"