1/////////////////////////////////////////////////////////////////////////////// 2// 3/// \file tuklib_physmem.c 4/// \brief Get the amount of physical memory 5// 6// Author: Lasse Collin 7// 8// This file has been put into the public domain. 9// You can do whatever you want with this file. 10// 11/////////////////////////////////////////////////////////////////////////////// 12 13#include "tuklib_physmem.h" 14 15// We want to use Windows-specific code on Cygwin, which also has memory 16// information available via sysconf(), but on Cygwin 1.5 and older it 17// gives wrong results (from our point of view). 18#if defined(_WIN32) || defined(__CYGWIN__) 19# ifndef _WIN32_WINNT 20# define _WIN32_WINNT 0x0500 21# endif 22# include <windows.h> 23 24#elif defined(__OS2__) 25# define INCL_DOSMISC 26# include <os2.h> 27 28#elif defined(__DJGPP__) 29# include <dpmi.h> 30 31#elif defined(__VMS) 32# include <lib$routines.h> 33# include <syidef.h> 34# include <ssdef.h> 35 36// AIX 37#elif defined(TUKLIB_PHYSMEM_AIX) 38# include <sys/systemcfg.h> 39 40#elif defined(TUKLIB_PHYSMEM_SYSCONF) 41# include <unistd.h> 42 43#elif defined(TUKLIB_PHYSMEM_SYSCTL) 44# ifdef HAVE_SYS_PARAM_H 45# include <sys/param.h> 46# endif 47# include <sys/sysctl.h> 48 49// Tru64 50#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 51# include <sys/sysinfo.h> 52# include <machine/hal_sysinfo.h> 53 54// HP-UX 55#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 56# include <sys/param.h> 57# include <sys/pstat.h> 58 59// IRIX 60#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 61# include <invent.h> 62 63// This sysinfo() is Linux-specific. 64#elif defined(TUKLIB_PHYSMEM_SYSINFO) 65# include <sys/sysinfo.h> 66#endif 67 68 69extern uint64_t 70tuklib_physmem(void) 71{ 72 uint64_t ret = 0; 73 74#if defined(_WIN32) || defined(__CYGWIN__) 75 if ((GetVersion() & 0xFF) >= 5) { 76 // Windows 2000 and later have GlobalMemoryStatusEx() which 77 // supports reporting values greater than 4 GiB. To keep the 78 // code working also on older Windows versions, use 79 // GlobalMemoryStatusEx() conditionally. 80 HMODULE kernel32 = GetModuleHandle("kernel32.dll"); 81 if (kernel32 != NULL) { 82 BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress( 83 kernel32, "GlobalMemoryStatusEx"); 84 if (gmse != NULL) { 85 MEMORYSTATUSEX meminfo; 86 meminfo.dwLength = sizeof(meminfo); 87 if (gmse(&meminfo)) 88 ret = meminfo.ullTotalPhys; 89 } 90 } 91 } 92 93 if (ret == 0) { 94 // GlobalMemoryStatus() is supported by Windows 95 and later, 95 // so it is fine to link against it unconditionally. Note that 96 // GlobalMemoryStatus() has no return value. 97 MEMORYSTATUS meminfo; 98 meminfo.dwLength = sizeof(meminfo); 99 GlobalMemoryStatus(&meminfo); 100 ret = meminfo.dwTotalPhys; 101 } 102 103#elif defined(__OS2__) 104 unsigned long mem; 105 if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 106 &mem, sizeof(mem)) == 0) 107 ret = mem; 108 109#elif defined(__DJGPP__) 110 __dpmi_free_mem_info meminfo; 111 if (__dpmi_get_free_memory_information(&meminfo) == 0 112 && meminfo.total_number_of_physical_pages 113 != (unsigned long)-1) 114 ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; 115 116#elif defined(__VMS) 117 int vms_mem; 118 int val = SYI$_MEMSIZE; 119 if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) 120 ret = (uint64_t)vms_mem * 8192; 121 122#elif defined(TUKLIB_PHYSMEM_AIX) 123 ret = _system_configuration.physmem; 124 125#elif defined(TUKLIB_PHYSMEM_SYSCONF) 126 const long pagesize = sysconf(_SC_PAGESIZE); 127 const long pages = sysconf(_SC_PHYS_PAGES); 128 if (pagesize != -1 && pages != -1) 129 // According to docs, pagesize * pages can overflow. 130 // Simple case is 32-bit box with 4 GiB or more RAM, 131 // which may report exactly 4 GiB of RAM, and "long" 132 // being 32-bit will overflow. Casting to uint64_t 133 // hopefully avoids overflows in the near future. 134 ret = (uint64_t)pagesize * (uint64_t)pages; 135 136#elif defined(TUKLIB_PHYSMEM_SYSCTL) 137 int name[2] = { 138 CTL_HW, 139#ifdef HW_PHYSMEM64 140 HW_PHYSMEM64 141#else 142 HW_PHYSMEM 143#endif 144 }; 145 union { 146 uint32_t u32; 147 uint64_t u64; 148 } mem; 149 size_t mem_ptr_size = sizeof(mem.u64); 150 if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { 151 // IIRC, 64-bit "return value" is possible on some 64-bit 152 // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), 153 // so support both. 154 if (mem_ptr_size == sizeof(mem.u64)) 155 ret = mem.u64; 156 else if (mem_ptr_size == sizeof(mem.u32)) 157 ret = mem.u32; 158 } 159 160#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 161 // Docs are unclear if "start" is needed, but it doesn't hurt 162 // much to have it. 163 int memkb; 164 int start = 0; 165 if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 166 != -1) 167 ret = (uint64_t)memkb * 1024; 168 169#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 170 struct pst_static pst; 171 if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) 172 ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; 173 174#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 175 inv_state_t *st = NULL; 176 if (setinvent_r(&st) != -1) { 177 inventory_t *i; 178 while ((i = getinvent_r(st)) != NULL) { 179 if (i->inv_class == INV_MEMORY 180 && i->inv_type == INV_MAIN_MB) { 181 ret = (uint64_t)i->inv_state << 20; 182 break; 183 } 184 } 185 186 endinvent_r(st); 187 } 188 189#elif defined(TUKLIB_PHYSMEM_SYSINFO) 190 struct sysinfo si; 191 if (sysinfo(&si) == 0) 192 ret = (uint64_t)si.totalram * si.mem_unit; 193#endif 194 195 return ret; 196} 197