Skip to content

C Systems Programming on Linux: Core Syntax & Language Fundamentals

Created: 2026-11-06 Last updated: 2026-11-07

Executive Summary: C is the bedrock of modern computing. The Linux kernel, relational database engines (PostgreSQL, SQLite), web servers (Nginx), and the standard C runtime (glibc) that exposes kernel system calls are all written in C. Mastering C requires building a solid foundation from first principles: understanding how code compiles to native machine instructions, how variables occupy memory cells, how bitwise operators inspect system flags, and how pointers reference hardware addresses. This guide forms Part 1.1 (Fundamentals) of our C Systems Programming curriculum: taking you step-by-step from your first compiled binary to an interactive Linux system calculator.

  • Debian Toolchain & Anatomy of main()


    Install build-essential, compile with strict ISO C17 flags (-Wall -Wextra), inspect exit codes, and master printf format specifiers.

  • Types & Bitwise Operations


    Utilize portable fixed-width integers (<stdint.h>) and manipulate hardware flags with bitwise operators (&, |, ^, ~, <<, >>).

  • Control Flow, Arrays & Safe Strings


    Direct logic with if/else, switch, and loops (for, while), manage contiguous byte arrays, and eliminate buffer overflows using fgets().

  • Pointers & Interactive CLI Lab


    Demystify memory addresses (&), dereferencing (*), and Call-by-Reference, culminating in an interactive Linux system utility.


1. The Debian C Toolchain & Anatomy of main()

To write, compile, and execute C software on Debian or Ubuntu, install the core compiler collection and system libraries:

Installing the Build Toolchain on Debian/Ubuntu
sudo apt update && sudo apt install -y build-essential

Anatomy of a C Source File

Create a workspace directory ~/c_lab and author hello.c:

~/c_lab/hello.c
#include <stdio.h>   // Standard Input/Output library (provides printf)
#include <stdlib.h>  // Standard library (provides EXIT_SUCCESS, EXIT_FAILURE)

int main(int argc, char *argv[]) {
    // Print a formatted message to standard output
    printf("Welcome to C Systems Programming on Debian Linux!\n");

    // Inspect command-line arguments passed from the shell
    if (argc > 1) {
        printf("First argument supplied: %s\n", argv[1]);
    }

    // Return status code to the Linux kernel (0 indicates success)
    return EXIT_SUCCESS;
}
  • #include <stdio.h>: The preprocessor directive that imports declarations for standard I/O functions.
  • int main(...): The mandated entry point of every C program. Operating systems expect an integer return code upon process termination.
  • argc & argv: argc contains the number of arguments (including the program path itself), while argv is an array of strings holding those arguments.
  • EXIT_SUCCESS: Standardized macro evaluating to 0. A non-zero return code signals an error condition to the calling shell.

Compiling with the GNU Compiler Collection (GCC)

C is a compiled language: your human-readable source code is translated directly into native machine instructions (ELF binary) for your CPU architecture (x86_64 or aarch64):

Compiling with Strict ISO C17 Flags
gcc -std=c17 -Wall -Wextra hello.c -o hello
./hello SystemsEngineer
  • -std=c17: Enforces strict conformance to the modern ISO C17 standard.
  • -Wall -Wextra: Activates comprehensive compiler warnings, catching common bugs and type mismatches at compile time.
  • -o hello: Specifies the output binary name instead of the default a.out.

Standard Format Specifiers in printf()

The printf() function interpolates variables into formatted text strings using conversion specifiers:

Specifier Expected Type Output Format Example
%d or %i int Signed decimal integer (-42, 100)
%u unsigned int Unsigned decimal integer (42, 65535)
%f float / double Floating-point number (3.141592)
%.2f float / double Floating-point with 2 decimal places (3.14)
%c char Single ASCII character ('A', '\n')
%s char * Null-terminated character string ("Linux")
%zu size_t Unsigned byte sizes returned by sizeof()
%p void * Memory address printed in hexadecimal (0x7ffd9b...)
%% None Literal percent character (%)

2. Variables, Primitive Types & Modern <stdint.h>

In C, every variable must be declared with an explicit data type before it can store a value.

Primitive Scalar Types: char & <stdbool.h>

