busybox/procps/free.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini free implementation for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 *
   7 * Licensed under GPLv2, see file LICENSE in this source tree.
   8 */
   9//config:config FREE
  10//config:       bool "free (3.1 kb)"
  11//config:       default y
  12//config:       help
  13//config:       free displays the total amount of free and used physical and swap
  14//config:       memory in the system, as well as the buffers used by the kernel.
  15//config:       The shared memory column should be ignored; it is obsolete.
  16
  17//applet:IF_FREE(APPLET_NOFORK(free, free, BB_DIR_USR_BIN, BB_SUID_DROP, free))
  18
  19//kbuild:lib-$(CONFIG_FREE) += free.o
  20
  21//usage:#define free_trivial_usage
  22//usage:       "" IF_DESKTOP("[-b/k/m/g]")
  23//usage:#define free_full_usage "\n\n"
  24//usage:       "Display the amount of free and used system memory"
  25//usage:
  26//usage:#define free_example_usage
  27//usage:       "$ free\n"
  28//usage:       "              total         used         free       shared      buffers\n"
  29//usage:       "  Mem:       257628       248724         8904        59644        93124\n"
  30//usage:       " Swap:       128516         8404       120112\n"
  31//usage:       "Total:       386144       257128       129016\n"
  32
  33#include "libbb.h"
  34#ifdef __linux__
  35# include <sys/sysinfo.h>
  36#endif
  37
  38struct globals {
  39        unsigned mem_unit;
  40#if ENABLE_DESKTOP
  41        uint8_t unit_steps;
  42# define G_unit_steps g->unit_steps
  43#else
  44# define G_unit_steps 10
  45#endif
  46        unsigned long cached_kb, available_kb, reclaimable_kb;
  47};
  48/* Because of NOFORK, "globals" are not in global data */
  49
  50static unsigned long long scale(struct globals *g, unsigned long d)
  51{
  52        return ((unsigned long long)d * g->mem_unit) >> G_unit_steps;
  53}
  54
  55/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */
  56static NOINLINE unsigned int parse_meminfo(struct globals *g)
  57{
  58        char buf[60]; /* actual lines we expect are ~30 chars or less */
  59        FILE *fp;
  60        int seen_cached_and_available_and_reclaimable;
  61
  62        fp = xfopen_for_read("/proc/meminfo");
  63        g->cached_kb = g->available_kb = g->reclaimable_kb = 0;
  64        seen_cached_and_available_and_reclaimable = 3;
  65        while (fgets(buf, sizeof(buf), fp)) {
  66                if (sscanf(buf, "Cached: %lu %*s\n", &g->cached_kb) == 1)
  67                        if (--seen_cached_and_available_and_reclaimable == 0)
  68                                break;
  69                if (sscanf(buf, "MemAvailable: %lu %*s\n", &g->available_kb) == 1)
  70                        if (--seen_cached_and_available_and_reclaimable == 0)
  71                                break;
  72                if (sscanf(buf, "SReclaimable: %lu %*s\n", &g->reclaimable_kb) == 1)
  73                        if (--seen_cached_and_available_and_reclaimable == 0)
  74                                break;
  75        }
  76        /* Have to close because of NOFORK */
  77        fclose(fp);
  78
  79        return seen_cached_and_available_and_reclaimable == 0;
  80}
  81
  82int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  83int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
  84{
  85        struct globals G;
  86        struct sysinfo info;
  87        unsigned long long cached, cached_plus_free, available;
  88        int seen_available;
  89
  90#if ENABLE_DESKTOP
  91        G.unit_steps = 10;
  92        if (argv[1] && argv[1][0] == '-') {
  93                switch (argv[1][1]) {
  94                case 'b':
  95                        G.unit_steps = 0;
  96                        break;
  97                case 'k': /* 2^10 */
  98                        /* G.unit_steps = 10; - already is */
  99                        break;
 100                case 'm': /* 2^20 */
 101                        G.unit_steps = 20;
 102                        break;
 103                case 'g': /* 2^30 */
 104                        G.unit_steps = 30;
 105                        break;
 106                default:
 107                        bb_show_usage();
 108                }
 109        }
 110#endif
 111        printf("       %12s%12s%12s%12s%12s%12s\n"
 112        "Mem:   ",
 113                "total",
 114                "used",
 115                "free",
 116                "shared", "buff/cache", "available" /* swap and total don't have these columns */
 117        );
 118
 119        sysinfo(&info);
 120        /* Extract cached and memavailable from /proc/meminfo and convert to mem_units */
 121        seen_available = parse_meminfo(&G);
 122        G.mem_unit = (info.mem_unit ? info.mem_unit : 1); /* kernels < 2.4.x return mem_unit==0, so cope */
 123        available = ((unsigned long long) G.available_kb * 1024) / G.mem_unit;
 124        cached = ((unsigned long long) G.cached_kb * 1024) / G.mem_unit;
 125        cached += info.bufferram;
 126        cached += ((unsigned long long) G.reclaimable_kb * 1024) / G.mem_unit;
 127        cached_plus_free = cached + info.freeram;
 128
 129/* In case (long long * G.mem_unit) can overflow, this can be used to reduce the chances */
 130#if 0 //ENABLE_DESKTOP
 131        while (!(G.mem_unit & 1) && G.unit_steps != 0) {
 132                G.mem_unit >>= 1;
 133                G.unit_steps--;
 134                //bb_error_msg("mem_unit:%d unit_steps:%d", G.mem_unit, G.unit_steps);
 135        }
 136#endif
 137
 138#define FIELDS_6 "%12llu %11llu %11llu %11llu %11llu %11llu\n"
 139#define FIELDS_3 (FIELDS_6 + 6 + 7 + 7)
 140#define FIELDS_2 (FIELDS_6 + 6 + 7 + 7 + 7)
 141
 142        printf(FIELDS_6,
 143                scale(&G, info.totalram),                //total
 144                scale(&G, info.totalram - cached_plus_free), //used
 145                scale(&G, info.freeram),                 //free
 146                scale(&G, info.sharedram),               //shared
 147                scale(&G, cached),                       //buff/cache
 148                scale(&G, available)                     //available
 149        );
 150        /* On kernels < 3.14, MemAvailable is not provided.
 151         * Show alternate, more meaningful busy/free numbers by counting
 152         * buffer cache as free memory. */
 153        if (!seen_available) {
 154                printf("-/+ buffers/cache: ");
 155                printf(FIELDS_2,
 156                        scale(&G, info.totalram - cached_plus_free), //used
 157                        scale(&G, cached_plus_free)                  //free
 158                );
 159        }
 160#if BB_MMU
 161        printf("Swap:  ");
 162        printf(FIELDS_3,
 163                scale(&G, info.totalswap),                 //total
 164                scale(&G, info.totalswap - info.freeswap), //used
 165                scale(&G, info.freeswap)                   //free
 166        );
 167#endif
 168        return EXIT_SUCCESS;
 169}
 170