From: git Date: Sun, 24 May 2026 12:59:48 +0000 (-0400) Subject: initial public commit X-Git-Url: https://git.datadissipation.net/?a=commitdiff_plain;h=9486f5946765dc462feaab9cfe1c209dae1c11f3;p=free.git initial public commit --- 9486f5946765dc462feaab9cfe1c209dae1c11f3 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..78e53b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +include config.mk + +TARGET = free +SRC = free.c + +all: ${TARGET} + +clean: + rm -f *.[oa] ${TARGET} + +install: all + mkdir -p ${PREFIX}/bin + cp -f ${TARGET} ${PREFIX}/bin + chmod 755 ${PREFIX}/bin/${TARGET} + mkdir -p ${MANPREFIX}/man1 + cp -f ${TARGET}.1 ${MANPREFIX}/man1 + chmod 644 ${MANPREFIX}/man1/${TARGET}.1 + +uninstall: + rm -f ${PREFIX}/bin/${TARGET} ${MANPREFIX}/man1/${TARGET}.1 + +.PHONY: all clean install uninstall diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..ed60c37 --- /dev/null +++ b/config.mk @@ -0,0 +1,7 @@ +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +CFLAGS = -Wall -Wextra -Wpedantic -O2 -lkvm +LDFLAGS = + +CC = cc diff --git a/free.1 b/free.1 new file mode 100644 index 0000000..b623262 --- /dev/null +++ b/free.1 @@ -0,0 +1,46 @@ +.Dd May 2026 +.Dt FREE 1 +.Os +.Sh NAME +.Nm free +.Nd display amount of free and used memory in the system +.Sh SYNOPSIS +.Nm +.Op Fl bghkmpt +.Sh DESCRIPTION +.Nm +displays the total amount of free and used physical and swap memory in the system, as +well as the buffers, caches (including ZFS ARC) and wired memory used by the kernel. +The displayed columns are: +.Bl -tag -width -indent +.It total +Total usable memory. +.It used +Unavailable memory. +.It free +Truly unallocated memory. +.It wired +Memory that can't be swapped to disk. +.It buff/cache +Memory used by kernel buffers, traditional FS cache and ARC. +.It available +Available memory. +.Sh OPTIONS +.Bl -tag -width ".Fl b " +.It Fl b +Display in bytes. +.It Fl k +Display in kibibytes. +.It Fl m +Display in mebibytes. +.It Fl g +Display in gibibytes. +.It Fl t +Display in tebibytes. +.It Fl p +Display in pebytes. +.It Fl h +Display in human-readable units. +.Sh SEE ALSO +.Xr top 1 , +.Xr sysctl 1 diff --git a/free.c b/free.c new file mode 100644 index 0000000..971711b --- /dev/null +++ b/free.c @@ -0,0 +1,233 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +typedef enum { + NO_CONV, + KB_CONV, + MB_CONV, + GB_CONV, + TB_CONV, + PB_CONV, + HU_CONV +} ConvFlags; + +static kvm_t *kd; +static long pgsz; +static char sbuff[32]; + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-bghkmpt]\n", getprogname()); + exit(2); +} + +long long +getphysmem(int unused) +{ + (void)unused; + + long long physcnt; + size_t len = sizeof(physcnt); + + if (sysctlbyname("hw.physmem", &physcnt, &len, NULL, 0) < 0) + err(1, "hw.physmem"); + + return physcnt; +} + +long long +getavailmem(int flag) +{ + size_t len; + unsigned int freecnt, inactcnt; + + len = sizeof(freecnt); + if (sysctlbyname("vm.stats.vm.v_free_count", &freecnt, &len, NULL, 0) < 0) + err(1, "vm.stats.vm.v_free_count"); + if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactcnt, &len, NULL, 0) < 0) + err(1, "vm.stats.vm.v_inactive_count"); + + if (flag == 1) + return (long long)(getphysmem(0) - (freecnt + inactcnt) * pgsz); + else if (flag == 2) + return (long long)(freecnt * pgsz); + else + return (long long)((freecnt + inactcnt) * pgsz); +} + +long long +getwiredmem(int unused) +{ + (void)unused; + + size_t len; + unsigned int wirecnt; + + len = sizeof(wirecnt); + if (sysctlbyname("vm.stats.vm.v_wire_count", &wirecnt, &len, NULL, 0) < 0) + err(1, "vm.stats.vm.v_wire_count"); + + return (long long)(wirecnt * pgsz); + +} + +long long +getbufcachestats(int unused) +{ + (void)unused; + + size_t len; + long bufspace, arcspace; + unsigned int cachecnt; + + len = sizeof(bufspace); + if (sysctlbyname("vfs.bufspace", &bufspace, &len, NULL, 0) < 0) + err(1, "vfs.bufspace"); + /* Ignore failure in case ZFS is not used */ + (void)sysctlbyname("kstat.zfs.misc.arcstats.size", &arcspace, &len, NULL, 0); + + len = sizeof(cachecnt); + if (sysctlbyname("vm.stats.vm.v_cache_count", &cachecnt, &len, NULL, 0) < 0) + err(1, "vm.stats.vm.v_inactive_count"); + + return (long long)(cachecnt * pgsz + bufspace + arcspace); +} + +long long +getswapstats(int flag) +{ + int nswap; + struct kvm_swap swap; + + if ((nswap = kvm_getswapinfo(kd, &swap, 1, 0)) < 0) + errx(1, "kvm_getswapinfo: %s", kvm_geterr(kd)); + + if (flag == 0) + return (long long)(swap.ksw_total * pgsz); + else if (flag == 1) + return (long long)(swap.ksw_used * pgsz); + else + return (long long)((swap.ksw_total - swap.ksw_used) * pgsz); +} + +int +tohumansize(long long bytes, char **buff, ConvFlags flag) +{ + int i = 0; + double size = (double)bytes; + char units[] = {'\0', 'K', 'M', 'G', 'T', 'P'}; + + if (flag != HU_CONV) + memset(units, '\0', sizeof(units)); + if (flag == NO_CONV) + return asprintf(buff, "%.0f", size); + + while (i < (int)flag) { + if (size <= 1024 && flag == HU_CONV) break; + size /= 1024; + i++; + } + + if (i == 0 || size < 0.1) + return asprintf(buff, "%.0f", size); + else if (size < 10) + return asprintf(buff, "%.1f%c", size, units[i]); + else + return asprintf(buff, "%.0f%c", size, units[i]); +} + +void +sprintfaligned(char *buff, size_t len) +{ + size_t blen; + char *ind; + + blen = strlen(buff); + ind = sbuff + len - blen; + if (ind < sbuff) { + size_t t = sbuff - ind; + ind = sbuff; + *ind = '%'; + ind++; buff += t; + blen = 30; + } + strlcpy(ind, buff, blen + 1); + + return; +} + +int +main(int argc, char *argv[]) +{ + int c, i; + char *buff; + size_t offset; + ConvFlags flags; + long long (*getmemstats[])(int) = + {getphysmem, getavailmem, getavailmem, getwiredmem, getbufcachestats, getavailmem}; + + flags = KB_CONV; + while ((c = getopt(argc, argv, "bghkmpt")) >= 0) { + switch(c) { + case 'b': + flags = NO_CONV; break; + case 'k': + flags = KB_CONV; break; + case 'm': + flags = MB_CONV; break; + case 'g': + flags = GB_CONV; break; + case 't': + flags = TB_CONV; break; + case 'p': + flags = PB_CONV; break; + case 'h': + flags = HU_CONV; break; + default: + usage(); + } + } + + pgsz = sysconf(_SC_PAGESIZE); + + /* initializing kvm device for swap stats */ + if (!(kd = kvm_open(NULL, "/dev/null", NULL, 0, "kvm_open"))) + err(1, "kvm_open"); + + printf("%16s%12s%12s%12s%12s%12s\nMem:", + "total", "used", "free", "wired", "buff/cache", "available"); + + offset = 12; + for (i = 0; i < 6; i++) { + memset(sbuff, ' ', sizeof(sbuff)); + tohumansize(getmemstats[i](i), &buff, flags); + sprintfaligned(buff, offset); + printf("%s", sbuff); + free(buff); + } + + offset = 11; + printf("\nSwap:"); + for (i = 0; i < 3; i++) { + memset(sbuff, ' ', sizeof(sbuff)); + tohumansize(getswapstats(i), &buff, flags); + sprintfaligned(buff, offset); + printf("%s", sbuff); + free(buff); + offset = 12; + } + putc('\n', stdout); + + return 0; +}