Subtopic 4
Isolation
Code Examples
Keeping the Parent’s Memory Clean
One clever use of fork() is to run a function that might leak memory or fragment the heap, while keeping the parent’s memory completely clean. Because the child runs in its own memory space, any heap damage, leaks, or excessive allocation happens only in the child. When the child exits, all that memory is freed automatically by the kernel.
Consider this scenario: you want to call a function func(arg) that:
- Allocates lots of memory but you don’t have its source (e.g., a third-party library)
- May leak memory or fragment the heap
- Returns a result, and after it returns, you want a clean heap
The solution: run func() in a child process.
↓
func() leaks 10 MB
↓
func() returns result
↓
Parent heap: 10 MB leaked ✗
Heap fragmentation remains
No way to clean up
↓
Child calls func() → leaks 10 MB
↓
Child exits with result
↓
Child’s memory freed by kernel ✓
Parent uses wait() to get result
Parent heap: completely clean ✓
pid_t child_pid;
int status;
child_pid = fork();
if (child_pid == -1)
perror("fork"), exit(1);
if (child_pid == 0) {
/* Child calls the potentially-leaky function */
/* exit() uses the return value as exit status */
exit(func(arg));
}
/* Parent waits; gets result via exit status */
if (wait(&status) == -1)
perror("wait"), exit(1);
/* Decode result from exit status */
int result = WEXITSTATUS(status); /* 0-255 only! */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
/* Simulated third-party function with a memory leak */
int leaky_compute(int input)
{
/* Allocates memory but never frees it (leak!) */
int i;
for (i = 0; i < 1000; i++) {
void *p = malloc(1024); /* 1 KB each = 1 MB total */
if (p) {
((char*)p)[0] = (char)i; /* touch it */
}
/* Note: no free(p) -- intentional leak */
}
return input * input; /* return square of input */
}
int main(void)
{
int input = 7;
int result;
pid_t child_pid;
int status;
printf("[Parent] Calling leaky_compute in a child.\n");
printf("[Parent] Parent heap is CLEAN before fork.\n");
child_pid = fork();
if (child_pid == -1) { perror("fork"); exit(1); }
if (child_pid == 0) {
/* CHILD: call the leaky function */
int r = leaky_compute(input);
printf("[Child] leaky_compute(%d) = %d, exiting.\n",
input, r);
/* All leaked memory freed by kernel on exit */
exit(r); /* pass result as exit code (0-255) */
}
/* PARENT: wait and get result */
if (wait(&status) == -1) { perror("wait"); exit(1); }
if (WIFEXITED(status)) {
result = WEXITSTATUS(status);
printf("[Parent] Result = %d\n", result);
printf("[Parent] Parent heap is still CLEAN!\n");
}
return 0;
}
Exit status is limited to 8 bits. Use a pipe to return larger data:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
/* Function that does heavy work and returns a large string */
void heavy_work(char *result_buf, size_t buflen)
{
/* Simulate intensive work with large memory usage */
char *tmp = malloc(4 * 1024 * 1024); /* 4 MB temp buffer */
if (tmp) {
memset(tmp, 'X', 4 * 1024 * 1024);
/* ... do work ... */
free(tmp); /* or maybe it leaks */
}
/* Return a complex result string */
snprintf(result_buf, buflen, "Result: processed %d items, "
"checksum=0xDEADBEEF", 42);
}
int main(void)
{
int pipefd[2];
char result[256];
pid_t pid;
/* Create pipe BEFORE fork */
if (pipe(pipefd) == -1) { perror("pipe"); exit(1); }
pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
/* CHILD: close read end, use write end */
close(pipefd[0]);
char buf[256];
heavy_work(buf, sizeof(buf)); /* may leak memory */
/* Write result to pipe */
write(pipefd[1], buf, strlen(buf) + 1);
close(pipefd[1]);
_exit(0);
}
/* PARENT: close write end, read result */
close(pipefd[1]);
ssize_t n = read(pipefd[0], result, sizeof(result) - 1);
if (n > 0) result[n] = '\0';
close(pipefd[0]);
wait(NULL);
printf("[Parent] Child returned: %s\n", result);
printf("[Parent] Memory is clean.\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
/* Simulate a game tree search that allocates memory */
int tree_search(int depth, int branch_factor)
{
if (depth == 0) return 1; /* leaf node: found */
/* Allocate a "node" (won't be freed = leak) */
int *node = malloc(1024);
if (node) node[0] = depth;
int result = 0;
for (int b = 0; b < branch_factor; b++) {
result += tree_search(depth - 1, branch_factor);
}
return result;
}
/* Run tree_search in a child; parent stays clean */
int isolated_tree_search(int depth, int branch_factor)
{
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == -1) return -1;
if (pid == 0) {
close(pipefd[0]);
int r = tree_search(depth, branch_factor);
write(pipefd[1], &r, sizeof(r));
close(pipefd[1]);
_exit(0);
}
close(pipefd[1]);
int result;
read(pipefd[0], &result, sizeof(result));
close(pipefd[0]);
wait(NULL);
return result;
}
int main(void)
{
printf("Tree search result (depth=3, branch=2): %d\n",
isolated_tree_search(3, 2));
printf("Tree search result (depth=4, branch=2): %d\n",
isolated_tree_search(4, 2));
printf("Parent heap remains clean throughout.\n");
return 0;
}
Fork a child process, run the function in the child, and use exit()/wait() to return the result. All memory leaked by the function (in the child’s heap) is automatically freed by the kernel when the child exits. The parent’s heap is untouched.
The exit status is only 8 bits wide (0–255). Values larger than 255 are truncated. For larger or more complex return values, use pipes, shared memory (mmap/shm), a temporary file, or a socket to pass data from child to parent.
The kernel tracks all memory pages mapped to a process. When a process terminates, the kernel reclaims all pages in its virtual address space regardless of whether the process ever called free(). Memory leaks only matter during a process’s lifetime. On exit, the OS always cleans up completely.
(1) Browser tab isolation: each tab runs in its own process so a memory leak in one tab doesn’t affect others or the browser itself. (2) Test runners: each test case runs in a forked child so memory state from one test can’t affect the next, and a crash/leak is fully isolated.
