Skip to content

Instantly share code, notes, and snippets.

@scottt
Last active April 5, 2023 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottt/bd56a0b5d45cb77c17d69fafbc095985 to your computer and use it in GitHub Desktop.
Save scottt/bd56a0b5d45cb77c17d69fafbc095985 to your computer and use it in GitHub Desktop.
Memory stores cause minor page faults after fork() but not after vfork() on Linux
$ make test
./test-fork-exec-memset
vfork-exec-memset: using vfork

 Performance counter stats for './vfork-exec-memset':

           244,243      page-faults:u                                                         
           244,243      minor-faults:u                                                        
                 0      major-faults:u                                                        

       0.803667298 seconds time elapsed

       0.200547000 seconds user
       0.595543000 seconds sys



 Performance counter stats for './fork-exec-memset':

           488,411      page-faults:u                                                         
           488,411      minor-faults:u                                                        
                 0      major-faults:u                                                        

       1.322967201 seconds time elapsed

       0.288548000 seconds user
       1.004638000 seconds sys
// Memory stores cause minor page faults after fork() but not after vfork() on Linux
// See discussion in https://twitter.com/offlinemark/status/1643348064835612672
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/wait.h>
enum { gigabyte = 1000000000 };
char *program_name;
enum { FORK_EXEC_USE_VFORK = 1 };
int fork_exec(char * const program, int flags)
{
int r, wstatus;
pid_t pid;
if (flags & FORK_EXEC_USE_VFORK) {
pid = vfork();
} else {
pid = fork();
}
char * const argv[] = { NULL };
if (pid == 0) {
r = execvp(program, argv);
if (r != 0) {
fprintf(stderr, "execvp: %s\n", strerror(errno));
}
} else {
int options = 0;
r = waitpid(pid, &wstatus, options);
if (r == -1) {
fprintf(stderr, "waitpid: %s\n", strerror(errno));
return r;
}
return wstatus;
}
return -1;
}
int main(int argc, char **argv)
{
program_name = basename(argv[0]);
volatile char *buf = malloc(gigabyte);
memset(buf, 0x55, gigabyte);
// fprintf(stderr, "program_name: %s\n", program_name);
if (strstr(program_name, "vfork-")) {
fprintf(stderr, "%s: using vfork\n", program_name);
fork_exec("true", FORK_EXEC_USE_VFORK);
} else {
fork_exec("true", 0);
}
memset(buf, 0xaa, gigabyte);
return 0;
}
CFLAGS := -Wall -Og
PROGRAMS := fork-exec-memset vfork-exec-memset
.PHONY: all
all: $(PROGRAMS)
.PHONY: test
test: all
./test-fork-exec-memset
vfork-exec-memset: fork-exec-memset
ln -sf $< $@
.PHONY: clean
clean:
rm -f $(PROGRAMS)
#!/bin/bash
for i in vfork-exec-memset fork-exec-memset ; do
perf stat -e page-faults,minor-faults,major-faults -- ./$i
done
@scottt
Copy link
Author

scottt commented Apr 5, 2023

I had to compile with -Og instead of -Os to keep the second memset() call from being optimized out by GCC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment