This repository contains small projects for exploring operating-system concepts through working programs. Each directory is an independent experiment and has its own source code, build commands, and README.
The projects currently cover:
- Process creation and command execution in a small Unix-like shell
- File systems, inodes, directories, and FUSE callbacks in user space
- Virtual memory, heap mappings, and process memory inspection through
/proc
These projects are intended for learning and experimentation rather than production use. They are designed primarily for Linux and may require system packages or elevated permissions.
.
├── fuse/ In-memory FUSE file system
├── hackVM/ Virtual-memory and process-heap experiments
└── shell/ Minimal Unix-style shell
The shell project implements a small command interpreter in C. It reads a command line, splits it into arguments, starts external programs with fork() and execvp(), and waits for child processes with waitpid().
It currently includes:
- An interactive command loop
- External command execution
- A
cdbuilt-in command - Simple pipelines using
|
It deliberately does not provide advanced shell features such as job control, command history, input/output redirection, quoting rules, or environment expansion.
Build and run it from the project directory:
cd shell
make
./mainExample commands:
ls -la
pwd
cd /tmpThe compiler command in shell/Makefile enables debugging symbols and sanitizers for address, leak, and undefined-behavior checks.
The FUSE project implements a user-space file system. The recommended implementation in inode.c models files and directories with an in-memory inode table and linked directory entries. The older ram.c implementation stores complete paths and is kept as a simpler comparison project.
The inode implementation demonstrates:
- Regular files and directories
- File creation, reading, writing, and removal
- Directory listing and path lookup
- File metadata and permissions
- Hard links and symbolic links
- Timestamp updates
- File-system statistics
- AddressSanitizer-assisted development
All data is volatile. Files disappear when the file-system process exits, and the implementation does not provide persistence, journaling, locking, crash recovery, or rename support.
Install the usual Debian or Ubuntu dependencies:
sudo apt install build-essential pkg-config libfuse3-dev fuse3Build the inode implementation and mount it in a separate temporary directory:
cd fuse
make inode
mkdir -p /tmp/myfs
./inode -f /tmp/myfsWhile the file system is running, use another terminal to try it:
printf 'hello from FUSE\n' > /tmp/myfs/hello.txt
cat /tmp/myfs/hello.txt
mkdir /tmp/myfs/data
ls -la /tmp/myfsUnmount it from another terminal:
cd fuse
make unmountThe older path-based implementation can be built with make main; that target produces the executable ram. See fuse/Readme.md for implementation details and limitations.
This project explores the relationship between a process's virtual address space and the memory managed by the operating system.
It contains:
simple.c, a small C process that allocates a string on the heap and keeps running while printing its addressread_write_heap.go, a Go utility that reads/proc/<pid>/mapsto locate a target process's heap and uses/proc/<pid>/memto find and overwrite a string
Build the programs:
cd hackVM
gcc -o simple simple.c
go build -o read_write_heap read_write_heap.goRun the sample process in one terminal and note its process ID:
./simpleIn another terminal, replace the string in the running process:
sudo ./read_write_heap <pid> "Holberton" "Hello"The target string and replacement should fit the allocated memory region. Access to /proc/<pid>/mem is restricted on many Linux systems, which is why sudo may be required. Use this tool only with processes you own or are explicitly authorized to inspect.
The exact requirements depend on the project:
- A Linux environment
- GCC and GNU Make for the C projects
- AddressSanitizer support in the compiler used by the Makefiles
- FUSE 3 development files and
fusermount3forfuse - Go for
hackVM
There is no top-level Makefile; build each project from its own directory. Generated binaries are local build artifacts and are not shared between projects.
A useful order for exploring the repository is:
- Start with
shellto see processes, system calls, and parent/child relationships. - Continue with
fuseto see how file-system operations are represented as callbacks and data structures. - Finish with
hackVMto inspect how a running process is mapped into virtual memory.
For project-specific design notes and limitations, read:
This is an educational collection that will evolve as new operating-system concepts are implemented. The code favors visibility and experimentation over complete POSIX compatibility, portability, or production-grade error handling.