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