Sample to verify the effect of mlockall() on globals

Global variables and arrays are not part of the binary, but are allocated by the OS at application startup. The virtual memory pages associated to this data is not immediately mapped to physical pages of RAM, meaning that page faults occur on access. It turns out that the mlockall() call forces all global variables and arrays into RAM, meaning that subsequent access to this memory does not result in page faults. As such, using global variables and arrays does not introduce any additional problems for real time applications. You can verify this behavior using the following program (run as 'root' to allow the mlockall() operation)

/*
 * # gcc -o check_global  check_global.c -Wall -O3
 * # ./check_global
 * # ./check_global -nolockmem  (will cause faults)
 * This application checks whether mlockall() forces all pages of a
 * global array into RAM. Normally, the OS maps a 'copy on write' MMU page
 * to such arrays meaning that reading from the array returns only zeros and
 * the first write results in a page fault so a physical page of RAM is
 * mapped to it (and initialised to zero before the write occurs).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <unistd.h>
 
/* Define the global array*/
#define  NUM_PAGES 5000
#define  PAGE_SZ  (1024 * 4)  /* Assume 4kb pages*/
char glob_buffer[NUM_PAGES * PAGE_SZ];
 
/* Lock the application in memory to avoid page faults*/
static void lock_application(void)
{
	if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
		perror("mlockall() failed - root ?");
		exit(-1);
	}
}
 
/* Dump minor and major page faults that occurred since the previous call */
static int dump_page_faults(void)
{
	int			new_minor_page_faults, new_major_page_faults;
	int			page_faults_detected = 0;
	static struct rusage	rusage_prev;
	static int		init;
	struct rusage		rusage;
 
	getrusage(RUSAGE_SELF, &rusage);
	new_minor_page_faults = rusage.ru_minflt - rusage_prev.ru_minflt;
	new_major_page_faults = rusage.ru_majflt - rusage_prev.ru_majflt;
	rusage_prev.ru_minflt = rusage.ru_minflt;
	rusage_prev.ru_majflt = rusage.ru_majflt;
 
	if (init) {
		if ((new_minor_page_faults > 0) ||
		    (new_major_page_faults > 0)) {
			printf("New minor/major page faults: %d/%d\n",
			       new_minor_page_faults, new_major_page_faults);
			page_faults_detected = 1;
		}
	}
	init = 1;
	return page_faults_detected;
}
 
 
int main(int argc, char *argv[])
{
	int unexpected_pagefaults_detected;
	int lock_memory = 1;
	int buff_value;
	int i;
 
	for (i = 1; i < argc; i++) {
		if (strncmp(argv[i], "-nolockmem", 10) == 0) {
			/* Results in many page faults! */
			lock_memory = 0;
		}
	}
 
	if (lock_memory) {
		lock_application();
		printf("Current and future memory locked in RAM\n");
	}
	(void)dump_page_faults(); /* Set the baseline*/
 
	/* From this point onwards we no longer expect any page faults */
	unexpected_pagefaults_detected = 0;
 
	for (i = 0; i < NUM_PAGES; i++) {
		buff_value = (int) (glob_buffer[i * PAGE_SZ]);
 
		if (buff_value != 0) {
			printf("Reading unexpected value %d on page %d of global buffer.\n",
				 buff_value, i);
		}
		if (dump_page_faults())
			unexpected_pagefaults_detected = 1;
 
		glob_buffer[(i * PAGE_SZ) + 1] = 1;
		if (dump_page_faults()) {
			printf("Writing to page %d of global buffer caused page fault(s)\n",
					 i);
			unexpected_pagefaults_detected = 1;
		}
	}
 
	printf("Done, result: %s\n",
		unexpected_pagefaults_detected ? "failed" : "success");
	return unexpected_pagefaults_detected ? 1 : 0;
}