A small orchestration system written in C for the Operating Systems course
LCC at Universidade do Minho, 2025/26. Users submit bash commands
through a runner program; a central controller schedules them, enforces a
configurable limit on how many run at once, and keeps a persistent log of what
ran and for how long.
The two programs talk over named pipes (FIFOs), and the assignment required
using only low-level system calls for I/O, so there's no printf for program
output and no system()/shell delegation anywhere. This is a cleaned-up and
extended version of what I originally submitted.
- Gonçalo Pereira (@pereiravp)
- David Mimoso (@davidmimoso)
runner ──(request via /tmp/controller_fifo)──▶ controller
▲ │
└──────(reply via /tmp/runner_fifo_<pid>)──────────┘
The controller runs a single event loop, reacting to whatever comes in: a
new command, a finished command, a status query, or a shutdown request. It
doesn't run anything itself, it just decides when a runner is allowed to go.
Each runner asks permission, waits for the go-ahead, runs its command, then
reports back, and the log records the user, command id, and total duration.
Needs gcc and make, on Linux.
make # build bin/controller and bin/runner
make test # build and run the test suite
make clean # remove build artifactsStart the controller with a parallelism limit and a scheduling policy:
./bin/controller <max-parallel> [fifo|rr]max-parallel: how many commands may run at the same time.- policy:
fifo(first-come, first-served) orrr(round-robin by user). Defaults tofifoif omitted.
Submit and manage commands with the runner:
./bin/runner -e <user-id> "<command>" # execute a command
./bin/runner -c # list running and queued commands
./bin/runner -s # ask the controller to shut downExample session:
$ ./bin/runner -e 1 "echo hello"
[runner] command 98301 submitted
[runner] executing command 98301...
hello
[runner] command 98301 finished
The runner supports redirections (>, >>, <, 2>) and pipelines (|):
./bin/runner -e 1 "grep system /etc/passwd | wc -l > out.txt"- FIFO: commands start in submission order. Simple, but one user flooding the queue makes everyone else wait.
- Round-robin by user: when a slot frees up, it goes to whoever has been served the fewest times so far, the fairness property the assignment asked for.
If one user floods the queue and another submits a single command late, under FIFO the latecomer waits for the whole queue to drain; under round-robin they get served almost right away. That's what the test suite checks.
make test runs 11 functional checks with no external framework: basic
execution, each redirection operator, pipelines, the completion log, the status
query, and the fairness difference between the two policies. Each test starts a
fresh controller and tears it down cleanly.
There is also testes.sh, a benchmark script that runs a mixed workload under
different parallelism limits and both policies, saving one log per run
(resultados_<policy>_<limit>.txt) for analysis.
src/ controller.c, runner.c
include/ common.h: shared message formats and constants
tests/ run_tests.sh: the functional test suite
testes.sh benchmark / data-collection script
Makefile build with all / controller / runner / test / clean targets
- Up to 100 tasks queued at once (
MAX_TASKS). - The controller only shuts down once every in-flight command has finished, even after a shutdown request, so no command is dropped mid-run.
SIGPIPEis ignored so a runner closing unexpectedly can't take the controller down with it.