anetczuk.github.io
Apr 21, 2022 • 2 min read

Dumping and restoring Unix processes

Let’s say there is application with complex internal state. Let’s say there is a need to persist it’s state for further use. One way to solve the problem would be to dump the state programically from within the application. In Python problem could be solved for example using pickle. Unfortunately C++ users have no luck. Moreover, in C++ it’s not always possible to access internal state of application. Cases include accessing internals of linked libraries (e.g. of RNG from cstdlib).

Other possible solution for the problem is to dump application’s whole memory and restore it later using capabilities of operating system. One such tool is CRIU.

CRIU provides two basic operations: dump and restore alowing to persisting and loading processes respectively. For dumping it takes two additional parameters: PID of the process to persist and directory to store the state. Restore needs directory where dump operation saved the state.

Simple example

Take look at following simple_job.py program:

#!/usr/bin/env python3

###
### Infinitely print random number every 2 seconds.
###

import os
import time
import random


pid = os.getpid()
print( "PID:", pid )


counter = 0

while True:
    counter += 1
    rnum = random.randrange( 999999 )
    print( pid, "step:", counter, "rand:", rnum )
    time.sleep( 2.0 )

Program just prints random number in interval of 2 seconds.

Simple dump script dump.sh could look like:

#!/bin/bash

set -eu

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )


##
## find PID by process name
##
pid_list=$(pgrep -f simple_job.py)

echo -e "found PIDs:\n$pid_list"

readarray -t pid_array <<<"$pid_list"

last_pid=${pid_array[-1]}

echo "last PID: ${last_pid}"


##
## check root privilege
##
if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi


##
## dump process
##
dump_dir="$SCRIPT_DIR/dump/"

criu dump --shell-job -R -t ${last_pid} -D $dump_dir


echo "Dump done to $dump_dir"

Script looks for PID of process by it’s executable name, then takes it and dumps the process.

Corresponding restore script restore.sh:

#!/bin/bash

set -eu

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )


##
## check root privilege
##
if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi


##
## restore process
##
dump_dir="$SCRIPT_DIR/dump/"

criu restore --shell-job -D $dump_dir


echo "Process restored from $dump_dir"

It opens storage directory and restores dumped process.

Given CRIU scripts use additional parameters to control CRIU:

Restore script can be run more than once (sequentailly). Output of restored process will always be the same – copy of restored process will always generate the same random numbers.

Limitations

CRIU works locally and as primary operation stores content of RAM memory of given process. Consequently it is not capable of persisting external resources such as content of local files or connected components.

Detailed list of limitations can be found here.

References

Post by: anetczuk