| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| /// \file tuklib_physmem.c | |
| /// \brief Get the amount of physical memory | |
| // | |
| // Author: Lasse Collin | |
| // | |
| // This file has been put into the public domain. | |
| // You can do whatever you want with this file. | |
| // | |
| /////////////////////////////////////////////////////////////////////////////// | |
| #include "tuklib_physmem.h" | |
| // We want to use Windows-specific code on Cygwin, which also has memory | |
| // information available via sysconf(), but on Cygwin 1.5 and older it | |
| // gives wrong results (from our point of view). | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| # ifndef _WIN32_WINNT | |
| # define _WIN32_WINNT 0x0500 | |
| # endif | |
| # include <windows.h> | |
| #elif defined(__OS2__) | |
| # define INCL_DOSMISC | |
| # include <os2.h> | |
| #elif defined(__DJGPP__) | |
| # include <dpmi.h> | |
| #elif defined(__VMS) | |
| # include <lib$routines.h> | |
| # include <syidef.h> | |
| # include <ssdef.h> | |
| // AIX | |
| #elif defined(TUKLIB_PHYSMEM_AIX) | |
| # include <sys/systemcfg.h> | |
| #elif defined(TUKLIB_PHYSMEM_SYSCONF) | |
| # include <unistd.h> | |
| #elif defined(TUKLIB_PHYSMEM_SYSCTL) | |
| # ifdef HAVE_SYS_PARAM_H | |
| # include <sys/param.h> | |
| # endif | |
| # include <sys/sysctl.h> | |
| // Tru64 | |
| #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) | |
| # include <sys/sysinfo.h> | |
| # include <machine/hal_sysinfo.h> | |
| // HP-UX | |
| #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) | |
| # include <sys/param.h> | |
| # include <sys/pstat.h> | |
| // IRIX | |
| #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) | |
| # include <invent.h> | |
| // This sysinfo() is Linux-specific. | |
| #elif defined(TUKLIB_PHYSMEM_SYSINFO) | |
| # include <sys/sysinfo.h> | |
| #endif | |
| extern uint64_t | |
| tuklib_physmem(void) | |
| { | |
| uint64_t ret = 0; | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| if ((GetVersion() & 0xFF) >= 5) { | |
| // Windows 2000 and later have GlobalMemoryStatusEx() which | |
| // supports reporting values greater than 4 GiB. To keep the | |
| // code working also on older Windows versions, use | |
| // GlobalMemoryStatusEx() conditionally. | |
| HMODULE kernel32 = GetModuleHandle("kernel32.dll"); | |
| if (kernel32 != NULL) { | |
| BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress( | |
| kernel32, "GlobalMemoryStatusEx"); | |
| if (gmse != NULL) { | |
| MEMORYSTATUSEX meminfo; | |
| meminfo.dwLength = sizeof(meminfo); | |
| if (gmse(&meminfo)) | |
| ret = meminfo.ullTotalPhys; | |
| } | |
| } | |
| } | |
| if (ret == 0) { | |
| // GlobalMemoryStatus() is supported by Windows 95 and later, | |
| // so it is fine to link against it unconditionally. Note that | |
| // GlobalMemoryStatus() has no return value. | |
| MEMORYSTATUS meminfo; | |
| meminfo.dwLength = sizeof(meminfo); | |
| GlobalMemoryStatus(&meminfo); | |
| ret = meminfo.dwTotalPhys; | |
| } | |
| #elif defined(__OS2__) | |
| unsigned long mem; | |
| if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, | |
| &mem, sizeof(mem)) == 0) | |
| ret = mem; | |
| #elif defined(__DJGPP__) | |
| __dpmi_free_mem_info meminfo; | |
| if (__dpmi_get_free_memory_information(&meminfo) == 0 | |
| && meminfo.total_number_of_physical_pages | |
| != (unsigned long)-1) | |
| ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; | |
| #elif defined(__VMS) | |
| int vms_mem; | |
| int val = SYI$_MEMSIZE; | |
| if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) | |
| ret = (uint64_t)vms_mem * 8192; | |
| #elif defined(TUKLIB_PHYSMEM_AIX) | |
| ret = _system_configuration.physmem; | |
| #elif defined(TUKLIB_PHYSMEM_SYSCONF) | |
| const long pagesize = sysconf(_SC_PAGESIZE); | |
| const long pages = sysconf(_SC_PHYS_PAGES); | |
| if (pagesize != -1 && pages != -1) | |
| // According to docs, pagesize * pages can overflow. | |
| // Simple case is 32-bit box with 4 GiB or more RAM, | |
| // which may report exactly 4 GiB of RAM, and "long" | |
| // being 32-bit will overflow. Casting to uint64_t | |
| // hopefully avoids overflows in the near future. | |
| ret = (uint64_t)pagesize * (uint64_t)pages; | |
| #elif defined(TUKLIB_PHYSMEM_SYSCTL) | |
| int name[2] = { | |
| CTL_HW, | |
| #ifdef HW_PHYSMEM64 | |
| HW_PHYSMEM64 | |
| #else | |
| HW_PHYSMEM | |
| #endif | |
| }; | |
| union { | |
| uint32_t u32; | |
| uint64_t u64; | |
| } mem; | |
| size_t mem_ptr_size = sizeof(mem.u64); | |
| if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { | |
| // IIRC, 64-bit "return value" is possible on some 64-bit | |
| // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), | |
| // so support both. | |
| if (mem_ptr_size == sizeof(mem.u64)) | |
| ret = mem.u64; | |
| else if (mem_ptr_size == sizeof(mem.u32)) | |
| ret = mem.u32; | |
| } | |
| #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) | |
| // Docs are unclear if "start" is needed, but it doesn't hurt | |
| // much to have it. | |
| int memkb; | |
| int start = 0; | |
| if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) | |
| != -1) | |
| ret = (uint64_t)memkb * 1024; | |
| #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) | |
| struct pst_static pst; | |
| if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) | |
| ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; | |
| #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) | |
| inv_state_t *st = NULL; | |
| if (setinvent_r(&st) != -1) { | |
| inventory_t *i; | |
| while ((i = getinvent_r(st)) != NULL) { | |
| if (i->inv_class == INV_MEMORY | |
| && i->inv_type == INV_MAIN_MB) { | |
| ret = (uint64_t)i->inv_state << 20; | |
| break; | |
| } | |
| } | |
| endinvent_r(st); | |
| } | |
| #elif defined(TUKLIB_PHYSMEM_SYSINFO) | |
| struct sysinfo si; | |
| if (sysinfo(&si) == 0) | |
| ret = (uint64_t)si.totalram * si.mem_unit; | |
| #endif | |
| return ret; | |
| } |