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