System Programming Topic

System Programming Introduction

What is System Programming?

System Programming involves writing software that provides services to the computer hardware and other software. It's closer to the hardware level than application programming and deals with:

  • Operating system components

  • Device drivers

  • System utilities

  • Low-level resource management

  • Performance optimization

Key Characteristics

  • Hardware Interaction: Direct communication with hardware

  • Resource Management: Memory, CPU, I/O devices

  • Performance Critical: Efficient resource usage

  • Platform Dependent: Often specific to OS/hardware

  • Low-level Access: Direct memory, interrupts, system calls

System Programming vs Application Programming

Aspect System Programming Application Programming
Level Low-level, close to OS High-level, user-facing
Language C, C++, Rust, Assembly Python, Java, JavaScript
Focus Resource management, performance Business logic, user experience
Portability Often platform-specific Usually cross-platform
Examples OS kernels, drivers, compilers Web apps, mobile apps, games

Core Concepts

1. System Calls

System calls are the interface between user programs and the operating system kernel.

c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// File operations using system calls
int main() {
    // Open a file (system call)
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }
    
    // Read from file (system call)
    char buffer[100];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("Read: %s\n", buffer);
    }
    
    // Close file (system call)
    close(fd);
    return 0;
}

2. Process Management

c

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();  // Create a new process
    
    if (pid == -1) {
        perror("fork failed");
        return 1;
    }
    else if (pid == 0) {
        // Child process
        printf("Child process: PID = %d, Parent PID = %d\n", 
               getpid(), getppid());
        
        // Execute a new program
        execlp("ls", "ls", "-l", NULL);
        perror("execlp failed");  // Only reached if execlp fails
    }
    else {
        // Parent process
        printf("Parent process: PID = %d, Child PID = %d\n", 
               getpid(), pid);
        
        // Wait for child to complete
        int status;
        waitpid(pid, &status, 0);
        printf("Child process exited with status: %d\n", status);
    }
    
    return 0;
}

3. Memory Management

c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Manual memory management
void memory_management_example() {
    // Stack allocation (automatic)
    int stack_var = 42;
    
    // Heap allocation (manual)
    int *heap_var = (int*)malloc(sizeof(int));
    if (heap_var == NULL) {
        perror("malloc failed");
        return;
    }
    *heap_var = 100;
    
    // Array allocation
    int *array = (int*)calloc(10, sizeof(int)); // Initialized to 0
    if (array == NULL) {
        perror("calloc failed");
        free(heap_var);
        return;
    }
    
    // Use the memory
    for (int i = 0; i < 10; i++) {
        array[i] = i * i;
    }
    
    // Resize array
    int *new_array = (int*)realloc(array, 20 * sizeof(int));
    if (new_array != NULL) {
        array = new_array;
    }
    
    // Free memory (CRITICAL!)
    free(heap_var);
    free(array);
}

// Memory layout example
void memory_layout() {
    int local_var = 10;           // Stack
    static int static_var = 20;   // Data segment
    const char *str = "Hello";    // Code/Data segment
    
    int *dynamic_var = malloc(sizeof(int));  // Heap
    *dynamic_var = 30;
    
    printf("Stack variable: %p\n", (void*)&local_var);
    printf("Static variable: %p\n", (void*)&static_var);
    printf String constant: %p\n", (void*)str);
    printf("Heap variable: %p\n", (void*)dynamic_var);
    
    free(dynamic_var);
}

4. File I/O and Descriptors

c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

void file_io_example() {
    int fd;
    char buffer[1024];
    ssize_t bytes;
    
    // Open file for writing
    fd = open("testfile.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open failed");
        return;
    }
    
    // Write to file
    char *data = "Hello, System Programming!\n";
    bytes = write(fd, data, strlen(data));
    if (bytes == -1) {
        perror("write failed");
        close(fd);
        return;
    }
    
    close(fd);
    
    // Open for reading
    fd = open("testfile.txt", O_RDONLY);
    if (fd == -1) {
        perror("open for reading failed");
        return;
    }
    
    // Read from file
    bytes = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes > 0) {
        buffer[bytes] = '\0';
        printf("Read: %s", buffer);
    }
    
    close(fd);
}