Before exploring integer sizes, understand C's foundational character and boolean mechanics:

1. The char Type & ASCII Integer Arithmetic

In C, a char is fundamentally a 1-byte integer (8 bits) representing ASCII characters (values 0 to 127):

Character Mechanics & Arithmetic
#include <stdio.h>

int main(void) {
    char letter = 'A'; // ASCII value 65
    printf("Character: %c | ASCII Integer: %d\n", letter, letter);

    // Character arithmetic: convert uppercase to lowercase by adding 32
    char lowercase = letter + 32;
    printf("Shifted character: %c (ASCII: %d)\n", lowercase, lowercase);

    // Common Escape Sequences: \n (newline), \t (tab), \r (carriage return), \\ (backslash)
    printf("Path format:\tC:\\Debian\\Kernel\n");
    return 0;
}

2. Boolean Logic with <stdbool.h>

Historically, C had no native boolean type: any non-zero value evaluates to true, and 0 evaluates to false. Modern C99/C17 standardizes booleans via <stdbool.h>:

Booleans in Modern C
#include <stdio.h>
#include <stdbool.h>

int main(void) {
    bool is_service_active = true;
    bool has_root_privilege = false;

    // In C, booleans are integers: true evaluates to 1, false to 0
    printf("Service: %d | Privileged: %d\n", is_service_active, has_root_privilege);
    printf("Status string: %s\n", is_service_active ? "RUNNING" : "STOPPED");
    return 0;
}

Primitive Types & Architecture Variance

Historically, C primitives like short, int, and long have variable bitwidths depending on whether the hardware is 16-bit, 32-bit, or 64-bit. To eliminate portability bugs, production systems programming relies on the fixed-width integer types standardized in <stdint.h>:

Type Exact Width Value Range Standard Systems Use Case
uint8_t 1 Byte (8 bits) 0 to 255 Byte buffers, network packet octets, bit flags
int16_t 2 Bytes (16 bits) -32,768 to +32,767 Sensor data, audio samples
int32_t 4 Bytes (32 bits) -2.14B to +2.14B General counters, array indices, exit codes
uint64_t 8 Bytes (64 bits) 0 to 18 Quintillion Timestamps, disk byte offsets, memory capacities
size_t Native pointer size 0 to address space limit Return type of sizeof(), memory buffer allocations
Demonstrating Exact-Width Types
#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint8_t  network_flag = 0b00000101; // Binary literal
    int32_t  temperature  = -12;
    uint64_t ram_bytes    = 17179869184ULL; // 16 GiB

    printf("Flag: %u | Temp: %d C | RAM: %lu bytes\n", 
           network_flag, temperature, (unsigned long)ram_bytes);
    printf("Byte sizes: uint8=%zu, int32=%zu, uint64=%zu\n", 
           sizeof(network_flag), sizeof(temperature), sizeof(ram_bytes));
    return 0;
}

3. Operators & Bitwise Logic (The Systems Superpower)

Beyond standard arithmetic (+, -, *, /, %) and relational comparisons (==, !=, <, >, <=, >=), systems programming requires absolute mastery of Bitwise Operators.

Hardware registers, Linux file permissions, and network headers pack multiple binary states into single bytes using bit flags:

