#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define PAGESIZE (64*1024) /* How many pages we've mmap'd. */ static long pages; /* Pointer to mmap'd memory used as a circular buffer. One thread touches pages, another thread releases them on notification. */ static char *p; /* How many pages to touch each 5ms. This makes at most 2000 pages/sec. */ #define TOUCH_CHUNK 10 /* How many pages to free when we're notified. With a 100ms FREE_DELAY, we can free ~9110 pages/sec, or perhaps only 5*911 = 4555 pages/sec if we're notified only 5 times/sec. */ #define FREE_CHUNK 911 /* Delay in milliseconds before freeing pages, to simulate latency while finding pages to free. */ #define FREE_DELAY 100 static void touch(void); static int release(void *arg); static void release_pages(void); static void show_meminfo(void); static void* _release (void *arg); int main (int argc, char **argv) { pthread_t thr; mlockall(MCL_CURRENT); /* lock text*/ setvbuf(stdout, (char *)NULL, _IOLBF, 0); pages = atol(argv[1]) * 1024 * 1024 / PAGESIZE; p = mmap(NULL, pages * PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 0, 0); if (p == MAP_FAILED) { perror("mmap"); exit(1); } if(pthread_create(&thr, NULL, _release, NULL)) { perror("pthread_create"); exit(1); } touch(); return 0; } static void touch (void) { long page = 0; while (1) { int i; struct timespec t; for (i = 0; i < TOUCH_CHUNK; i++) { p[page * PAGESIZE] = 1; if (++page >= pages) { page = 0; } } #if 1 t.tv_sec = 0; t.tv_nsec = 5 * 1000 * 1000; /* 5ms */ if (nanosleep(&t, NULL) == -1) { perror("nanosleep"); } #endif } } static int release (void *arg) { int fd = open("/dev/mem_notify", O_RDONLY); if (fd == -1) { perror("open(/dev/mem_notify)"); exit(1); } while (1) { struct pollfd pfd; int nfds; pfd.fd = fd; pfd.events = POLLIN; printf("poll\n"); nfds = poll(&pfd, 1, -1); if (nfds == -1) { perror("poll"); exit(1); } printf("notify\n"); if (nfds == 1) { struct timespec t; t.tv_sec = 0; t.tv_nsec = FREE_DELAY * 1000 * 1000; if (nanosleep(&t, NULL) == -1) { perror("nanosleep"); } printf("wakeup\n"); release_pages(); printf("time: %ld\n", time(NULL)); // show_meminfo(); } } } static void* _release (void *arg) { release(arg); return NULL; } static void release_pages (void) { /* Index of the next page to free. */ static long page = 0; int i; /* Release FREE_CHUNK pages. */ for (i = 0; i < FREE_CHUNK; i++) { int r = madvise(p + page*PAGESIZE, PAGESIZE, MADV_DONTNEED); if (r == -1) { perror("madvise"); exit(1); } // printf("free %p\n", p + page*PAGESIZE); if (++page >= pages) { page = 0; } } } static void show_meminfo (void) { char buffer[2000]; int fd; ssize_t n; fd = open("/proc/meminfo", O_RDONLY); if (fd == -1) { perror("open(/proc/meminfo)"); exit(1); } n = read(fd, buffer, sizeof(buffer)); if (n == -1) { perror("read(/proc/meminfo)"); exit(1); } n = write(1, buffer, n); if (n == -1) { perror("write(stdout)"); exit(1); } if (close(fd) == -1) { perror("close(/proc/meminfo)"); exit(1); } }