// File descriptor manipulation
void duplicate_fd() {
    int fd = open("file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return;
    }
    
    // Duplicate file descriptor
    int new_fd = dup(fd);
    printf("Original FD: %d, Duplicated FD: %d\n", fd, new_fd);
    
    // Both FDs point to the same file description
    close(fd);
    close(new_fd);
}

5. Inter-Process Communication (IPC)

Pipes

c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

void pipe_example() {
    int pipefd[2];
    pid_t pid;
    char buffer[100];
    
    // Create pipe
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return;
    }
    
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return;
    }
    
    if (pid == 0) {
        // Child process - writer
        close(pipefd[0]);  // Close read end
        
        char *message = "Hello from child process!";
        write(pipefd[1], message, strlen(message) + 1);
        close(pipefd[1]);
        
        _exit(0);
    } else {
        // Parent process - reader
        close(pipefd[1]);  // Close write end
        
        ssize_t bytes = read(pipefd[0], buffer, sizeof(buffer));
        if (bytes > 0) {
            printf("Parent received: %s\n", buffer);
        }
        
        close(pipefd[0]);
        wait(NULL);  // Wait for child
    }
}

Shared Memory

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>

#define SHM_SIZE 1024

void shared_memory_example() {
    int shmid;
    char *shared_memory;
    key_t key = 1234;  // Arbitrary key
    
    // Create shared memory segment
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        return;
    }
    
    // Attach shared memory
    shared_memory = shmat(shmid, NULL, 0);
    if (shared_memory == (char *) -1) {
        perror("shmat");
        return;
    }
    
    // Write to shared memory
    sprintf(shared_memory, "Hello from process %d", getpid());
    printf("Written to shared memory: %s\n", shared_memory);
    
    // Detach shared memory
    shmdt(shared_memory);
    
    // Remove shared memory segment (in real code, do this carefully)
    // shmctl(shmid, IPC_RMID, NULL);
}

6. Signals

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

volatile sig_atomic_t signal_received = 0;

// Signal handler function
void signal_handler(int sig) {
    signal_received = sig;
    printf("\nReceived signal: %d (%s)\n", sig, strsignal(sig));
}

void signal_example() {
    // Register signal handlers
    signal(SIGINT, signal_handler);   // Ctrl+C
    signal(SIGTERM, signal_handler);  // Termination request
    signal(SIGUSR1, signal_handler);  // User-defined signal
    
    printf("Process PID: %d\n", getpid());
    printf("Send signals to this process:\n");
    printf("  Ctrl+C (SIGINT)\n");
    printf("  kill -TERM %d (SIGTERM)\n", getpid());
    printf("  kill -USR1 %d (SIGUSR1)\n", getpid());
    
    // Main loop
    while (!signal_received) {
        printf("Working...\n");
        sleep(2);
    }
    
    printf("Exiting due to signal %d\n", signal_received);
}

// Advanced signal handling with sigaction
void advanced_signal_handling() {
    struct sigaction sa;
    
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    
    // Block signals during critical section
    sigset_t block_set, old_set;
    sigemptyset(&block_set);
    sigaddset(&block_set, SIGINT);
    
    sigprocmask(SIG_BLOCK, &block_set, &old_set);
    printf("Critical section - SIGINT blocked\n");
    sleep(5);
    sigprocmask(SIG_SETMASK, &old_set, NULL);
    printf("Critical section ended\n");
}

System Programming Languages

1. C - The Classic System Language

c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

// Multi-threading with pthreads
void* thread_function(void* arg) {
    int thread_num = *(int*)arg;
    printf("Thread %d running\n", thread_num);
    return NULL;
}

void threading_example() {
    pthread_t threads[3];
    int thread_args[3];
    
    for (int i = 0; i < 3; i++) {
        thread_args[i] = i;
        if (pthread_create(&threads[i], NULL, thread_function, &thread_args[i]) != 0) {
            perror("pthread_create");
            return;
        }
    }
    
    // Wait for all threads to complete
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }
    
    printf("All threads completed\n");
}

2. C++ for System Programming

cpp

#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include <vector>
#include <memory>

// RAII for resource management
class FileHandler {
private:
    std::fstream file;
    
public:
    FileHandler(const std::string& filename, std::ios_base::openmode mode) {
        file.open(filename, mode);
        if (!file.is_open()) {
            throw std::runtime_error("Cannot open file: " + filename);
        }
    }
    
