Skip to content

aohayon5/Unix-Shell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Unix Shell with Job Control

A Unix shell implementation in C featuring process management, job control, and signal handling. Demonstrates advanced systems programming concepts including process groups, signal masking, and race condition prevention.

Overview

This shell supports foreground and background job execution, built-in commands, and proper signal handling for job control. The implementation focuses on correctly handling race conditions and managing process lifecycle through careful use of signals and synchronization primitives.

Features

  • Job Control: Run processes in foreground or background
  • Signal Handling: Proper handling of SIGINT (Ctrl-C), SIGTSTP (Ctrl-Z), and SIGCHLD
  • Built-in Commands: quit, jobs, fg, bg
  • Process Management: Correct zombie reaping and process group management
  • Race Condition Prevention: Careful signal blocking to avoid race conditions

Requirements

  • Linux or WSL (Windows Subsystem for Linux)
  • GCC compiler
  • POSIX-compliant system

Compilation

make

Or compile directly:

gcc -Wall -O2 -o tsh tsh.c

Usage

Run the shell interactively:

./tsh

The shell displays a prompt and waits for commands:

tsh> /bin/ls -l
tsh> /bin/ps &
tsh> jobs
tsh> fg %1
tsh> quit

Commands

Built-in Commands

  • quit - Exit the shell
  • jobs - List all background jobs
  • fg <job> - Bring a background job to foreground (use %1 for job ID or PID)
  • bg <job> - Resume a stopped background job

Running Programs

  • <program> [args] - Run program in foreground
  • <program> [args] & - Run program in background

Signal Control

  • Ctrl-C - Send SIGINT to foreground job
  • Ctrl-Z - Send SIGTSTP to foreground job (suspend)

Technical Implementation

Key Challenges Solved

Race Condition Prevention

  • Signal blocking before forking to prevent race between child termination and job list addition
  • Proper use of sigprocmask() to block/unblock SIGCHLD signals

Signal Handling

  • Custom handlers for SIGINT, SIGTSTP, and SIGCHLD
  • Forwarding signals to entire process groups using negative PIDs
  • Non-blocking signal handlers using waitpid() with WNOHANG

Process Groups

  • Each job runs in its own process group
  • Uses setpgid(0, 0) to create new process group for child
  • Ensures shell remains in foreground process group

Zombie Reaping

  • All reaping done in SIGCHLD handler
  • Uses waitpid(-1, &status, WNOHANG | WUNTRACED) to handle all terminated/stopped children

Architecture

The shell uses a job list to track all running and stopped jobs. Each job contains:

  • Job ID (JID)
  • Process ID (PID)
  • State (foreground, background, stopped)
  • Command line

Main execution flow:

  1. Parse command line
  2. Check if built-in command
  3. If not built-in:
    • Block SIGCHLD
    • Fork child process
    • Child: set process group, unblock signals, exec program
    • Parent: add job to list, unblock signals, wait if foreground

Testing

The tests/ directory contains trace files that test various shell features:

# Test basic execution
./tsh < tests/trace01.txt

# Test process control
./tsh < tests/trace05.txt

# Test signal handling
./tsh < tests/trace08.txt

# Test background jobs
./tsh < tests/trace11.txt

# Comprehensive test
./tsh < tests/trace15.txt

Each trace file tests specific functionality:

  • trace01-04: Basic command execution
  • trace05-07: Process termination and signals
  • trace08-10: Signal handling (SIGINT, SIGTSTP)
  • trace11-13: Background jobs and job control
  • trace14-16: Comprehensive job control scenarios

Project Structure

unix-shell/
├── tsh.c              # Shell implementation
├── Makefile           # Build configuration
├── README.md          # This file
└── tests/             # Test trace files
    ├── trace01.txt
    ├── trace02.txt
    └── ...

Implementation Notes

  • Uses fork(), execve(), waitpid(), kill() for process management
  • Uses sigprocmask(), sigemptyset(), sigaddset() for signal blocking
  • Uses setpgid() to manage process groups
  • No use of sleep() for synchronization (busy-wait loop with sleep() used in waitfg)
  • All signal handlers are async-signal-safe

Key Concepts Demonstrated

  • Process creation and management
  • Signal handling and masking
  • Race condition prevention
  • Process groups and job control
  • Zombie process reaping
  • Foreground/background job management
  • Built-in command implementation

About

Unix shell implementation with job control, signal handling, and process management

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published