Operator Name Logic Example (a = 5 [0101], b = 3 [0011])
& Bitwise AND 1 only if both bits are 1 5 & 3 → 0001 (1)
\| Bitwise OR 1 if at least one bit is 1 5 \| 3 → 0111 (7)
^ Bitwise XOR 1 if bits differ 5 ^ 3 → 0110 (6)
~ Bitwise NOT Inverts all bits (one's complement) ~00000101 → 11111010
<< Left Shift Shifts bits left (multiplies by 2ⁿ) 1 << 2 → 0100 (4)
>> Right Shift Shifts bits right (divides by 2ⁿ) 8 >> 1 → 0100 (4)
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    BYTE["1 Byte (8 Bits): 0000 0111"]
    R["Bit 2: READ (1 << 2 = 4)"]
    W["Bit 1: WRITE (1 << 1 = 2)"]
    X["Bit 0: EXECUTE (1 << 0 = 1)"]

    BYTE --> R --> W --> X

Real-World Example: Linux Permission Bitmasks

~/c_lab/bitmask.c
#include <stdio.h>
#include <stdint.h>

// Define bit flags using left-shift
#define PERM_EXEC  (1 << 0) // 0001 (1)
#define PERM_WRITE (1 << 1) // 0010 (2)
#define PERM_READ  (1 << 2) // 0100 (4)

int main(void) {
    // Combine Read and Write permissions using bitwise OR (|)
    uint8_t user_perms = PERM_READ | PERM_WRITE; // 0110 (6 in octal)

    // Check if user has Read permission using bitwise AND (&)
    if (user_perms & PERM_READ) {
        printf("User has READ access.\n");
    }

    // Check if user has Execute permission
    if (user_perms & PERM_EXEC) {
        printf("User has EXECUTE access.\n");
    } else {
        printf("User lacks EXECUTE access.\n");
    }

    // Grant Execute permission dynamically
    user_perms |= PERM_EXEC; // Now 0111 (7 in octal = rwx)
    printf("Permissions updated. Octal value: %o\n", user_perms);

    return 0;
}

4. Control Flow: Decisions & Iterations

1. Conditional Branching (if, else if, else)

int32_t disk_usage_percent = 88;

if (disk_usage_percent >= 90) {
    printf("CRITICAL: Disk capacity nearly exhausted!\n");
} else if (disk_usage_percent >= 80) {
    printf("WARNING: Disk capacity high.\n");
} else {
    printf("OK: Disk capacity normal.\n");
}

2. Multi-Way Decisions with switch

When evaluating a single integral variable against discrete values, switch generates efficient jump tables:

char log_level = 'W';

switch (log_level) {
    case 'D':
        printf("[DEBUG] Verbose diagnostics enabled.\n");
        break;
    case 'I':
        printf("[INFO] Service operational.\n");
        break;
    case 'W':
        printf("[WARN] High memory consumption detected.\n");
        break;
    case 'E':
        printf("[ERROR] Database connection lost.\n");
        break;
    default:
        printf("[UNKNOWN] Unrecognized log level.\n");
        break;
}

The Missing break Pitfall

If you omit the break; statement at the end of a case block, execution falls through directly into subsequent cases, executing their logic unconditionally.


3. Iteration Loops: for, while, and do-while

  • for Loop (Counted Iterations): Ideal when iteration counts are known beforehand.
  • while Loop (Condition-Controlled): Runs as long as a condition evaluates to true (checked before execution).
  • do-while Loop (Guaranteed Execution): Condition is checked after execution, guaranteeing at least one run.
Loop Constructs in C
#include <stdio.h>

int main(void) {
    // 1. Counted for loop
    printf("Counting up with for: ");
    for (int i = 1; i <= 5; i++) {
        printf("%d ", i);
    }
    printf("\n");

    // 2. Condition-controlled while loop
    int count = 3;
    printf("Counting down with while: ");
    while (count > 0) {
        printf("%d ", count);
        count--;
    }
    printf("\n");

    // 3. do-while loop (runs at least once)
    int retry = 0;
    do {
        printf("Executing initial attempt (retry = %d)\n", retry);
        retry++;
    } while (retry < 1);

    return 0;
}

5. Functions, Variable Scope & Call-by-Value

A function groups reusable logic under a declared identifier with defined inputs and output.

Function Declarations vs. Definitions

C compilers parse files sequentially from top to bottom. If function A calls function B, the compiler must know B's signature before A executes. This is achieved via Function Prototypes (Forward Declarations):

~/c_lab/functions.c
#include <stdio.h>
#include <stdint.h>

// 1. Function Prototype (Forward Declaration)
uint64_t compute_factorial(uint32_t n);
void modify_value(int val);

int main(void) {
    uint32_t num = 5;
    uint64_t fact = compute_factorial(num);
    printf("Factorial of %u is %lu\n", num, fact);

    // Call-by-Value demonstration
    int original = 42;
    modify_value(original);
    printf("Original value in main: %d (Unchanged!)\n", original);

    return 0;
}

// 2. Function Definition
uint64_t compute_factorial(uint32_t n) {
    uint64_t result = 1;
    for (uint32_t i = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}

// In C, arguments are passed strictly by value (copied)
void modify_value(int val) {
    val = 999; // Mutates only the local copy on this stack frame!
}

6. Arrays & C-Strings (Zero Magic)

1. Single & Multidimensional Arrays

An array is a contiguous sequence of memory cells containing identical types:

Array Initialization and Traversal
#include <stdio.h>
#include <stdint.h>

int main(void) {
    // 1D Array with 5 integers
    int32_t latencies[5] = {15, 22, 18, 9, 31};

    // Calculate number of elements dynamically at compile time
    size_t count = sizeof(latencies) / sizeof(latencies[0]);
    printf("Array contains %zu elements:\n", count);
    for (size_t i = 0; i < count; i++) {
        printf("  Element [%zu] = %d\n", i, latencies[i]);
    }

    // 2D Array (Matrix: 2 rows, 3 columns)
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    printf("Matrix element [1][2] = %d\n", matrix[1][2]); // Output: 6
    return 0;
}

Why Is There No Built-In ArrayList in Standard C?

Programmers coming from Python (list), Java (ArrayList), or C++ (std::vector) often search for a dynamic resizable array.
Standard C has NO built-in ArrayList! C arrays allocated on the stack have a fixed, immutable size defined at compile time. Creating a dynamically expanding array requires manual heap allocation with malloc() and geometric resizing with realloc()—a powerful technique we implement from scratch in Part 2.1 (Dynamic Data Structures).

No Runtime Bounds Checking!

C does not check whether array indices stay within allocated bounds. Reading or writing latencies[10] accesses arbitrary process memory, causing undefined behavior or sudden segmentation faults.


2. The Anatomy of a C String: Buffers vs. Literals

Unlike high-level languages where strings are complex objects, a C string is simply a char array terminated by an unprintable null byte ('\0', ASCII 0):

%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    C0["'D' (0x44)"]
    C1["'e' (0x65)"]
    C2["'b' (0x62)"]
    C3["'i' (0x69)"]
    C4["'a' (0x61)"]
    C5["'n' (0x6E)"]
    C6["'\0' (Null-Byte Sentinel)"]

    C0 --> C1 --> C2 --> C3 --> C4 --> C5 --> C6

Modifiable Array vs. Read-Only String Literal

How you declare a string fundamentally alters where it resides in memory:

// 1. Modifiable Stack Buffer: Memory is allocated on the stack; bytes can be mutated!
char mutable_str[] = "Debian";
mutable_str[0] = 'd'; // ✅ Valid: string is now "debian"

// 2. Read-Only String Literal: Stored in the binary's .rodata (read-only) segment!
const char *readonly_str = "Debian";
// readonly_str[0] = 'd'; // ❌ CRASH: Hardware MMU triggers an instant Segmentation Fault!

3. The Professional String Manipulation Toolkit (<string.h>)

Because strings in C are simple byte arrays, systems programmers rely on <string.h> for parsing, searching, tokenizing, and copying:

~/c_lab/string_toolkit.c
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int main(void) {
    // 1. Length vs. Allocation: strlen() counts bytes up to '\0'; sizeof() reports buffer capacity
    char buffer[64] = "/var/log/syslog";
    printf("[1] Length: %zu chars | Capacity: %zu bytes\n", strlen(buffer), sizeof(buffer));

    // 2. Safe Copy & Formatting: snprintf() guarantees null-termination and prevents overflows
    char formatted_path[64];
    snprintf(formatted_path, sizeof(formatted_path), "%s.%d.gz", buffer, 1);
    printf("[2] Formatted log path: %s\n", formatted_path);

    // 3. Exact vs. Prefix Comparison: strcmp() vs. strncmp()
    char http_req[] = "GET /api/v1/metrics HTTP/1.1";
    if (strncmp(http_req, "GET ", 4) == 0) {
        printf("[3] HTTP Method: GET request detected.\n");
    }

    // 4. Character Search: strchr() (first match) & strrchr() (last match)
    char filepath[] = "backup_2026_archive.tar.gz";
    char *extension = strrchr(filepath, '.'); // Find file extension
    if (extension) {
        printf("[4] Extracted file extension: %s\n", extension);
    }

    // 5. Substring Search: strstr() locates a substring inside a larger string
    char email[] = "user@example.com";
    char *domain = strstr(email, "@example.com");
    if (domain) {
        printf("[5] Domain found at index: %ld\n", domain - email);
    }

    // 6. Safe Reentrant Tokenization: strtok_r() splits strings without global state
    char csv_line[] = "srv01,192.168.1.10,online,8192";
    char *saveptr; // Internal pointer tracking state between iterations
    printf("[6] Tokenizing CSV line:\n");
    char *token = strtok_r(csv_line, ",", &saveptr);
    while (token != NULL) {
        printf("    Field: %s\n", token);
        token = strtok_r(NULL, ",", &saveptr);
    }

    // 7. Overlapping Memory Copy: memmove() handles shared buffer boundaries safely
    char shift_buf[] = "1234567890";
    // Move '4567890' 2 positions to the left (overlapping source and destination!)
    memmove(shift_buf + 1, shift_buf + 3, 7);
    printf("[7] memmove overlap result: %s\n", shift_buf);

    return 0;
}

4. Safe Terminal Input with fgets()

The legacy function scanf("%s", buffer) is infamous for causing critical buffer overflow vulnerabilities because it cannot verify buffer capacity. Production C code uses fgets():

Safe User Input with fgets
#include <stdio.h>
#include <string.h>

int main(void) {
    char username[32];

    printf("Enter system username: ");
    // Read up to 31 characters + null terminator safely from stdin
    if (fgets(username, sizeof(username), stdin) != NULL) {
        // Strip trailing newline character if present
        username[strcspn(username, "\n")] = '\0';
        printf("Authenticated user: [%s]\n", username);
    }
    return 0;
}

7. Pointers – The Mental Model Made Simple

A Pointer is simply a variable whose stored value is a memory address in RAM.

Think of physical RAM as a gigantic street of numbered mailboxes. Every variable lives at a specific house number (address):

%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    subgraph MemoryAddressSpace ["Computer RAM"]
        direction TB
        PTR["Address: 0x7ffd00<br/>Value: 0x7ffd08<br/>(Pointer: ptr)"]
        TARGET["Address: 0x7ffd08<br/>Value: 42<br/>(Variable: counter)"]
        PTR -->|"Points to address of"| TARGET
    end

There are exactly two fundamental pointer operators:

  1. The Address-Of Operator &: "Where does this variable live?" Returns its memory address.
  2. The Dereference Operator *: "Look inside the address!" Reads or modifies the value at that address.
Demonstrating Pointers & Dereferencing
#include <stdio.h>

int main(void) {
    int counter = 42;
    int *ptr = &counter; // ptr holds the memory address of counter

    printf("Value of counter:         %d\n", counter);
    printf("Memory address (&counter): %p\n", (void *)&counter);
    printf("Value stored in ptr:       %p\n", (void *)ptr);
    printf("Dereferenced value (*ptr): %d\n", *ptr);

    // Mutate counter indirectly through the pointer
    *ptr = 100;
    printf("New value of counter:     %d\n", counter);

    return 0;
}

Solving Call-by-Value: Call-by-Reference via Pointers

Earlier we observed that C cannot modify a caller's variables because arguments are copied. By passing the memory address (pointer) instead of the value, functions can directly mutate external variables:

~/c_lab/swap.c
#include <stdio.h>

// Function receives pointers to two external integers
void swap(int *a, int *b) {
    int temp = *a; // Read value inside address a
    *a = *b;       // Write value from b into address a
    *b = temp;     // Write temp value into address b
}

int main(void) {
    int x = 10;
    int y = 99;

    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(&x, &y); // Pass addresses using &
    printf("After swap:  x = %d, y = %d\n", x, y);

    return 0;
}

8. Hands-On Beginner Lab: Building an Interactive Linux System Calculator

Let us synthesize every foundational concept—fixed-width integers, control structures, bitwise masking, safe fgets() input, functions, and pointers—into a robust, complete CLI utility:

~/c_lab/sys_calc.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

// Permission bit flags
#define PERM_EXEC  (1 << 0) // 1 (001)
#define PERM_WRITE (1 << 1) // 2 (010)
#define PERM_READ  (1 << 2) // 4 (100)

// Function prototypes
void convert_bytes(uint64_t bytes, double *kib, double *mib, double *gib);
void print_permissions(uint8_t octal_digit);
void clear_input_buffer(void);

int main(void) {
    char input_line[64];
    int choice = 0;

    while (choice != 3) {
        printf("\n=========================================\n");
        printf("    LINUX SYSTEM CALCULATOR (C17)\n");
        printf("=========================================\n");
        printf("1. Convert Storage (Bytes -> KiB, MiB, GiB)\n");
        printf("2. Decode Linux Permission Octal (0-7)\n");
        printf("3. Exit\n");
        printf("Select an option [1-3]: ");

        if (fgets(input_line, sizeof(input_line), stdin) == NULL) {
            break;
        }

        // Parse user choice integer safely
        if (sscanf(input_line, "%d", &choice) != 1) {
            printf("Invalid input. Please enter a valid number.\n");
            continue;
        }

        switch (choice) {
            case 1: {
                printf("\nEnter capacity in raw Bytes: ");
                if (fgets(input_line, sizeof(input_line), stdin) != NULL) {
                    uint64_t raw_bytes = 0;
                    if (sscanf(input_line, "%lu", &raw_bytes) == 1) {
                        double kib, mib, gib;
                        // Use pointer call-by-reference to populate multiple results
                        convert_bytes(raw_bytes, &kib, &mib, &gib);
                        printf("-> %.2f KiB\n", kib);
                        printf("-> %.2f MiB\n", mib);
                        printf("-> %.4f GiB\n", gib);
                    } else {
                        printf("Failed to parse byte quantity.\n");
                    }
                }
                break;
            }

            case 2: {
                printf("\nEnter permission digit (0 to 7): ");
                if (fgets(input_line, sizeof(input_line), stdin) != NULL) {
                    int octal = 0;
                    if (sscanf(input_line, "%d", &octal) == 1 && octal >= 0 && octal <= 7) {
                        printf("Permissions for octal %d: ", octal);
                        print_permissions((uint8_t)octal);
                        printf("\n");
                    } else {
                        printf("Invalid octal digit! Must be between 0 and 7.\n");
                    }
                }
                break;
            }

            case 3:
                printf("Exiting Linux System Calculator. Goodbye!\n");
                break;

            default:
                printf("Unknown option. Select 1, 2, or 3.\n");
                break;
        }
    }

    return EXIT_SUCCESS;
}

// Converts bytes to human-readable units via pointer outputs
void convert_bytes(uint64_t bytes, double *kib, double *mib, double *gib) {
    *kib = (double)bytes / 1024.0;
    *mib = *kib / 1024.0;
    *gib = *mib / 1024.0;
}

// Decodes bitwise permissions into rwx format
void print_permissions(uint8_t octal_digit) {
    char r = (octal_digit & PERM_READ)  ? 'r' : '-';
    char w = (octal_digit & PERM_WRITE) ? 'w' : '-';
    char x = (octal_digit & PERM_EXEC)  ? 'x' : '-';
    printf("[%c%c%c]", r, w, x);
}

Compiling & Testing the Project

Compiling & Running the System Calculator
gcc -std=c17 -Wall -Wextra sys_calc.c -o sys_calc
./sys_calc

Try option 1 with 1073741824 bytes (exactly 1 GiB), and option 2 with 7 ([rwx]) or 5 ([r-x]). You have constructed a complete, crash-resilient C CLI tool from fundamental building blocks!


Where to Go Next

Now that you have mastered core C syntax, control flow, bitwise manipulation, and pointer mechanics, proceed to the build toolchain:

  • Part 1.2: Build Toolchain & Makefiles


    Demystify the four GCC compilation phases (cpp, cc1, as, ld), static (.a) vs. dynamic (.so) libraries, and production GNU Makefiles.

    Toolchain Guide

  • Linux Process Management


    Inspect running ELF processes, signals (SIGTERM/SIGKILL), strace syscall traces, and /proc internals.

    Process Management

  • Python Systems Automation


    Compare C's manual memory and bitwise control with Python's rapid scripting and automation ecosystem.

    Python Guide