tuklib_physmem.c revision 278433
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 73extern uint64_t 74tuklib_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