    ~FileHandler() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    void write(const std::string& data) {
        file << data;
    }
    
    std::string read() {
        std::string content;
        file.seekg(0, std::ios::end);
        content.resize(file.tellg());
        file.seekg(0, std::ios::beg);
        file.read(&content[0], content.size());
        return content;
    }
};

void cpp_system_example() {
    try {
        FileHandler fh("test.txt", std::ios::out);
        fh.write("C++ System Programming\n");
        
        // File automatically closed when fh goes out of scope
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

3. Rust - Modern System Programming

rust

use std::fs::File;
use std::io::{Write, Read};
use std::thread;
use std::time::Duration;

// Memory safety with ownership system
fn rust_file_operations() -> std::io::Result<()> {
    // File creation (automatically closed when variable goes out of scope)
    let mut file = File::create("rust_test.txt")?;
    file.write_all(b"Hello from Rust system programming!")?;
    
    // Reading file
    let mut file = File::open("rust_test.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    println!("File contents: {}", contents);
    
    Ok(())
}

// Threading with move semantics
fn rust_threading() {
    let data = vec![1, 2, 3, 4, 5];
    
    let handle = thread::spawn(move || {
        // data is moved into the thread
        for item in data {
            println!("Processing: {}", item);
            thread::sleep(Duration::from_millis(100));
        }
    });
    
    handle.join().unwrap();
}

fn main() {
    match rust_file_operations() {
        Ok(()) => println!("File operations successful"),
        Err(e) => eprintln!("Error: {}", e),
    }
    
    rust_threading();
}

Operating System Concepts

1. Process Scheduling

c

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>

void process_info() {
    printf("Process ID: %d\n", getpid());
    printf("Parent Process ID: %d\n", getppid());
    printf("Process Group ID: %d\n", getpgrp());
    printf("User ID: %d\n", getuid());
    printf("Effective User ID: %d\n", geteuid());
    
    // Get priority
    int priority = getpriority(PRIO_PROCESS, 0);
    printf("Process priority: %d\n", priority);
}

// Process states and scheduling
void cpu_intensive() {
    struct timeval start, end;
    gettimeofday(&start, NULL);
    
    // CPU-bound work
    volatile long long counter = 0;
    for (long long i = 0; i < 1000000000LL; i++) {
        counter += i;
    }
    
    gettimeofday(&end, NULL);
    
    long seconds = end.tv_sec - start.tv_sec;
    long microseconds = end.tv_usec - start.tv_usec;
    double elapsed = seconds + microseconds * 1e-6;
    
    printf("Computation took: %.6f seconds\n", elapsed);
}

2. Virtual Memory and Paging

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

void memory_mapping_example() {
    size_t size = getpagesize();  // System page size
    printf("Page size: %zu bytes\n", size);
    
    // Memory map a file
    int fd = open("mapped_file.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("open");
        return;
    }
    
    // Extend file to required size
    if (ftruncate(fd, size) == -1) {
        perror("ftruncate");
        close(fd);
        return;
    }
    
    // Memory map the file
    char *mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, 
                       MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return;
    }
    
    // Use mapped memory
    sprintf(mapped, "This is memory mapped I/O!");
    printf("Mapped content: %s\n", mapped);
    
    // Synchronize and unmap
    msync(mapped, size, MS_SYNC);
    munmap(mapped, size);
    close(fd);
}

3. I/O System and Buffering

c

#include <stdio.h>
#include <unistd.h>

void io_buffering() {
    // Standard I/O buffering modes
    printf("Default stdout buffering mode\n");
    
    // Set line buffering
    setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
    printf("Line buffered output\n");
    
    // Set no buffering
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("Unbuffered output\n");
    
    // Direct system call (unbuffered)
    write(STDOUT_FILENO, "Direct write\n", 13);
}

// Asynchronous I/O simulation
void async_io_simulation() {
    printf("Starting asynchronous operation...\n");
    
    pid_t pid = fork();
    if (pid == 0) {
        // Child process - background work
        sleep(2);
        printf("Background task completed\n");
        _exit(0);
    } else {
        // Parent continues immediately
        printf("Main process continues without waiting\n");
    }
}

System Tools and Utilities

1. Command Line Tools

bash

# Process monitoring
ps aux                    # Show all processes
top                       # Interactive process viewer
htop                      # Enhanced top

# System information
uname -a                  # System information
lscpu                     # CPU information
free -h                   # Memory usage
df -h                     # Disk usage

# Performance monitoring
vmstat 1                  # Virtual memory statistics
iostat 1                  # I/O statistics
netstat -tuln             # Network connections

# Debugging tools
strace -p <PID>           # Trace system calls
ltrace -p <PID>           # Trace library calls
gdb <program>             # GNU debugger

2. Building System Software

makefile

# Makefile example for system program
CC = gcc
CFLAGS = -Wall -Wextra -std=c99 -D_POSIX_C_SOURCE=200809L
LDFLAGS = -lpthread -lrt

TARGET = system_program
SOURCES = main.c process.c memory.c io.c
OBJECTS = $(SOURCES:.c=.o)

$(TARGET): $(OBJECTS)
	$(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJECTS) $(TARGET)

install: $(TARGET)
	install -m 755 $(TARGET) /usr/local/bin/

.PHONY: clean install

Real-World System Programming Examples

1. Simple Web Server

c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

void simple_web_server() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    char *response = 
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/plain\r\n"
        "Content-Length: 13\r\n"
        "\r\n"
        "Hello, World!";
    
    // Create socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // Set socket options
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // Bind socket to port
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    // Listen for connections
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    printf("Server listening on port %d\n", PORT);
    
    while (1) {
        // Accept connection
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, 
                                (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
        
        // Read request
        read(new_socket, buffer, BUFFER_SIZE);
        printf("Received request:\n%s\n", buffer);
        
        // Send response
        write(new_socket, response, strlen(response));
        printf("Response sent\n");
        
        close(new_socket);
    }
}

2. Custom Shell Implementation

c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define MAX_INPUT 1024
#define MAX_ARGS 64

void custom_shell() {
    char input[MAX_INPUT];
    char *args[MAX_ARGS];
    char *token;
    pid_t pid;
    int status;
    
    while (1) {
        printf("mysh> ");
        fflush(stdout);
        
        // Read input
        if (!fgets(input, MAX_INPUT, stdin)) {
            break;
        }
        
        // Remove newline
        input[strcspn(input, "\n")] = 0;
        
        // Skip empty input
        if (strlen(input) == 0) {
            continue;
        }
        
        // Exit command
        if (strcmp(input, "exit") == 0) {
            break;
        }
        
        // Tokenize input
        int i = 0;
        token = strtok(input, " ");
        while (token != NULL && i < MAX_ARGS - 1) {
            args[i++] = token;
            token = strtok(NULL, " ");
        }
        args[i] = NULL;
        
        // Execute command
        pid = fork();
        if (pid == 0) {
            // Child process
            execvp(args[0], args);
            perror("execvp failed");
            exit(1);
        } else if (pid > 0) {
            // Parent process
            waitpid(pid, &status, 0);
        } else {
            perror("fork failed");
        }
    }
}

Key System Programming Concepts Summary

1. Process Management

  • Process creation (fork), execution (exec), termination

  • Process states, scheduling, and context switching

  • Inter-process communication (pipes, signals, shared memory)

2. Memory Management

  • Virtual memory and paging

  • Memory allocation (malloc, free)

  • Memory mapping and protection

3. File Systems

  • File descriptors and I/O operations

  • Directory operations and file metadata

  • File locking and permissions

4. Networking

  • Socket programming

  • Network protocols and interfaces

  • Client-server architecture

5. Concurrency

  • Multi-threading and synchronization

  • Race conditions and deadlocks

  • Atomic operations and locks

6. Security

  • User and group permissions

  • Privilege separation

  • Secure coding practices

Learning Path

  1. Start with C - Master pointers, memory management, system calls

  2. Learn POSIX API - Understand standard system interfaces

  3. Study OS Concepts - Processes, memory, filesystems, networking

  4. Practice with Projects - Build utilities, servers, system tools

  5. Explore Advanced Topics - Kernel development, drivers, distributed systems

System programming provides deep understanding of how computers work and enables building efficient, reliable software that interacts directly with hardware and operating system resources.

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?