(root)/
coreutils-9.4/
lib/
physmem.c
       1  /* Calculate the size of physical memory.
       2  
       3     Copyright (C) 2000-2001, 2003, 2005-2006, 2009-2023 Free Software
       4     Foundation, Inc.
       5  
       6     This file is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU Lesser General Public License as
       8     published by the Free Software Foundation; either version 2.1 of the
       9     License, or (at your option) any later version.
      10  
      11     This file is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Written by Paul Eggert.  */
      20  
      21  #include <config.h>
      22  
      23  #include "physmem.h"
      24  
      25  #include <unistd.h>
      26  
      27  #if HAVE_SYS_PSTAT_H
      28  # include <sys/pstat.h>
      29  #endif
      30  
      31  #if HAVE_SYS_SYSMP_H
      32  # include <sys/sysmp.h>
      33  #endif
      34  
      35  #if HAVE_SYS_SYSINFO_H
      36  # include <sys/sysinfo.h>
      37  #endif
      38  
      39  #if HAVE_MACHINE_HAL_SYSINFO_H
      40  # include <machine/hal_sysinfo.h>
      41  #endif
      42  
      43  #if HAVE_SYS_TABLE_H
      44  # include <sys/table.h>
      45  #endif
      46  
      47  #include <sys/types.h>
      48  
      49  #if HAVE_SYS_PARAM_H
      50  # include <sys/param.h>
      51  #endif
      52  
      53  #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__)
      54  # include <sys/sysctl.h>
      55  #endif
      56  
      57  #if HAVE_SYS_SYSTEMCFG_H
      58  # include <sys/systemcfg.h>
      59  #endif
      60  
      61  #ifdef _WIN32
      62  
      63  # define WIN32_LEAN_AND_MEAN
      64  # include <windows.h>
      65  
      66  /* Don't assume that UNICODE is not defined.  */
      67  # undef GetModuleHandle
      68  # define GetModuleHandle GetModuleHandleA
      69  
      70  /* Avoid warnings from gcc -Wcast-function-type.  */
      71  # define GetProcAddress \
      72     (void *) GetProcAddress
      73  
      74  /*  MEMORYSTATUSEX is missing from older windows headers, so define
      75      a local replacement.  */
      76  typedef struct
      77  {
      78    DWORD dwLength;
      79    DWORD dwMemoryLoad;
      80    DWORDLONG ullTotalPhys;
      81    DWORDLONG ullAvailPhys;
      82    DWORDLONG ullTotalPageFile;
      83    DWORDLONG ullAvailPageFile;
      84    DWORDLONG ullTotalVirtual;
      85    DWORDLONG ullAvailVirtual;
      86    DWORDLONG ullAvailExtendedVirtual;
      87  } lMEMORYSTATUSEX;
      88  typedef BOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
      89  
      90  #endif
      91  
      92  #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
      93  
      94  /* Return the total amount of physical memory.  */
      95  double
      96  physmem_total (void)
      97  {
      98  #if defined _SC_PHYS_PAGES && defined _SC_PAGESIZE
      99    { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin.  */
     100      double pages = sysconf (_SC_PHYS_PAGES);
     101      double pagesize = sysconf (_SC_PAGESIZE);
     102      if (0 <= pages && 0 <= pagesize)
     103        return pages * pagesize;
     104    }
     105  #endif
     106  
     107  #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
     108    { /* This works on linux.  */
     109      struct sysinfo si;
     110      if (sysinfo(&si) == 0)
     111        return (double) si.totalram * si.mem_unit;
     112    }
     113  #endif
     114  
     115  #if HAVE_PSTAT_GETSTATIC
     116    { /* This works on hpux11.  */
     117      struct pst_static pss;
     118      if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0))
     119        {
     120          double pages = pss.physical_memory;
     121          double pagesize = pss.page_size;
     122          if (0 <= pages && 0 <= pagesize)
     123            return pages * pagesize;
     124        }
     125    }
     126  #endif
     127  
     128  #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
     129    { /* This works on irix6. */
     130      struct rminfo realmem;
     131      if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
     132        {
     133          double pagesize = sysconf (_SC_PAGESIZE);
     134          double pages = realmem.physmem;
     135          if (0 <= pages && 0 <= pagesize)
     136            return pages * pagesize;
     137        }
     138    }
     139  #endif
     140  
     141  #if HAVE_GETSYSINFO && defined GSI_PHYSMEM
     142    { /* This works on Tru64 UNIX V4/5.  */
     143      int physmem;
     144  
     145      if (getsysinfo (GSI_PHYSMEM, (caddr_t) &physmem, sizeof (physmem),
     146                      NULL, NULL, NULL) == 1)
     147        {
     148          double kbytes = physmem;
     149  
     150          if (0 <= kbytes)
     151            return kbytes * 1024.0;
     152        }
     153    }
     154  #endif
     155  
     156  #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_PHYSMEM
     157    { /* This works on *bsd, kfreebsd-gnu, and darwin.  */
     158      unsigned int physmem;
     159      size_t len = sizeof physmem;
     160      static int mib[2] = { CTL_HW, HW_PHYSMEM };
     161  
     162      if (sysctl (mib, ARRAY_SIZE (mib), &physmem, &len, NULL, 0) == 0
     163          && len == sizeof (physmem))
     164        return (double) physmem;
     165    }
     166  #endif
     167  
     168  #if HAVE__SYSTEM_CONFIGURATION
     169    /* This works on AIX.  */
     170    return _system_configuration.physmem;
     171  #endif
     172  
     173  #if defined _WIN32
     174    { /* this works on windows */
     175      PFN_MS_EX pfnex;
     176      HMODULE h = GetModuleHandle ("kernel32.dll");
     177  
     178      if (!h)
     179        return 0.0;
     180  
     181      /*  Use GlobalMemoryStatusEx if available.  */
     182      if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
     183        {
     184          lMEMORYSTATUSEX lms_ex;
     185          lms_ex.dwLength = sizeof lms_ex;
     186          if (!pfnex (&lms_ex))
     187            return 0.0;
     188          return (double) lms_ex.ullTotalPhys;
     189        }
     190  
     191      /*  Fall back to GlobalMemoryStatus which is always available.
     192          but returns wrong results for physical memory > 4GB.  */
     193      else
     194        {
     195          MEMORYSTATUS ms;
     196          GlobalMemoryStatus (&ms);
     197          return (double) ms.dwTotalPhys;
     198        }
     199    }
     200  #endif
     201  
     202    /* Guess 64 MB.  It's probably an older host, so guess small.  */
     203    return 64 * 1024 * 1024;
     204  }
     205  
     206  /* Return the amount of physical memory available.  */
     207  double
     208  physmem_available (void)
     209  {
     210  #if defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE
     211    { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin.  */
     212      double pages = sysconf (_SC_AVPHYS_PAGES);
     213      double pagesize = sysconf (_SC_PAGESIZE);
     214      if (0 <= pages && 0 <= pagesize)
     215        return pages * pagesize;
     216    }
     217  #endif
     218  
     219  #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
     220    { /* This works on linux.  */
     221      struct sysinfo si;
     222      if (sysinfo(&si) == 0)
     223        return ((double) si.freeram + si.bufferram) * si.mem_unit;
     224    }
     225  #endif
     226  
     227  #if HAVE_PSTAT_GETSTATIC && HAVE_PSTAT_GETDYNAMIC
     228    { /* This works on hpux11.  */
     229      struct pst_static pss;
     230      struct pst_dynamic psd;
     231      if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0)
     232          && 0 <= pstat_getdynamic (&psd, sizeof psd, 1, 0))
     233        {
     234          double pages = psd.psd_free;
     235          double pagesize = pss.page_size;
     236          if (0 <= pages && 0 <= pagesize)
     237            return pages * pagesize;
     238        }
     239    }
     240  #endif
     241  
     242  #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
     243    { /* This works on irix6. */
     244      struct rminfo realmem;
     245      if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
     246        {
     247          double pagesize = sysconf (_SC_PAGESIZE);
     248          double pages = realmem.availrmem;
     249          if (0 <= pages && 0 <= pagesize)
     250            return pages * pagesize;
     251        }
     252    }
     253  #endif
     254  
     255  #if HAVE_TABLE && defined TBL_VMSTATS
     256    { /* This works on Tru64 UNIX V4/5.  */
     257      struct tbl_vmstats vmstats;
     258  
     259      if (table (TBL_VMSTATS, 0, &vmstats, 1, sizeof (vmstats)) == 1)
     260        {
     261          double pages = vmstats.free_count;
     262          double pagesize = vmstats.pagesize;
     263  
     264          if (0 <= pages && 0 <= pagesize)
     265            return pages * pagesize;
     266        }
     267    }
     268  #endif
     269  
     270  #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_USERMEM
     271    { /* This works on *bsd, kfreebsd-gnu, and darwin.  */
     272      unsigned int usermem;
     273      size_t len = sizeof usermem;
     274      static int mib[2] = { CTL_HW, HW_USERMEM };
     275  
     276      if (sysctl (mib, ARRAY_SIZE (mib), &usermem, &len, NULL, 0) == 0
     277          && len == sizeof (usermem))
     278        return (double) usermem;
     279    }
     280  #endif
     281  
     282  #if defined _WIN32
     283    { /* this works on windows */
     284      PFN_MS_EX pfnex;
     285      HMODULE h = GetModuleHandle ("kernel32.dll");
     286  
     287      if (!h)
     288        return 0.0;
     289  
     290      /*  Use GlobalMemoryStatusEx if available.  */
     291      if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
     292        {
     293          lMEMORYSTATUSEX lms_ex;
     294          lms_ex.dwLength = sizeof lms_ex;
     295          if (!pfnex (&lms_ex))
     296            return 0.0;
     297          return (double) lms_ex.ullAvailPhys;
     298        }
     299  
     300      /*  Fall back to GlobalMemoryStatus which is always available.
     301          but returns wrong results for physical memory > 4GB  */
     302      else
     303        {
     304          MEMORYSTATUS ms;
     305          GlobalMemoryStatus (&ms);
     306          return (double) ms.dwAvailPhys;
     307        }
     308    }
     309  #endif
     310  
     311    /* Guess 25% of physical memory.  */
     312    return physmem_total () / 4;
     313  }
     314  
     315  
     316  #if DEBUG
     317  
     318  # include <stdio.h>
     319  # include <stdlib.h>
     320  
     321  int
     322  main (void)
     323  {
     324    printf ("%12.f %12.f\n", physmem_total (), physmem_available ());
     325    exit (0);
     326  }
     327  
     328  #endif /* DEBUG */
     329  
     330  /*
     331  Local Variables:
     332  compile-command: "gcc -DDEBUG -g -O -Wall -W physmem.c"
     333  End:
     334  */