# Syllabus học Linux

## Phase 0 - Setup (2 ngày)

**Ngày 1: Cài đặt môi trường ảo hóa**

*   Khái niệm
    
    *   Type-1 vs Type-2 hypervisor - vì sao KVM (Type-1, tích hợp kernel) nhanh hơn VirtualBox (Type-2) trên Linux host.
        
    *   Virtualization extension: `Intel VT-x`/`AMD-V` - kiểm tra `egrep -c '(vmx|svm)' /proc/cpuinfo`.
        
*   Thực hành và quan sát
    
    *   Cài `libvirt` + `qemu-kvm` (nếu host là Linux) hoặc VirtualBox.
        
    *   Tải ISO Rocky Linux minimal, cài đặt thủ công - **partition bằng tay**, không dùng auto-partition (để hiểu rõ layout).
        
*   Advanced concepts
    
    *   Nested virtualization: nếu bạn cài VM trong VM (VD lab trong máy ảo công ty), cần bật `kvm_intel nested=1`.
        

**Ngày 2: SSH & Snapshot**

*   Khái niệm
    
    *   SSH key-pair: asymmetric crypto, vì sao an toàn hơn password.
        
    *   `authorized_keys`, `known_hosts`, cơ chế host key verification chống MITM.
        
*   Thực hành và quan sát
    
    *   Generate keypair (`ed25519`), copy key bằng `ssh-copy-id`, disable `PasswordAuthentication` trong `sshd_config`.
        
    *   `~/.ssh/config` - alias host, `IdentityFile`, `Port` custom.
        
*   Advanced concepts
    
    *   Snapshot VM ngay sau bước này (LVM snapshot hoặc hypervisor snapshot) - điểm khôi phục cho toàn bộ lab sau này.
        

## Phase 1 - Nền tảng hệ thống (6 tuần)

### Tuần 1: Shell & Filesystem

**Ngày 1: Filesystem Hierarchy Standard & cơ chế mount**

*   Cơ chế
    
    *   FHS: `/etc /var /usr /opt /proc /sys /tmp /run` - vai trò từng thư mục, cái nào persistent, cái nào tmpfs (RAM-backed).
        
    *   `/proc` và `/sys` không phải file thật - chúng là **giao diện kernel expose ra userspace** (procfs, sysfs là pseudo-filesystem).
        
    *   Mount point là gì - cơ chế 1 filesystem được "gắn" vào 1 thư mục trong cây thư mục gốc.
        
*   Thực hành và quan sát
    
    *   `mount` không tham số - đọc toàn bộ danh sách mount hiện tại, phân biệt real disk vs tmpfs vs proc/sysfs.
        
    *   `cat /proc/mounts` so với lệnh `mount` - hiểu vì sao 2 output gần giống nhau (`mount` đọc từ `/proc/self/mounts`).
        
*   Advanced concepts
    
    *   `stat -f /` xem filesystem type của root.
        
    *   Bind mount: `mount --bind` - 1 thư mục xuất hiện ở 2 chỗ khác nhau, dùng nhiều trong container.
        

**Ngày 2: Bash cơ bản & Shell expansion**

*   Cơ chế
    
    *   Shell là 1 chương trình đọc-thực thi (REPL), không phải "hệ điều hành mini" - hiểu rõ ranh giới shell built-in vs external command (`type ls`, `type cd`).
        
    *   Thứ tự expansion của bash: brace → tilde → parameter → command substitution → arithmetic → word splitting → pathname (glob).
        
*   Thực hành và quan sát
    
    *   Biến môi trường vs biến shell (`export` khác gì không export).
        
    *   `~/.bashrc` vs `~/.bash_profile` vs `/etc/profile` - thứ tự load khi login shell vs non-login shell.
        
*   Advanced concepts
    
    *   `set -x` để trace từng lệnh thực thi trong script - công cụ debug quan trọng nhất của shell script.
        

**Ngày 3: Pipes & Redirection**

*   Cơ chế
    
    *   Pipe (`|`) tạo ra 1 anonymous pipe trong kernel - thực chất là 1 buffer trong bộ nhớ kernel, không phải file.
        
    *   File descriptor: 0 (stdin), 1 (stdout), 2 (stderr) - redirection thực chất là thao tác trên bảng file descriptor của process.
        
*   Thực hành và quan sát
    
    *   `>`, `>>`, `2>`, `2>&1`, `&>` - phân biệt rõ thứ tự `cmd > file 2>&1` khác `cmd 2>&1 > file`.
        
    *   `tee` - vừa ghi file vừa in stdout, giải thích cơ chế nó đọc stdin và ghi ra 2 nơi.
        
*   Advanced concepts
    
    *   Process substitution `<(cmd)` - kernel tạo 1 file descriptor ảo trỏ tới output của command, không cần tạo file tạm thật.
        

**Ngày 4: grep & find**

*   Cơ chế
    
    *   Regex cơ bản (BRE) vs extended (ERE, `grep -E`) - vì sao `+`, `?`, `|` cần escape trong BRE.
        
    *   `find` duyệt cây thư mục theo depth-first, mỗi kết quả trigger `-exec` là 1 lần fork+exec (chậm) vs `-exec ... +` gom batch (nhanh hơn).
        
*   Thực hành và quan sát
    
    *   10 bài tập: tìm file >100MB, tìm file sửa đổi trong 24h qua, tìm text trong nhiều file kèm số dòng.
        
*   Advanced concepts
    
    *   `find -exec cmd {} +` vs `| xargs` - so sánh performance khi xử lý hàng nghìn file.
        

**Ngày 5: sed & awk**

*   Cơ chế
    
    *   `sed` là stream editor - xử lý từng dòng theo pattern space, không load toàn bộ file vào RAM (khác với việc mở file bằng editor).
        
    *   `awk` là ngôn ngữ xử lý dữ liệu theo field/record - `$0, $1, NF, NR` là gì.
        
*   Thực hành và quan sát
    
    *   Parse 1 file log Apache mẫu: trích IP, status code, đếm request theo status.
        
*   Advanced concepts
    
    *   `sed -i` in-place edit thực chất tạo file tạm rồi rename - hiểu vì sao an toàn hơn ghi đè trực tiếp.
        

**Ngày 6: Ôn tập tuần 1**

*   Viết 1 script bash tổng hợp log: đếm request theo IP, sort theo số lượng giảm dần, xuất top 10.
    
*   Không nhìn lại note - tự nhớ syntax.
    

### Tuần 2: Package management & RPM internals

**Ngày 1: dnf & repo**

*   Cơ chế
    
    *   Repo metadata: `dnf` không cài trực tiếp từ file rpm, mà tải metadata (danh sách package + dependency graph) từ repo trước, cache tại `/var/cache/dnf`.
        
    *   Dependency resolution: SAT solver bên trong dnf giải quyết xung đột version.
        
*   Thực hành và quan sát
    
    *   `dnf install/remove/update/history/repolist`, `dnf history undo <id>` (rollback transaction).
        
    *   Thêm EPEL repo, quan sát `/etc/yum.repos.d/`.
        
*   Advanced concepts
    
    *   `dnf` transaction là atomic - nếu 1 package fail, toàn bộ transaction rollback (không giống cài package rời rạc).
        

**Ngày 2: RPM internals**

*   Cơ chế
    
    *   Cấu trúc file `.rpm`: lead + signature + header (metadata + file list) + payload (cpio archive nén).
        
    *   RPM database: `/var/lib/rpm` - nơi lưu trạng thái package đã cài, dùng Berkeley DB (RHEL8+) hoặc sqlite (RHEL9+).
        
*   Thực hành và quan sát
    
    *   `rpm -qa`, `rpm -qf <file>` (tìm package sở hữu 1 file), `rpm -qi`, `rpm -ql`.
        
    *   `rpm -qf /bin/ls` - tìm ra package `coreutils`.
        
*   Advanced concepts
    
    *   `rpm -V <package>` - verify checksum các file đã cài, phát hiện file bị chỉnh sửa ngoài ý muốn (dùng trong security audit thật).
        

**Ngày 3: Users & Groups - cấu trúc file**

*   Cơ chế
    
    *   `/etc/passwd`: 7 field (username:x:UID:GID:GECOS:home:shell) - vì sao password field là `x` (đã chuyển sang shadow).
        
    *   `/etc/shadow`: password hash (thuật toán, salt, hash - format `$id$salt$hash`), password aging fields.
        
    *   `/etc/group`, `/etc/gshadow` - primary group vs supplementary group.
        
*   Thực hành và quan sát
    
    *   `useradd/usermod/groupadd`, tạo user với UID cố định, home dir custom.
        
    *   `chage -l <user>` xem password aging policy.
        
*   Advanced concepts
    
    *   `getent passwd` - vì sao dùng `getent` thay vì đọc trực tiếp `/etc/passwd` (hỗ trợ NSS: LDAP, SSSD, không chỉ file local).
        

**Ngày 4: Permissions cơ bản - deep dive** *(mở rộng theo yêu cầu)*

*   Cơ chế
    
    *   rwx cho owner/group/other - octal representation, vì sao `755` = `rwxr-xr-x`.
        
    *   **SUID**: process chạy với quyền của **owner file** thay vì user gọi nó - cơ chế `/usr/bin/passwd` cần SUID root để ghi `/etc/shadow`.
        
    *   **SGID**: trên file → chạy với quyền group owner; trên **thư mục** → file mới tạo trong đó tự động thừa hưởng group của thư mục (dùng cho shared team folder).
        
    *   **Sticky bit**: trên thư mục → chỉ owner của file mới được xóa file đó dù thư mục có quyền write chung (VD `/tmp`).
        
    *   `umask` - cơ chế trừ quyền mặc định khi tạo file mới (`umask 022` → file mới `644`, thư mục mới `755`).
        
*   Thực hành và quan sát
    
    *   Tạo file SUID thử nghiệm (`chmod u+s`), giải thích rủi ro bảo mật cụ thể: nếu SUID root gắn vào 1 binary có lỗi (VD gọi `system()` không sanitize input) → privilege escalation.
        
    *   Tạo thư mục SGID cho team share, test file mới tạo tự động đúng group.
        
*   Advanced concepts
    
    *   `capabilities` (POSIX capabilities) - cách hiện đại thay thế SUID toàn quyền root: `getcap /usr/bin/ping` (chỉ cần `CAP_NET_RAW` thay vì full root). `setcap`/`getcap` thực hành trên 1 binary tự viết.
        
    *   Vì sao capabilities an toàn hơn SUID: giới hạn đúng quyền cần thiết thay vì "tất cả hoặc không gì" (root or nothing).
        

**Ngày 5: ACL & PAM**

*   Cơ chế
    
    *   ACL (Access Control List): mở rộng permission model truyền thống (owner/group/other) - cho phép set quyền riêng cho từng user/group cụ thể trên 1 file.
        
    *   PAM (Pluggable Authentication Modules): cơ chế "cắm module xác thực" - vì sao đổi chính sách password (độ dài, độ phức tạp) không cần sửa code từng app, chỉ sửa `/etc/pam.d/`.
        
*   Thực hành và quan sát
    
    *   `setfacl -m u:user2:rx file`, `getfacl file` - case thực tế: thư mục nhiều user cần quyền khác nhau mà group model không đủ linh hoạt.
        
    *   Đọc `/etc/pam.d/sshd` - trace qua các dòng `auth`, `account`, `password`, `session`.
        
*   Advanced concepts
    
    *   `pam_faillock` - cấu hình khóa account sau N lần login sai (chống brute-force), thực hành cấu hình thật.
        

**Ngày 6: Ôn tập tuần 2**

*   Dựng 3 user, 1 group chung, thư mục share dùng SGID + ACL cho 1 user ngoài group được quyền đọc riêng.
    
*   Set capabilities cho 1 binary tự viết thay vì chạy nó bằng `sudo`.
    

### Tuần 3: Authentication & Package Distribution *(tuần mới)*

**Ngày 1: LDAP khái niệm & SSSD**

*   Cơ chế
    
    *   LDAP (Lightweight Directory Access Protocol): directory service tập trung - thay vì mỗi server tự quản lý `/etc/passwd` riêng, toàn bộ user/group nằm ở 1 nơi (Active Directory hoặc OpenLDAP), server chỉ query.
        
    *   SSSD (System Security Services Daemon): cache + broker giữa hệ thống local và backend (LDAP/AD/Kerberos) - vì sao cần cache: nếu mất kết nối tới directory server, user vẫn login được bằng cache gần nhất (offline authentication).
        
*   Thực hành và quan sát
    
    *   Cài `sssd`, `realmd`, quan sát file cấu hình `/etc/sssd/sssd.conf` mẫu (không cần AD thật, đọc cấu trúc domain/id\_provider/auth\_provider).
        
    *   `getent passwd <user_ldap>` - nếu có LDAP server test, thấy user "ảo" xuất hiện dù không có trong `/etc/passwd`.
        
*   Advanced concepts
    
    *   `sssd` cache location `/var/lib/sss/db` - `sss_cache -E` để force expire cache khi cần đồng bộ lại ngay.
        

**Ngày 2: Kerberos cơ bản**

*   Cơ chế
    
    *   Kerberos: giao thức authentication dùng **ticket** thay vì gửi password qua mạng mỗi lần - KDC (Key Distribution Center) cấp TGT (Ticket Granting Ticket) sau khi xác thực 1 lần, sau đó dùng TGT để xin service ticket cho từng service cụ thể mà không cần nhập lại password.
        
    *   Vì sao Kerberos quan trọng trong RHEL doanh nghiệp: tích hợp trực tiếp với Active Directory (đa số công ty dùng Windows AD làm domain controller), SSO (Single Sign-On) giữa nhiều Linux server.
        
*   Thực hành và quan sát
    
    *   `kinit <user>` (nếu có KDC test/AD test) - lấy TGT, `klist` xem ticket hiện có, thời hạn hết hạn.
        
*   Advanced concepts
    
    *   `realm join` - lệnh RHEL hiện đại để join domain AD chỉ với vài dòng lệnh (SSSD + Kerberos + LDAP được cấu hình tự động phía sau) - hiểu nó làm gì "bên dưới" quan trọng hơn thuộc lệnh, vì khi lỗi bạn cần biết debug từng lớp (LDAP query fail hay Kerberos ticket fail hay SSSD cache stale).
        

**Ngày 3: Repo nội bộ & GPG signing**

*   Cơ chế
    
    *   GPG signing: mỗi RPM package có thể ký bằng private key, client verify bằng public key trong `/etc/pki/rpm-gpg/` - đảm bảo package không bị chỉnh sửa giữa đường (supply-chain integrity), lý do repo nội bộ doanh nghiệp luôn cần ký package tự build.
        
    *   `createrepo` - sinh metadata (`repodata/`) từ 1 thư mục chứa file `.rpm`, biến nó thành repo `dnf` có thể dùng được.
        
*   Thực hành và quan sát
    
    *   Tạo GPG keypair (`gpg --gen-key`), ký 1 RPM bằng `rpm --addsign`, verify bằng `rpm -K`.
        
    *   Dựng 1 repo nội bộ đơn giản (thư mục + `createrepo` + serve qua `httpd` hoặc `python -m http.server`), thêm vào `/etc/yum.repos.d/` của VM khác, `dnf install` từ đó.
        
*   Advanced concepts
    
    *   `repotrack`/mirror sync - cách doanh nghiệp lớn mirror toàn bộ repo RHEL/EPEL nội bộ để không phụ thuộc internet mỗi lần cài package (air-gapped environment phổ biến trong ngành tài chính/chính phủ).
        

**Ngày 4: Build RPM cơ bản**

*   Cơ chế
    
    *   RPM build dùng **spec file**: định nghĩa metadata, dependency, script pre/post-install, và danh sách file - `rpmbuild` đọc spec + source tarball để tạo ra `.rpm`.
        
    *   `%prep`, `%build`, `%install`, `%files` - 4 section cốt lõi của spec file, tương ứng đúng 4 giai đoạn: chuẩn bị source → compile → "install" vào thư mục tạm (buildroot) → khai báo file nào thuộc package.
        
*   Thực hành và quan sát
    
    *   Viết spec file đơn giản cho 1 script bash của bạn (không cần compile), `rpmbuild -bb file.spec`, cài thử package vừa build.
        
*   Advanced concepts
    
    *   `%post`/`%postun` scriptlet - script chạy tự động sau khi cài/gỡ package (VD tự tạo systemd service, tự enable) - hiểu cơ chế này giải thích vì sao 1 số package "tự cấu hình" sau khi cài mà không cần bạn làm gì thêm.
        

**Ngày 5: Ôn tập tuần 3**

*   Mini-project: dựng 1 VM có user đăng nhập qua SSSD (giả lập hoặc LDAP test server), đồng thời tự build + ký (GPG) + publish 1 RPM package lên repo nội bộ tự dựng, cài package đó qua `dnf` từ chính repo này.
    

**Ngày 6: Buffer/đọc thêm**

*   Đọc tài liệu chính thức Red Hat về `realmd`/`sssd` để mở rộng hiểu biết ngoài phạm vi lab.
    

### Tuần 4: systemd

**Ngày 1: Unit & Target**

*   Cơ chế
    
    *   systemd thay thế SysV init: parallel startup (không tuần tự), dependency-based thay vì runlevel cứng.
        
    *   Unit types: `.service`, `.socket`, `.mount`, `.target`, `.timer` - target là "tập hợp unit", tương đương runlevel cũ nhưng linh hoạt hơn.
        
*   Thực hành và quan sát
    
    *   `systemctl status/start/stop/enable/disable`, `systemctl list-units --type=service`.
        
    *   `systemctl get-default`, đổi default target giữa `multi-user.target` và `graphical.target`.
        
*   Advanced concepts
    
    *   `systemd-analyze blame` - đo thời gian boot của từng service, tìm bottleneck.
        

**Ngày 2: journald**

*   Cơ chế
    
    *   journald lưu log dạng binary có cấu trúc (không phải text thuần như syslog cũ) - cho phép query structured (theo field: `_PID`, `_SYSTEMD_UNIT`...).
        
    *   Volatile (RAM, `/run/log/journal`) vs persistent (`/var/log/journal`) storage.
        
*   Thực hành và quan sát
    
    *   `journalctl -u <service>`, `--since`, `-p err`, `-f` (follow, giống `tail -f`).
        
    *   `journalctl -o json-pretty` xem cấu trúc field thật của 1 log entry.
        
*   Advanced concepts
    
    *   `journalctl --disk-usage`, `SystemMaxUse=` trong `journald.conf` - giới hạn dung lượng log tránh full disk.
        

**Ngày 3: Viết Service Unit**

*   Cơ chế
    
    *   `Type=simple` vs `Type=forking` vs `Type=oneshot` - vì sao systemd cần biết process có tự fork ra background hay không để track đúng PID chính.
        
    *   `ExecStart`, `ExecStop`, `ExecReload`, `WorkingDirectory`, `User=`/`Group=` (chạy service không cần root).
        
*   Thực hành và quan sát
    
    *   Viết 1 unit file service chạy script Python/bash, chạy dưới user riêng (không phải root).
        
*   Advanced concepts
    
    *   `Restart=on-failure`, `RestartSec=` - test bằng cách kill process, quan sát systemd tự restart.
        

**Ngày 4: Timer Unit & Dependency**

*   Cơ chế
    
    *   Timer unit thay cron: `OnCalendar=` (giống cron expression nhưng dễ đọc hơn) vs `OnBootSec=`/`OnUnitActiveSec=` (relative timer).
        
    *   `After=`, `Before=`, `Requires=`, `Wants=` - phân biệt ordering (After/Before) vs dependency thật (Requires/Wants: nếu Requires fail thì unit này cũng fail, Wants thì không).
        
*   Thực hành và quan sát
    
    *   Viết timer unit chạy script mỗi 5 phút, so sánh với crontab tương đương.
        
*   Advanced concepts
    
    *   `systemd-run --on-calendar=... --unit=test` - chạy 1 lệnh theo timer mà không cần viết file unit.
        

**Ngày 5: Resource control qua systemd (cầu nối sang cgroups)**

*   Cơ chế
    
    *   systemd tự động tạo cgroup cho **mỗi unit** - đây là cách RHEL8+ quản lý resource limit hiện đại, thay `ulimit`.
        
    *   `CPUQuota=`, `MemoryMax=`, `TasksMax=` trong unit file - set trực tiếp không cần biết cgroup filesystem thủ công.
        
*   Thực hành và quan sát
    
    *   Thêm `MemoryMax=100M` vào service tự viết, viết chương trình cố tình ăn nhiều RAM, quan sát bị OOM-kill đúng giới hạn.
        
    *   `systemd-cgtop` - xem resource usage theo từng cgroup/unit real-time.
        
*   Advanced concepts
    
    *   `systemctl set-property <unit> CPUQuota=50%` - đổi giới hạn resource **runtime**, không cần restart service.
        

**Ngày 6: Socket activation**

*   Cơ chế
    
    *   Socket activation: systemd **tự mở port thay cho app**, chỉ thực sự khởi động process khi có connection đầu tiên đến - app không cần tự gọi `bind()`/`listen()`, systemd truyền sẵn file descriptor đã bind cho nó.
        
    *   Lợi ích thật: khởi động server nhanh hơn (port sẵn sàng ngay cả khi app chưa load xong - connection được queue chờ), parallel boot tốt hơn (không cần chờ đúng thứ tự service A phải chạy trước service B nếu B chỉ cần port của A đã mở), và app có thể được **restart** mà không mất connection đang chờ (port vẫn do systemd giữ).
        
*   Thực hành và quan sát
    
    *   Viết 1 `.socket` unit + `.service` unit tương ứng, app nhận file descriptor từ systemd (`sd_listen_fds()` hoặc đơn giản hơn: dùng socat/inetd-style demo) thay vì tự bind port.
        
    *   Test: dừng service, gửi request tới port đó - quan sát systemd tự khởi động service khi có connection đến (thay vì connection refused).
        
*   Advanced concepts
    
    *   Đây chính là cơ chế `sshd` **có thể** dùng (dù mặc định RHEL chạy sshd thường trực, không socket-activated) và là cơ chế D-Bus dùng triệt để - hiểu nó giúp giải thích vì sao nhiều service hệ thống "luôn sẵn sàng" dù process thực tế chưa chạy.
        

**Ngày 7: Ôn tập tuần 4**

*   Service của bạn: tự restart khi crash, chạy dưới user riêng, có giới hạn memory qua cgroup, có timer trigger định kỳ.
    

### Tuần 5: Process management

**Ngày 1: ps & process state**

*   Cơ chế
    
    *   Process state: `R` (running), `S` (sleeping, interruptible), `D` (uninterruptible sleep - thường đang chờ I/O, không kill được bằng SIGKILL), `Z` (zombie), `T` (stopped).
        
    *   `VSZ` (virtual memory size) vs `RSS` (resident set size - phần thực sự nằm trong RAM) - vì sao VSZ luôn lớn hơn RSS nhiều.
        
*   Thực hành và quan sát
    
    *   `ps aux`, `ps -ef`, đọc từng cột, so sánh VSZ/RSS của vài process khác nhau.
        
*   Advanced concepts
    
    *   Tự tạo 1 process ở state `D` (VD `dd` đọc từ device chậm) - hiểu vì sao đây là dấu hiệu I/O bottleneck khi troubleshoot production.
        

**Ngày 2: Signals**

*   Cơ chế
    
    *   Signal là cơ chế kernel gửi thông báo bất đồng bộ tới process - mỗi signal có default action (terminate, ignore, core dump, stop).
        
    *   SIGTERM (có thể catch, cho process dọn dẹp trước khi thoát) vs SIGKILL (kernel force kill ngay, process không kịp làm gì) vs SIGHUP (truyền thống: terminal đóng, nay thường dùng để reload config).
        
*   Thực hành và quan sát
    
    *   `kill -l` xem toàn bộ danh sách signal, thử SIGTERM vs SIGKILL vs SIGHUP trên 1 process demo (viết signal handler bằng bash `trap`).
        
*   Advanced concepts
    
    *   Vì sao process ở state `D` không phản hồi SIGKILL - signal chỉ được deliver khi process quay lại từ kernel-space (uninterruptible sleep nghĩa là đang trong 1 syscall kernel không cho phép ngắt).
        

**Ngày 3: Job control**

*   Cơ chế
    
    *   Foreground/background job, process group, controlling terminal - vì sao `Ctrl+C` chỉ gửi SIGINT tới foreground process group.
        
    *   `nohup` - chặn SIGHUP khi terminal đóng, giữ process sống.
        
*   Thực hành và quan sát
    
    *   `&`, `jobs`, `fg/bg`, `nohup command &`, `disown`.
        
*   Advanced concepts
    
    *   `setsid` - tạo session mới hoàn toàn tách khỏi terminal (dùng khi viết daemon thủ công không qua systemd).
        

**Ngày 4: nice/priority & process tree**

*   Cơ chế
    
    *   `nice` value (-20 đến 19) ảnh hưởng đến weight trong CFS scheduler (chi tiết học sâu ở Phase 3) - nice thấp hơn = ưu tiên CPU cao hơn.
        
    *   Process tree: mọi process (trừ PID 1) đều có parent - khi parent chết trước, child bị "reparent" cho PID 1 (hoặc subreaper).
        
*   Thực hành và quan sát
    
    *   `nice`/`renice`, `pstree -p` quan sát cây process thật.
        
*   Advanced concepts
    
    *   Tự tạo 1 zombie process (parent không gọi `wait()`) - quan sát bằng `ps` state `Z`, giải thích vì sao zombie không tốn RAM/CPU nhưng vẫn chiếm 1 PID slot.
        

**Ngày 5: /proc - soi process sống**

*   Cơ chế
    
    *   `/proc/<pid>/` chứa toàn bộ thông tin runtime của 1 process, do kernel expose trực tiếp (không phải file thật trên disk).
        
*   Thực hành và quan sát
    
    *   `/proc/<pid>/status` (memory, state, thread), `/proc/<pid>/cmdline`, `/proc/<pid>/environ`, `/proc/<pid>/fd/` (danh sách file descriptor đang mở).
        
*   Advanced concepts
    
    *   `/proc/<pid>/limits` - xem toàn bộ resource limit áp dụng cho process đó (ulimit + cgroup limit gộp lại).
        

**Ngày 6: Ôn tập tuần 4**

*   Viết script daemon hóa 1 process thủ công bằng `setsid` + xử lý signal đúng cách trong bash (`trap 'cleanup' SIGTERM SIGINT`).
    

**Ngày 7 (mới): strace & ltrace nhập môn**

*   Cơ chế
    
    *   `strace` dùng `ptrace()` để intercept mọi **syscall** của process - cùng cơ chế GDB dùng để attach (sẽ học kỹ ở Tuần 18), nhưng mục đích là log thay vì debug tương tác.
        
    *   `ltrace` intercept **library call** (hàm libc như `malloc`, `strcpy`) thay vì syscall - tầng cao hơn strace.
        
*   Thực hành và quan sát
    
    *   `strace ls` - đọc output, nhận diện `open`, `read`, `write`, `close`, `exit_group`.
        
    *   `strace -c <cmd>` - thống kê số lần gọi + thời gian mỗi loại syscall, tìm syscall tốn thời gian nhất.
        
*   Advanced concepts
    
    *   `strace -f -p <PID>` - attach vào process đang chạy (kể cả child fork ra sau), dùng để debug "tại sao service bị treo" mà không cần restart nó - kỹ năng troubleshooting production cực kỳ thực tế, học sớm để dùng xuyên suốt các phase sau (không cần đợi tới Phase 3 Internals mới biết công cụ này tồn tại).
        

### Tuần 6: Networking cơ bản + review Phase 1

**Ngày 1: Địa chỉ IP & Interface**

*   Cơ chế
    
    *   `ip addr` thay `ifconfig` - vì sao (`ifconfig` thuộc bộ `net-tools` cũ, không hỗ trợ tính năng mạng hiện đại như VRF, network namespace).
        
    *   CIDR notation - `/24` nghĩa là gì, cách tính network address/broadcast address từ IP + subnet mask.
        
*   Thực hành và quan sát
    
    *   `ip addr show`, `ip route show`, gán thêm 1 IP phụ vào interface.
        
*   Advanced concepts
    
    *   Subnetting thực hành: chia 1 mạng `/24` thành 4 mạng `/26`, tính toán network/broadcast/usable range cho từng subnet.
        

**Ngày 2: Routing table**

*   Cơ chế
    
    *   Kernel routing table quyết định packet đi ra interface nào - longest prefix match (route cụ thể nhất thắng, không phải route đầu tiên khớp).
        
    *   Default gateway là 1 route đặc biệt (`0.0.0.0/0`).
        
*   Thực hành và quan sát
    
    *   `ip route show`, thêm 1 static route thủ công giữa 2 VM (`ip route add ... via ...`).
        
*   Advanced concepts
    
    *   `ip route get <IP>` - hỏi kernel "packet tới IP này sẽ đi theo route nào" mà không cần gửi thật, rất hữu ích khi debug routing phức tạp.
        

**Ngày 3: DNS resolution**

*   Cơ chế
    
    *   `/etc/resolv.conf`, `/etc/nsswitch.conf` - thứ tự resolve (files trước hay DNS trước).
        
    *   `/etc/hosts` override cục bộ.
        
*   Thực hành và quan sát
    
    *   `dig`, `host`, `getent hosts` - so sánh 3 công cụ, hiểu `getent` đi qua NSS (tôn trọng `/etc/hosts` + cache), còn `dig` query DNS server trực tiếp bỏ qua NSS.
        
*   Advanced concepts
    
    *   `dig +trace` - xem toàn bộ quá trình resolve từ root DNS server xuống tới authoritative server.
        

**Ngày 4: TCP state & ss**

*   Cơ chế
    
    *   TCP 3-way handshake (SYN → SYN-ACK → ACK), 4-way close (FIN/ACK).
        
    *   TCP state machine: `LISTEN`, `SYN_SENT`, `ESTABLISHED`, `TIME_WAIT` (vì sao tồn tại - tránh packet cũ của connection trước lẫn vào connection mới).
        
*   Thực hành và quan sát
    
    *   `ss -tulnp` (thay `netstat`), đọc từng state, đếm số connection ở `TIME_WAIT` trên 1 server thật.
        
*   Advanced concepts
    
    *   `net.ipv4.tcp_tw_reuse` - sysctl tuning liên quan TIME\_WAIT, khi nào cần chỉnh trong production (server nhiều short-lived connection).
        

**Ngày 5: firewalld cơ bản**

*   Cơ chế
    
    *   Zone-based firewall: mỗi interface thuộc 1 zone (`public`, `internal`, `trusted`...), mỗi zone có policy riêng - khác iptables truyền thống (chain tuyến tính).
        
*   Thực hành và quan sát
    
    *   `firewall-cmd --list-all`, mở 1 port cụ thể (`--add-port=8080/tcp --permanent`), reload.
        
*   Advanced concepts
    
    *   `firewall-cmd --get-active-zones` - xem interface nào đang thuộc zone nào, quan trọng khi server có nhiều NIC (VD 1 NIC public, 1 NIC internal).
        

**Ngày 6: Mini-project 1 - Review toàn bộ Phase 1**

*   Cài đặt 1 VM sạch từ đầu, tạo 3 user với permission/capabilities hợp lý, 1 service tự viết chạy bằng systemd (có giới hạn memory qua cgroup), mở đúng 1 port qua firewalld, ghi log ra journald, verify bằng `ss` + `journalctl`.
    
*   Không nhìn lại note.
    

## Phase 2 - Kỹ năng Production (10 tuần)

### Tuần 7-8: Storage & LVM

**Ngày 1: Partition table**

*   Cơ chế
    
    *   MBR (giới hạn 2TB, 4 partition primary) vs GPT (không giới hạn thực tế, có backup header) - vì sao RHEL hiện đại mặc định GPT.
        
*   Thực hành và quan sát
    
    *   `fdisk`/`parted` tạo partition mới trên disk ảo thêm vào VM.
        
*   Advanced concepts
    
    *   `partprobe` - báo kernel đọc lại partition table mà không cần reboot.
        

**Ngày 2: LVM - Physical Volume**

*   Cơ chế
    
    *   LVM thêm 1 lớp abstraction giữa disk vật lý và filesystem: PV (Physical Volume) → VG (Volume Group, gộp nhiều PV) → LV (Logical Volume, cắt ra từ VG) - cho phép resize linh hoạt không bị giới hạn bởi ranh giới disk vật lý.
        
*   Thực hành và quan sát
    
    *   `pvcreate`, `pvdisplay` trên disk ảo mới thêm.
        
*   Advanced concepts
    
    *   PE (Physical Extent) - đơn vị cấp phát nhỏ nhất trong LVM (mặc định 4MB), mọi resize đều theo bội số PE.
        

**Ngày 3: Volume Group & Logical Volume**

*   Cơ chế
    
    *   VG là "pool" dung lượng gộp từ nhiều PV - LV được cắt ra từ pool này, có thể nằm rải rác trên nhiều disk vật lý khác nhau (linear, striped, hoặc mirrored).
        
*   Thực hành và quan sát
    
    *   `vgcreate`, `lvcreate`, format xfs, mount thủ công.
        
*   Advanced concepts
    
    *   `lvcreate --type striped` - striping qua nhiều disk để tăng throughput (giống RAID 0 ở tầng LVM).
        

**Ngày 4: fstab & persistent mount**

*   Cơ chế
    
    *   UUID vs device path (`/dev/sda1`) trong `/etc/fstab` - vì sao dùng UUID (device path có thể đổi thứ tự giữa các lần boot nếu thêm/bớt disk).
        
*   Thực hành và quan sát
    
    *   Thêm entry vào `/etc/fstab`, test bằng `mount -a`, reboot thật để verify không lỗi (lỗi fstab là nguyên nhân phổ biến khiến server không boot được).
        
*   Advanced concepts
    
    *   `nofail` option - tránh boot bị treo nếu 1 mount point (VD network storage) không sẵn sàng lúc boot.
        

**Ngày 5: Resize LV không downtime**

*   Cơ chế
    
    *   xfs hỗ trợ **online grow** (mở rộng khi đang mount) nhưng **không** hỗ trợ shrink - khác ext4 (hỗ trợ cả hai nhưng phải unmount để shrink).
        
*   Thực hành và quan sát
    
    *   `lvextend` + `xfs_growfs` (không cần unmount) - thực hành mở rộng dung lượng trong khi service vẫn chạy.
        
*   Advanced concepts
    
    *   Giải thích vì sao production luôn ưu tiên "chỉ grow, không shrink" - thiết kế capacity dư ra từ đầu thay vì phụ thuộc shrink.
        

**Ngày 6: Swap**

*   Cơ chế
    
    *   Swap là vùng disk dùng làm "RAM ảo" khi RAM vật lý đầy - kernel chọn page ít dùng nhất để swap out (liên quan LRU, học sâu ở Phase 3).
        
*   Thực hành và quan sát
    
    *   Tạo swap file (`fallocate` + `mkswap` + `swapon`) và swap partition, so sánh cách cấu hình.
        
*   Advanced concepts
    
    *   `vm.swappiness` sysctl - điều chỉnh mức độ "sẵn sàng swap" của kernel (0-100), production DB server thường set thấp để ưu tiên giữ data trong RAM.
        

**Ngày 7 (tuần 7 ngày 1): LVM Snapshot**

*   Cơ chế
    
    *   Snapshot dùng copy-on-write: khi tạo snapshot, không copy toàn bộ data ngay - chỉ khi block gốc bị ghi đè, block cũ mới được copy sang snapshot (tiết kiệm dung lượng).
        
*   Thực hành và quan sát
    
    *   `lvcreate --snapshot`, ghi thêm data vào LV gốc, so sánh nội dung snapshot vs LV gốc, thử rollback (`lvconvert --merge`).
        
*   Advanced concepts
    
    *   Snapshot đầy (hết dung lượng dành riêng cho nó) sẽ tự động invalid - hiểu rủi ro này trước khi dùng snapshot làm backup thật trong production.
        

**Ngày 8: xfs vs ext4**

*   Cơ chế
    
    *   xfs: tối ưu cho file lớn, parallel I/O tốt hơn (RHEL mặc định từ RHEL7). ext4: journal đơn giản hơn, hỗ trợ shrink, phổ biến hơn ngoài RHEL ecosystem.
        
*   Thực hành và quan sát
    
    *   `xfs_info`, `tune2fs -l` (ext4) - đọc metadata filesystem.
        
*   Advanced concepts
    
    *   Journaling mode của ext4 (`data=ordered` mặc định vs `data=journal` - full journal chậm hơn nhưng an toàn hơn khi crash).
        

**Ngày 9: Ôn tập LVM full flow**

*   Thêm ổ đĩa mới vào VM đang chạy → PV → extend VG → extend LV → grow filesystem - toàn bộ không downtime.
    

**Ngày 10: Checkpoint**

*   Viết note giải thích LVM cho người mới (dùng đúng thuật ngữ PV/VG/LV/PE), tự đánh giá độ hiểu bằng cách không nhìn tài liệu.
    

### Tuần 9-10: Networking

**Ngày 1 (T8): NetworkManager**

*   Cơ chế
    
    *   NetworkManager quản lý connection profile (không phải interface trực tiếp) - 1 interface có thể có nhiều profile, switch qua lại.
        
*   Thực hành và quan sát
    
    *   `nmcli con add/mod/up/down`, set static IP qua nmcli (không sửa file trực tiếp).
        
*   Advanced concepts
    
    *   `nmcli con show <name>` - xem toàn bộ property của 1 connection profile, so sánh với file backing thật trong `/etc/NetworkManager/system-connections/`.
        

**Ngày 2: Bonding & Teaming**

*   Cơ chế
    
    *   Bonding: gộp nhiều NIC vật lý thành 1 interface logic - mode `active-backup` (failover) vs `802.3ad`/LACP (load balancing thật, cần switch hỗ trợ).
        
*   Thực hành và quan sát
    
    *   Tạo bond interface từ 2 NIC ảo trong VM (mode active-backup), test failover bằng cách down 1 NIC.
        
*   Advanced concepts
    
    *   Vì sao LACP cần cấu hình đồng bộ ở switch - nếu chỉ cấu hình phía server mà switch không hỗ trợ, bonding sẽ không hoạt động đúng.
        

**Ngày 3: VLAN**

*   Cơ chế
    
    *   VLAN tag (802.1Q) cho phép nhiều mạng logic chạy chung 1 hạ tầng vật lý - trunk port mang nhiều VLAN, access port chỉ 1 VLAN.
        
*   Thực hành và quan sát
    
    *   Tạo VLAN interface (`ip link add link eth0 name eth0.100 type vlan id 100`), test connectivity.
        
*   Advanced concepts
    
    *   Vì sao VLAN là nền tảng để hiểu network isolation trong container/k8s sau này (mỗi namespace network tương tự về mặt khái niệm cô lập).
        

**Ngày 4: nftables**

*   Cơ chế
    
    *   nftables thay iptables (RHEL9+ mặc định): 1 framework thống nhất thay vì 4 công cụ riêng (iptables/ip6tables/arptables/ebtables). Table → chain → rule.
        
*   Thực hành và quan sát
    
    *   `nft list ruleset`, viết 1 table/chain custom chặn 1 IP cụ thể.
        
*   Advanced concepts
    
    *   `nft` set - nhóm nhiều IP/port vào 1 set để rule gọn hơn, update động không cần reload toàn bộ ruleset (khác iptables cũ).
        

**Ngày 5: firewalld + nftables backend**

*   Cơ chế
    
    *   firewalld trên RHEL9+ dùng nftables làm backend (trước đó là iptables) - firewalld là lớp abstraction cao hơn, nft là cơ chế thực thi thật bên dưới.
        
*   Thực hành và quan sát
    
    *   `firewall-cmd --direct` để chèn rule nft thô khi zone-based không đủ linh hoạt.
        
*   Advanced concepts
    
    *   `nft list ruleset` sau khi cấu hình firewalld - đối chiếu để hiểu firewalld dịch config của nó thành nft rule cụ thể như thế nào.
        

**Ngày 6: Ôn tập T8**

*   2 VM, firewall chỉ mở đúng port cần dùng nftables trực tiếp, verify bằng `tcpdump`.
    

**Ngày 7 (T9): tcpdump & packet analysis**

*   Cơ chế
    
    *   `tcpdump` capture packet ở tầng kernel (dùng `libpcap`/AF\_PACKET socket) trước khi bị firewall lọc hoặc sau - filter expression BPF (Berkeley Packet Filter).
        
*   Thực hành và quan sát
    
    *   `tcpdump -i eth0 port 80 -w capture.pcap`, đọc output cơ bản (SYN/ACK flag).
        
*   Advanced concepts
    
    *   Capture ở 2 điểm khác nhau (client + server) cùng lúc - kỹ thuật debug "packet có đến nơi không" kinh điển trong production networking.
        

**Ngày 8: Network namespace**

*   Cơ chế
    
    *   Network namespace: mỗi namespace có bảng routing, interface, firewall rule **hoàn toàn riêng** - đây chính là nền tảng networking của container.
        
*   Thực hành và quan sát
    
    *   `ip netns add test1`, tạo veth pair nối namespace với host, cấu hình IP, test ping qua lại.
        
*   Advanced concepts
    
    *   Đây là bài tập quan trọng nhất tuần: hiểu network namespace = hiểu cách Docker/Podman tạo network riêng cho mỗi container mà không cần VM thật.
        

**Ngày 9: DNS server thật (BIND hoặc unbound cơ bản)**

*   Cơ chế
    
    *   Recursive resolver vs authoritative server - vì sao 1 DNS server công ty thường chỉ làm resolver (forward ra ngoài) chứ không authoritative cho domain public.
        
*   Thực hành và quan sát
    
    *   Cài `unbound` hoặc `bind`, cấu hình forward zone đơn giản, test resolve qua server tự dựng.
        
*   Advanced concepts
    
    *   DNS caching TTL - vì sao đổi DNS record không có hiệu lực ngay lập tức trên toàn mạng.
        

**Ngày 10: chrony & time sync**

*   Cơ chế
    
    *   Time drift: đồng hồ phần cứng server luôn trôi dần theo thời gian (crystal oscillator không hoàn hảo) - nếu không đồng bộ, chênh lệch tích lũy vài giây tới vài phút sau nhiều ngày.
        
    *   Vì sao lệch giờ gây sự cố nghiêm trọng: Kerberos ticket có timestamp check (lệch >5 phút mặc định là auth fail ngay), TLS certificate validation dựa vào thời gian hệ thống, log correlation giữa nhiều server sai lệch làm troubleshooting gần như bất khả thi, distributed database (etcd, Cassandra...) có thể corrupt data nếu lệch giờ nghiêm trọng.
        
    *   chrony (thay `ntpd` cũ trên RHEL hiện đại): đồng bộ nhanh hơn, xử lý tốt hơn với mạng không ổn định hoặc VM (VM thường có clock drift lớn hơn máy vật lý do CPU steal time).
        
*   Thực hành và quan sát
    
    *   `chronyc sources -v` - xem nguồn NTP đang dùng, độ lệch hiện tại.
        
    *   `chronyc tracking` - xem chi tiết offset, đồng hồ hệ thống đang nhanh/chậm bao nhiêu.
        
    *   Cố tình đổi giờ hệ thống sai (`date -s`), quan sát chrony tự điều chỉnh dần (không nhảy đột ngột - tránh phá vỡ ứng dụng dựa vào thời gian đơn điệu tăng).
        
*   Advanced concepts
    
    *   `makestep` trong `chrony.conf` - cấu hình khi nào chrony được phép "nhảy" giờ đột ngột (VD chỉ lúc boot) thay vì luôn điều chỉnh từ từ - quan trọng vì nhảy giờ đột ngột giữa lúc service đang chạy có thể gây lỗi logic (VD lock timeout tính sai).
        

**Ngày 11: Mini-project - Network troubleshooting layer-by-layer**

*   Giả lập lỗi network (sai route, sai firewall rule, sai DNS) trên 2 VM, tự chẩn đoán bằng cách đi từng layer: `ip addr` (L2/L3 config đúng chưa) → `ip route` → `ping` (L3 connectivity) → `ss`/`nc` (L4 port) → `curl`/`dig` (L7). Không hint.
    

### Tuần 11: SELinux

**Ngày 1: MAC vs DAC**

*   Cơ chế
    
    *   DAC (Discretionary Access Control - permission rwx truyền thống): owner tự quyết định ai được access. MAC (Mandatory Access Control - SELinux): policy hệ thống quyết định, **owner không override được**.
        
    *   Vì sao đây là lớp bảo vệ thứ 2 quan trọng: dù 1 process bị exploit và có quyền root, SELinux policy vẫn có thể chặn nó làm việc ngoài phạm vi được định nghĩa.
        
*   Thực hành và quan sát
    
    *   `getenforce`, `sestatus` - xem mode hiện tại (Enforcing/Permissive/Disabled).
        
*   Advanced concepts
    
    *   Vì sao **tuyệt đối không nên** `setenforce 0` trong production để "cho nhanh" - đây là thói quen sai phổ biến nhất khi mới học SELinux.
        

**Ngày 2: Security Context**

*   Cơ chế
    
    *   Context format: `user:role:type:level` - trong đó **type** (VD `httpd_sys_content_t`) là phần quan trọng nhất, quyết định policy áp dụng.
        
*   Thực hành và quan sát
    
    *   `ls -Z`, `ps -Z` - xem context của file và process.
        
*   Advanced concepts
    
    *   Type Enforcement: policy định nghĩa "type nào của process được phép access type nào của file/resource" - đây là core mechanism, không phải theo user như DAC.
        

**Ngày 3: Context mismatch thực tế**

*   Cơ chế
    
    *   Khi copy file vào thư mục mới, context **không tự đổi theo thư mục** trừ khi dùng `mv` trong cùng filesystem hoặc explicit relabel - đây là nguyên nhân phổ biến nhất gây lỗi "403 do SELinux" dù permission rwx đúng.
        
*   Thực hành và quan sát
    
    *   Đổi web root sang thư mục custom, copy file HTML vào, quan sát lỗi 403 dù chmod đúng.
        
*   Advanced concepts
    
    *   `cp -Z` vs `cp` thường - so sánh context của file sau copy.
        

**Ngày 4: Đọc AVC denial**

*   Cơ chế
    
    *   AVC (Access Vector Cache) denial được ghi vào audit log mỗi khi SELinux chặn 1 hành động - đây là "bằng chứng" để fix đúng thay vì đoán mò.
        
*   Thực hành và quan sát
    
    *   `ausearch -m avc -ts recent`, `sealert -a /var/log/audit/audit.log` (đọc gợi ý fix bằng ngôn ngữ tự nhiên).
        
*   Advanced concepts
    
    *   `audit2allow -a` - sinh policy module tùy chỉnh từ log denial (dùng cẩn thận, chỉ khi hiểu rõ denial đó có hợp lý hay không).
        

**Ngày 5: Fix đúng cách**

*   Cơ chế
    
    *   `semanage fcontext` định nghĩa **rule** context cho 1 path pattern (persistent qua relabel), còn `chcon` chỉ đổi context **tạm thời** (mất khi relabel toàn hệ thống).
        
*   Thực hành và quan sát
    
    *   `semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"`, sau đó `restorecon -Rv /custom/web` để áp dụng.
        
*   Advanced concepts
    
    *   Boolean SELinux (`getsebool -a`, `setsebool`) - bật/tắt 1 nhóm hành vi cụ thể (VD `httpd_can_network_connect`) mà không cần viết policy module riêng.
        

**Ngày 6: Viết SELinux policy module từ đầu**

*   Cơ chế
    
    *   Khác `audit2allow` (sinh policy từ log denial - reactive, dễ tạo policy quá rộng nếu không kiểm tra kỹ), viết policy module thủ công (`.te` file - Type Enforcement) cho phép định nghĩa chính xác type mới + rule allow cụ thể ngay từ đầu - cách làm đúng chuẩn khi phát triển 1 ứng dụng cần chạy dưới SELinux enforcing với custom behavior.
        
*   Thực hành và quan sát
    
    *   Viết 1 file `.te` đơn giản định nghĩa 1 type mới cho ứng dụng tự viết (VD `myapp_t`), rule cho phép nó network connect + đọc 1 thư mục cụ thể.
        
    *   `checkmodule`, `semodule_package`, `semodule -i` - quy trình compile và install policy module.
        
*   Advanced concepts
    
    *   So sánh policy bạn viết thủ công (chỉ đúng rule cần thiết) với policy `audit2allow` sinh ra tự động (thường quá rộng vì chỉ dựa trên denial log, không hiểu ý định thật của ứng dụng) - đây là lý do các RHEL policy chính thức (`selinux-policy` package) luôn viết tay bởi maintainer, không sinh tự động.
        

**Ngày 7: Ôn tập tuần 11**

*   Dựng service custom port + custom web root, để SELinux chặn tự nhiên, tự fix bằng đúng quy trình: đọc AVC → `semanage fcontext` → `restorecon` → verify, không dùng `setenforce 0`.
    

### Tuần 12: cgroups - Resource Control

**Ngày 1: Khái niệm cgroup & cgroup v1 vs v2**

*   Cơ chế
    
    *   cgroup (control group) là cơ chế **kernel** nhóm các process lại để giới hạn/đo lường resource (CPU, memory, I/O, số lượng process) - đây chính là nền tảng mà container (Docker/Podman) dùng để cô lập resource, **không phải** namespace (namespace lo việc cô lập "nhìn thấy gì", cgroup lo việc "được dùng bao nhiêu").
        
    *   cgroup v1: mỗi controller (cpu, memory...) có hierarchy riêng, phức tạp khi quản lý đồng thời. cgroup v2 (mặc định RHEL8+/9): **unified hierarchy** - 1 cây duy nhất cho tất cả controller, đơn giản và nhất quán hơn nhiều.
        
*   Thực hành và quan sát
    
    *   `mount | grep cgroup` - xem cgroup v2 mount ở đâu (`/sys/fs/cgroup`).
        
    *   `cat /sys/fs/cgroup/cgroup.controllers` - danh sách controller khả dụng.
        
*   Advanced concepts
    
    *   `systemd` chính là cgroup v2 manager mặc định trên RHEL hiện đại - mọi service systemd tự động có 1 cgroup riêng (đã chạm ở Tuần 4 Ngày 5, giờ đào sâu cơ chế thật).
        

**Ngày 2: CPU controller**

*   Cơ chế
    
    *   `cpu.max` (cgroup v2): giới hạn CPU time trong 1 chu kỳ (VD `50000 100000` = 50% của 1 core).
        
    *   `cpu.weight`: phân bổ CPU **tương đối** giữa các cgroup khi tranh chấp tài nguyên (khác `cpu.max` là giới hạn tuyệt đối).
        
*   Thực hành và quan sát
    
    *   Tạo 1 cgroup thủ công (`mkdir /sys/fs/cgroup/mytest`), thêm PID của 1 chương trình stress-CPU tự viết vào `cgroup.procs`, set `cpu.max`, quan sát bằng `top` - CPU usage bị giới hạn đúng %.
        
*   Advanced concepts
    
    *   So sánh `cpu.max` (hard limit, throttle cứng) với `nice`/priority (chỉ ảnh hưởng khi có tranh chấp, không giới hạn tuyệt đối) - 2 cơ chế hoàn toàn khác nhau dù cùng liên quan đến CPU.
        

**Ngày 3: Memory controller**

*   Cơ chế
    
    *   `memory.max`: hard limit, process bị OOM-kill nếu vượt quá - khác `memory.high`: soft limit, kernel sẽ throttle/reclaim memory trước, không kill ngay.
        
    *   `memory.current`, `memory.stat` - breakdown chi tiết loại memory đang dùng (anon, file cache, kernel stack...).
        
*   Thực hành và quan sát
    
    *   Viết chương trình C nhỏ liên tục `malloc` không giới hạn, chạy trong cgroup có `memory.max=50M`, quan sát bị kill và log trong `dmesg`.
        
*   Advanced concepts
    
    *   `memory.oom.group` - khi bật, toàn bộ process trong cgroup bị kill cùng lúc khi OOM (thay vì chỉ 1 process) - dùng khi các process trong cùng cgroup phụ thuộc lẫn nhau (VD multi-process app).
        

**Ngày 4: I/O & PIDs controller**

*   Cơ chế
    
    *   `io.max`: giới hạn throughput đọc/ghi (BPS) hoặc IOPS theo từng device, tránh 1 process "nuốt" hết I/O bandwidth ảnh hưởng process khác trên cùng disk.
        
    *   `pids.max`: giới hạn số process/thread tối đa trong cgroup - chống fork-bomb hiệu quả hơn `ulimit -u` truyền thống (ulimit áp dụng per-user, dễ bị bypass bằng cách chạy nhiều session).
        
*   Thực hành và quan sát
    
    *   Set `pids.max=10`, thử chạy fork-bomb (`:(){ :|:& };:`) trong cgroup đó - quan sát nó bị chặn ngay thay vì làm treo cả server.
        
*   Advanced concepts
    
    *   So sánh mức độ an toàn: fork-bomb ngoài cgroup có thể làm treo toàn bộ VM; fork-bomb trong cgroup có `pids.max` chỉ crash trong phạm vi cgroup đó.
        

**Ngày 5: systemd + cgroup - resource control thực chiến**

*   Cơ chế
    
    *   Ôn lại và đào sâu Tuần 4 Ngày 5: `systemd-cgtop`, `systemctl status <unit>` hiện luôn cgroup path - mọi `MemoryMax=`/`CPUQuota=` trong unit file chính là wrapper của cgroup filesystem operation vừa học thủ công.
        
*   Thực hành và quan sát
    
    *   `systemd-cgls` - xem toàn bộ cây cgroup của hệ thống (mọi service, mọi user session, mọi container nếu có).
        
    *   Tạo 1 slice riêng (`systemctl edit --force myapp.slice`) để nhóm nhiều service con vào 1 giới hạn resource chung.
        
*   Advanced concepts
    
    *   Liên hệ trực tiếp tới container: khi chạy `podman run --memory=100m`, Podman thực chất tạo 1 cgroup với `memory.max=100m` - không có "phép màu" nào khác, chỉ là cùng cơ chế bạn vừa làm thủ công.
        

**Ngày 6: Ôn tập tuần 11**

*   Mini-project: viết 1 service systemd chạy app tự viết, giới hạn đồng thời CPU (`cpu.max` qua `CPUQuota=`), memory (`memory.max` qua `MemoryMax=`), và số process con (`pids.max` qua `TasksMax=`) - verify từng giới hạn bằng cách cố tình vượt ngưỡng và quan sát hành vi kernel.
    

### Tuần 13: Logging & Backup

**Ngày 1: rsyslog**

*   Cơ chế
    
    *   rsyslog là syslog daemon hiện đại - hỗ trợ filter theo facility/priority, forward log qua TCP/UDP tới server tập trung (centralized logging).
        
*   Thực hành và quan sát
    
    *   Cấu hình rsyslog forward log sang 1 VM khác (đóng vai trò log server).
        
*   Advanced concepts
    
    *   `RELP` protocol - reliable log forwarding (đảm bảo không mất log ngay cả khi network gián đoạn tạm thời), khác UDP syslog truyền thống có thể mất packet.
        

**Ngày 2: journald persistent & giới hạn**

*   Cơ chế
    
    *   `Storage=persistent` trong `journald.conf` - quyết định log sống sót qua reboot hay không.
        
*   Thực hành và quan sát
    
    *   `SystemMaxUse=`, `SystemKeepFree=` - giới hạn dung lượng, test bằng cách generate log lớn.
        
*   Advanced concepts
    
    *   `journalctl --vacuum-size=500M` - chủ động dọn log cũ, dùng trong script maintenance định kỳ.
        

**Ngày 3: logrotate**

*   Cơ chế
    
    *   logrotate dùng cho log của app không qua journald (VD app tự ghi file log riêng) - rotate theo size hoặc thời gian, có thể nén, giữ N bản cũ.
        
*   Thực hành và quan sát
    
    *   Viết config `/etc/logrotate.d/myapp` cho app tự viết, test bằng `logrotate -f`.
        
*   Advanced concepts
    
    *   `postrotate` script - gửi signal cho app reload file handle sau khi rotate (nhiều app giữ file descriptor cũ nếu không được báo).
        

**Ngày 4: Chiến lược backup**

*   Cơ chế
    
    *   `rsync` dùng delta algorithm - chỉ transfer phần thay đổi của file, không copy toàn bộ mỗi lần (khác `cp`/`tar` full copy).
        
    *   `tar` incremental (`--listed-incremental`) - backup đầy đủ lần đầu, sau đó chỉ backup phần thay đổi.
        
*   Thực hành và quan sát
    
    *   `rsync -avz --delete` giữa 2 VM, `tar` incremental backup 2 lần liên tiếp, so sánh dung lượng.
        
*   Advanced concepts
    
    *   `rsync --dry-run` - luôn test trước khi chạy lệnh có `--delete` (xóa file đích không có ở nguồn) trong production.
        

**Ngày 5: Script backup tự động + test restore**

*   Thực hành
    
    *   Viết script backup + systemd timer chạy định kỳ.
        
    *   **Test restore thật** - quan trọng hơn cả việc backup, vì backup không test restore = không backup.
        
*   Advanced concepts
    
    *   Backup 3-2-1 rule: 3 bản copy, 2 loại media khác nhau, 1 bản off-site - nguyên tắc kinh điển dù đơn giản.
        

**Ngày 6: Ôn tập - Mini disaster recovery**

*   Giả lập mất dữ liệu (xóa nhầm thư mục quan trọng), restore từ backup, đo thời gian recovery (RTO - Recovery Time Objective).
    

### Tuần 14: Container Primitives

**Ngày 1: chroot**

*   Cơ chế
    
    *   `chroot` đổi **root directory** (`/`) mà 1 process nhìn thấy - process bên trong không thể truy cập file ngoài thư mục chroot (về mặt path resolution), nhưng **không cô lập** process ID, network, hay user (đây chỉ là 1 phần rất nhỏ của "container thật").
        
    *   Giới hạn bảo mật nổi tiếng: chroot **không phải** security boundary hoàn chỉnh - process có quyền root bên trong chroot vẫn có thể "thoát" ra ngoài bằng nhiều kỹ thuật (đây là lý do container thật cần thêm namespace + capabilities).
        
*   Thực hành và quan sát
    
    *   Tạo 1 thư mục chứa mini root filesystem (copy `/bin/bash` + thư viện cần thiết bằng `ldd` đã học), `chroot` vào đó, quan sát `ls /` chỉ thấy nội dung thư mục con.
        
*   Advanced concepts
    
    *   Thử `ps aux` bên trong chroot - vẫn thấy toàn bộ process của host, chứng minh chroot không cô lập PID (cần PID namespace, học ngày sau).
        

**Ngày 2: pivot\_root**

*   Cơ chế
    
    *   `pivot_root` khác `chroot`: nó thực sự **đổi root filesystem** ở mức mount point (swap old root và new root), dọn sạch old root hoàn toàn khỏi tầm nhìn - đây là cơ chế container runtime thật (runc, containerd) dùng, không phải `chroot` đơn thuần.
        
*   Thực hành và quan sát
    
    *   Thực hành `pivot_root` trong 1 mount namespace riêng (kết hợp `unshare` ngày sau) - quan sát old root biến mất hoàn toàn khỏi `/proc/mounts` sau khi unmount.
        
*   Advanced concepts
    
    *   Vì sao `pivot_root` yêu cầu new root phải là 1 mount point riêng biệt (không thể trực tiếp trên rootfs hiện tại) - ràng buộc kỹ thuật quan trọng khi tự viết container runtime.
        

**Ngày 3: unshare - tự tạo namespace**

*   Cơ chế
    
    *   `unshare` tạo 1 hoặc nhiều namespace mới cho process - ôn lại PID namespace, network namespace (đã học Tuần 10 Ngày 8, giờ dùng trực tiếp), mount namespace, UTS namespace (hostname riêng), IPC namespace, user namespace.
        
*   Thực hành và quan sát
    
    *   `unshare --pid --fork --mount-proc bash` - vào 1 shell với PID namespace riêng, `ps aux` bên trong giờ chỉ thấy vài process (PID 1 là chính bash này).
        
    *   `unshare --uts bash`, đổi hostname bên trong (`hostname test123`) - verify hostname ngoài host không đổi.
        
*   Advanced concepts
    
    *   `unshare --user --map-root-user bash` - user namespace: bên trong thấy mình là UID 0 (root) nhưng bên ngoài host thực chất là UID không đặc quyền - chính cơ chế Podman rootless dùng (đã nhắc ở Tuần 14 cũ, giờ thấy rõ cơ chế thật).
        

**Ngày 4: nsenter - bước vào namespace có sẵn**

*   Cơ chế
    
    *   `nsenter` cho phép 1 process **join vào namespace đã tồn tại** của process khác (ngược lại với `unshare` là tạo namespace mới) - dùng để debug container đang chạy từ host mà không cần `exec` vào qua Podman/Docker.
        
*   Thực hành và quan sát
    
    *   Tìm PID của 1 container Podman đang chạy (`podman inspect --format '{{.State.Pid}}'`), dùng `nsenter -t <PID> -n -p` để "bước vào" đúng network + PID namespace của nó từ host trực tiếp bằng tool Linux thuần, không qua `podman exec`.
        
*   Advanced concepts
    
    *   Đây chính xác là cách `podman exec`/`docker exec` hoạt động bên dưới - không có phép màu, chỉ là gọi `nsenter` (hoặc syscall tương đương `setns()`) vào namespace của container process.
        

**Ngày 5: Tự dựng "container mini"**

*   Thực hành - dự án chính của ngày
    
    *   Kết hợp toàn bộ: `unshare` (PID + mount + UTS + network namespace) + `pivot_root` (đổi root filesystem) + cgroup thủ công (`cgroup.procs` + `memory.max`, đã học Tuần 11) + `chroot`/`pivot_root` vào 1 rootfs mini (VD Alpine minirootfs tải sẵn) → chạy 1 process bên trong với: PID riêng, hostname riêng, filesystem riêng, giới hạn memory riêng.
        
    *   Đây chính là toàn bộ "công thức" của container - không dùng Podman/Docker, chỉ dùng primitive Linux thuần túy.
        
*   Advanced concepts
    
    *   So sánh độ phức tạp: bạn vừa làm bằng tay trong 1 ngày những gì `runc` (low-level container runtime thật, mà Podman/Docker/Kubernetes đều dùng bên dưới) làm tự động - hiểu rõ container runtime "thật ra làm gì" chứ không phải hộp đen.
        

**Ngày 6: Ôn tập tuần 13**

*   Viết script bash tự động hóa toàn bộ "container mini" ở Ngày 5 thành 1 lệnh duy nhất (giống 1 phiên bản `runc` siêu đơn giản của riêng bạn).
    

### Tuần 15: Container Internals & Podman

**Ngày 1: Podman vs Docker - kiến trúc**

*   Cơ chế
    
    *   Docker dùng daemon chạy nền (dockerd) với quyền root - mọi lệnh docker đều đi qua daemon này (single point of failure + security risk).
        
    *   Podman **daemonless**: mỗi container là process con trực tiếp của lệnh `podman run`, không qua daemon trung gian - dùng chính namespace + cgroup vừa học ở Tuần 10 & 12.
        
*   Thực hành và quan sát
    
    *   `podman info`, so sánh kiến trúc bằng cách quan sát `ps aux` khi container đang chạy (thấy container process là con trực tiếp, không phải con của 1 daemon).
        
*   Advanced concepts
    
    *   Rootless container: Podman chạy container không cần root, dùng **user namespace** (UID trong container map tới UID không đặc quyền ở host) - bảo mật tốt hơn Docker truyền thống đáng kể.
        

**Ngày 2: Container đầu tiên**

*   Cơ chế
    
    *   Image layer: mỗi instruction trong Containerfile tạo 1 layer riêng, các layer là read-only và **chia sẻ được** giữa nhiều image (tiết kiệm dung lượng, tăng tốc build/pull).
        
*   Thực hành và quan sát
    
    *   `podman run`, `podman ps/logs/exec -it`.
        
*   Advanced concepts
    
    *   `podman inspect` - xem chi tiết cgroup path, namespace ID của 1 container đang chạy, đối chiếu trực tiếp với kiến thức Tuần 10/12.
        

**Ngày 3: Containerfile**

*   Cơ chế
    
    *   Build cache: Docker/Podman cache từng layer, chỉ rebuild từ layer bị thay đổi trở đi - vì sao thứ tự instruction trong Containerfile ảnh hưởng tốc độ build (đặt `COPY` code sau `RUN` install dependency).
        
*   Thực hành và quan sát
    
    *   Viết Containerfile cho app tự viết, build, quan sát cache khi build lại sau khi chỉ sửa code.
        
*   Advanced concepts
    
    *   Multi-stage build - dùng 1 stage để build, chỉ copy artifact cần thiết sang stage runtime cuối, giảm kích thước image production đáng kể.
        

**Ngày 4: systemd + Podman**

*   Thực hành và quan sát
    
    *   `podman generate systemd` - tạo unit file quản lý container bằng systemd (kết hợp trực tiếp kiến thức Tuần 3).
        
*   Advanced concepts
    
    *   Quadlet (RHEL9+) - cách hiện đại hơn để định nghĩa container như 1 systemd unit trực tiếp bằng file `.container`, không cần generate thủ công.
        

**Ngày 5: Volume & Network trong Podman**

*   Cơ chế
    
    *   Volume: bind mount (map trực tiếp thư mục host) vs named volume (Podman tự quản lý location) - khi nào dùng loại nào.
        
    *   Container network: Podman tự tạo network namespace riêng cho mỗi container (trừ khi dùng `--network=host`), NAT ra ngoài qua bridge.
        
*   Thực hành và quan sát
    
    *   Mount volume cho app cần persist data, tạo network riêng cho nhiều container giao tiếp với nhau qua tên container (DNS nội bộ Podman).
        
*   Advanced concepts
    
    *   `podman network inspect` - đối chiếu trực tiếp với `ip netns` đã học Tuần 10 Ngày 8, thấy rõ container network chỉ là network namespace + veth pair được tự động hóa.
        

**Ngày 6: Ôn tập tuần 13**

*   Chạy 1 app thật (VD web app + DB) trong 2 container riêng, kết nối qua Podman network, quản lý cả 2 bằng systemd, verify resource limit qua cgroup.
    

**Ngày 7 (mới, thêm vào cuối tuần): Đối chiếu Podman với primitive tuần trước**

*   Thực hành và quan sát
    
    *   `podman run` 1 container, dùng `nsenter` (học Tuần 14 Ngày 4) để tự "bước vào" namespace của nó từ host, không qua `podman exec`.
        
    *   `cat /sys/fs/cgroup/.../memory.max` của cgroup mà Podman tự tạo cho container đó - đối chiếu với thao tác thủ công ở Tuần 11.
        
*   Advanced concepts
    
    *   Kết luận toàn bộ mạch: Podman = tự động hóa (namespace qua `unshare`\-equivalent syscall + `pivot_root` + cgroup + image layer management) - mọi thứ bạn đã tự tay làm ở Tuần 13, giờ chỉ là 1 lệnh `podman run`.
        

### Tuần 16: Performance & Troubleshooting

**Ngày 1: vmstat**

*   Cơ chế
    
    *   Cột `r` (process chờ CPU), `b` (process blocked I/O), `si/so` (swap in/out) - đọc đúng thứ tự ưu tiên khi chẩn đoán bottleneck.
        
*   Thực hành và quan sát
    
    *   `vmstat 2` chạy liên tục trong khi stress-test CPU/memory, đọc thay đổi real-time.
        
*   Advanced concepts
    
    *   `si/so` khác 0 liên tục là dấu hiệu **rất xấu** (thrashing) - RAM thực sự thiếu, không phải vấn đề tạm thời.
        

**Ngày 2: iostat & sar**

*   Cơ chế
    
    *   `%util` trong iostat gần 100% không hẳn là disk quá tải nếu là SSD (khác HDD) - cần đối chiếu với `await` (thời gian chờ trung bình) để kết luận đúng.
        
*   Thực hành và quan sát
    
    *   `iostat -x 2`, cấu hình `sar` thu thập số liệu lịch sử (`sysstat` service) để so sánh xu hướng theo thời gian.
        
*   Advanced concepts
    
    *   `sar -n DEV` - lịch sử network throughput, hữu ích khi cần chứng minh "server chậm vào giờ nào trong ngày" cho báo cáo incident.
        

**Ngày 3: Tìm process bất thường**

*   Thực hành và quan sát
    
    *   `ps aux --sort=-%mem`, `ps aux --sort=-%cpu`, kết hợp `/proc/<pid>/status` đã học Tuần 5.
        
*   Advanced concepts
    
    *   `pidstat` - breakdown CPU/memory theo từng **thread** của 1 process (hữu ích khi app multi-thread có 1 thread cụ thể bị leak).
        

**Ngày 4: iotop & phân biệt CPU-bound vs I/O-bound**

*   Cơ chế
    
    *   CPU-bound: `%CPU` cao, process ở state `R`. I/O-bound: `%CPU` thấp nhưng process ở state `D` thường xuyên, `iostat` cho thấy `await` cao.
        
*   Thực hành và quan sát
    
    *   `iotop -o` - chỉ hiện process đang thực sự có I/O.
        
*   Advanced concepts
    
    *   Kết hợp `vmstat` + `iostat` + `top` cùng lúc trong 1 buổi troubleshooting thật - không công cụ đơn lẻ nào đủ, luôn cần đối chiếu nhiều nguồn.
        

**Ngày 5: Xây checklist troubleshooting**

*   Tổng hợp toàn bộ Phase 2 thành 1 checklist cá nhân: "server chậm → check gì trước, gì sau" (CPU → memory/swap → I/O → network, theo đúng thứ tự loại trừ).
    

**Ngày 6: sosreport**

*   Cơ chế
    
    *   `sosreport` là công cụ chính thức của Red Hat: tự động thu thập hàng trăm file config/log/output lệnh chẩn đoán (dmesg, journalctl, rpm -qa, network config, SELinux status...) thành 1 archive duy nhất - đây là thứ **đầu tiên Red Hat Support yêu cầu** khi bạn mở ticket hỗ trợ.
        
*   Thực hành và quan sát
    
    *   `sosreport` (hoặc `sos report` trên bản mới), chạy thử, giải nén archive kết quả, khám phá cấu trúc thư mục - nhận ra phần lớn nội dung chính là output của các lệnh bạn đã học suốt từ Phase 1 (chỉ là được tự động hóa và đóng gói).
        
*   Advanced concepts
    
    *   `sosreport -o <plugin>` - chỉ chạy 1 plugin cụ thể (VD chỉ network, chỉ SELinux) khi không cần full report, hoặc `--case-id` để gắn số ticket khi gửi Red Hat Support.
        

**Ngày 7: tuned**

*   Cơ chế
    
    *   `tuned`: daemon áp dụng **profile tối ưu hệ thống** theo use-case (throughput-performance, latency-performance, virtual-guest, powersave...) - mỗi profile chỉnh hàng loạt sysctl/I/O scheduler/CPU governor cùng lúc thay vì tự set thủ công từng cái.
        
*   Thực hành và quan sát
    
    *   `tuned-adm list`, `tuned-adm active`, `tuned-adm profile throughput-performance` - đổi profile, quan sát thay đổi (VD `cat /proc/sys/vm/swappiness` trước/sau).
        
*   Advanced concepts
    
    *   `tuned-adm recommend` - tuned tự đề xuất profile phù hợp dựa trên phát hiện môi trường (VM, bare-metal, laptop) - và viết 1 custom profile riêng cho ứng dụng đặc thù (kế thừa từ 1 profile có sẵn, override vài tham số).
        

**Ngày 8: Mini-project 2 - Chẩn đoán không hint**

*   VM bị "load cao giả lập" (script stress CPU/IO/network ẩn), tự chẩn đoán nguyên nhân bằng công cụ đã học, viết báo cáo root cause như 1 incident report thật.
    

**Ngày 9: Ghi chú cầu nối - kexec/kdump** Phase 4 (Tuần 35-36) có kdump đầy đủ (cấu hình, trigger crash, phân tích bằng `crash`). Theo đề xuất "nối mạch production → kernel", nên thêm 1 đoạn **giới thiệu khái niệm** (chưa cần lab sâu) ngay tại cuối Phase 2:

*   Cơ chế (giới thiệu, lab đầy đủ vẫn ở Phase 4)
    
    *   `kexec`: cơ chế boot thẳng vào 1 kernel mới **từ bên trong kernel đang chạy**, bỏ qua toàn bộ BIOS/bootloader - vì sao nhanh hơn reboot thật nhiều (bỏ qua POST, firmware init).
        
    *   `kdump` dùng chính cơ chế `kexec`: khi kernel chính panic, nó `kexec` ngay vào 1 **kernel dự phòng nhỏ** đã load sẵn trong vùng RAM riêng, kernel dự phòng này dump memory của kernel bị crash ra disk rồi mới reboot thật.
        
*   Ghi chú
    
    *   Đây là lý do kdump cần 1 phần RAM riêng dành sẵn (`crashkernel=` trong GRUB) - kernel dự phòng phải chạy được ngay cả khi kernel chính vừa crash, không thể dùng chung vùng nhớ.
        
    *   Lab thực hành đầy đủ (cấu hình, trigger, phân tích bằng `crash`) → xem Phase 4 Tuần 35-36.
        

## Phase 2.5 - Toolchain, Debugging & Git

### Tuần 17: Compilation Pipeline & ELF

**Ngày 1: Cơ chế biên dịch, liên kết và vòng đời của một chương trình C++**

*   Pipeline Biên dịch
    
    *   Preprocessing: Cách `#include`, `#define`, `#ifdef` hoạt động. Hiểu "Translation Unit".
        
    *   Compilation: Parsing, Code Generation. Cách trình biên dịch chuyển mã nguồn thành Assembly.
        
    *   Assembling: Chuyển Assembly thành mã máy (Object file `.o`/`.obj`). Cấu trúc Object file.
        
    *   Linking: Static vs dynamic linking (DLL/SO). Symbol table và lỗi `undefined reference`.
        
*   Thực hành và quan sát
    
    *   CLI: biên dịch tay với `g++`/`clang++` (flag `-E`, `-S`, `-c` để xem từng bước).
        
    *   Tách chương trình thành nhiều `.cpp`/`.h`. Tự tạo thư viện tĩnh (`.a`) và liên kết.
        
*   Advanced concepts
    
    *   One Definition Rule (ODR): tại sao quan trọng để tránh lỗi liên kết.
        
    *   Link-Time Optimization (LTO): tối ưu hóa xuyên file.
        

**Ngày 2: Cấu trúc ELF**

*   Cơ chế
    
    *   ELF header, Program header table (dùng khi **load** - kernel/loader đọc để biết map segment nào vào đâu), Section header table (dùng khi **link/debug** - không cần thiết để chạy).
        
    *   Segment (`.text`, `.data`, `.bss`, `.rodata`) - vì sao `.bss` không chiếm dung lượng file dù chiếm RAM lúc runtime.
        
*   Thực hành và quan sát
    
    *   `readelf -h`, `readelf -S`, `readelf -l` trên 1 binary tự compile - đối chiếu từng phần với lý thuyết.
        
    *   `size <binary>` - xem nhanh kích thước text/data/bss.
        
*   Advanced concepts
    
    *   `file <binary>` - cách nó nhận diện ELF 32/64-bit, kiến trúc CPU, static/dynamic linked chỉ từ vài byte đầu (magic number).
        

**Ngày 3: objdump & nm - soi mã máy**

*   Cơ chế
    
    *   Disassembly: chuyển ngược mã máy về assembly - vì sao không thể khôi phục 100% source gốc (mất tên biến, comment, cấu trúc control-flow cấp cao).
        
    *   Symbol table: local vs global symbol, vì sao `static` function trong C không xuất hiện khi link từ file khác.
        
*   Thực hành và quan sát
    
    *   `objdump -d <binary>` - đọc disassembly của 1 hàm C đơn giản, đối chiếu với source.
        
    *   `nm <binary>` - phân loại symbol (`T` text/defined, `U` undefined, `t` local).
        
*   Advanced concepts
    
    *   `objdump -d -S` - interleave source code với assembly (cần compile bằng `-g`) - kỹ thuật dùng nhiều khi optimize hot path.
        

**Ngày 4: Dynamic linking & ldd**

*   Cơ chế
    
    *   Dynamic linker (`ld.so`) resolve symbol **lúc runtime**, không phải lúc link - đây là lý do 1 binary dynamic-linked nhỏ hơn nhiều nhưng cần đúng version thư viện trên máy chạy.
        
    *   `LD_LIBRARY_PATH`, `rpath`, `/etc/ld.so.conf` - thứ tự tìm kiếm thư viện `.so`.
        
*   Thực hành và quan sát
    
    *   `ldd <binary>` - xem toàn bộ thư viện dependency, thử xóa/đổi tên 1 `.so` xem lỗi `error while loading shared libraries`.
        
    *   Tự build 1 thư viện `.so`, link chương trình dùng nó, quan sát bằng `ldd`.
        
*   Advanced concepts
    
    *   `LD_PRELOAD` - inject 1 thư viện override function có sẵn (VD override `malloc` để log mọi lần cấp phát) - kỹ thuật debug/hack kinh điển, cũng là nền tảng hiểu 1 số malware hoạt động.
        

**Ngày 5: Makefile cơ bản**

*   Cơ chế
    
    *   Make dùng dependency graph + timestamp - chỉ rebuild target có dependency mới hơn nó, không rebuild toàn bộ mỗi lần (khác build script tuần tự).
        
*   Thực hành và quan sát
    
    *   Viết Makefile cho project nhiều file `.cpp`, dùng pattern rule (`%.o: %.cpp`).
        
*   Advanced concepts
    
    *   `make -j$(nproc)` - build parallel, và vì sao cần khai báo đúng dependency (thiếu dependency → race condition khi build parallel, lỗi rất khó debug).
        

**Ngày 6: Ôn tập tuần 15**

*   Từ 1 project C nhiều file: build bằng tay từng bước (preprocess → compile → assemble → link) không dùng Makefile, sau đó viết lại bằng Makefile, đối chiếu.
    
*   `readelf`/`objdump`/`nm`/`ldd` trên chính binary vừa build - giải thích được từng phần không cần tra cứu.
    

### Tuần 18: GDB & Core Dump

**Ngày 1: GDB cơ bản**

*   Cơ chế
    
    *   Breakpoint hoạt động bằng cách GDB ghi đè 1 byte đầu instruction bằng `int3` (x86) - khi CPU chạy tới đó, trap vào GDB, GDB khôi phục byte gốc để hiển thị đúng.
        
*   Thực hành và quan sát
    
    *   Compile với `-g` (giữ debug symbol), `gdb ./binary`, set breakpoint, `step`/`next`/`continue`, in giá trị biến (`print`).
        
*   Advanced concepts
    
    *   `-g` levels (`-g1/-g2/-g3`) - tradeoff giữa kích thước binary và độ chi tiết debug info.
        

**Ngày 2: GDB nâng cao - watchpoint & backtrace**

*   Cơ chế
    
    *   Watchpoint: GDB dùng hardware debug register (không phải software trap) để dừng khi 1 vùng nhớ thay đổi - hiệu quả hơn nhiều so với step qua từng dòng để tìm chỗ biến bị đổi.
        
*   Thực hành và quan sát
    
    *   `watch <variable>`, `backtrace` (xem call stack đầy đủ khi crash hoặc dừng tại breakpoint).
        
    *   Debug 1 chương trình có bug thật (VD buffer overflow tự tạo) bằng GDB.
        
*   Advanced concepts
    
    *   `bt full` - in cả local variable của từng frame trong call stack, hữu ích khi crash sâu trong nhiều lớp hàm gọi nhau.
        

**Ngày 3: Core dump - cơ chế**

*   Cơ chế
    
    *   Core dump: khi process nhận signal gây crash (SIGSEGV, SIGABRT...) mà không có handler, kernel dump toàn bộ memory image của process ra file - chứa đủ thông tin để debug **sau khi** process đã chết (khác GDB attach vào process đang sống).
        
    *   `ulimit -c` - giới hạn kích thước core dump (mặc định 0 = tắt trên nhiều distro).
        
*   Thực hành và quan sát
    
    *   `ulimit -c unlimited`, chạy chương trình cố tình segfault, tìm file core (`/var/lib/systemd/coredump/` trên RHEL hiện đại thay vì file `core` cùng thư mục).
        
*   Advanced concepts
    
    *   `/proc/sys/kernel/core_pattern` - kernel parameter quyết định core dump được ghi ở đâu/theo format nào (systemd-coredump can thiệp vào đây).
        

**Ngày 4: Phân tích core dump bằng GDB**

*   Thực hành và quan sát
    
    *   `gdb ./binary core` - load core dump, `backtrace` để tìm chính xác dòng code gây crash dù process đã chết từ lâu.
        
    *   `coredumpctl list`, `coredumpctl gdb <PID>` - cách RHEL hiện đại quản lý core dump qua systemd-coredump, tiện hơn tìm file thủ công.
        
*   Advanced concepts
    
    *   Core dump của binary đã optimize (`-O2`) thường khó debug hơn nhiều (biến bị inline/loại bỏ) - vì sao production build đôi khi giữ lại `-g` dù vẫn optimize, tách riêng debug symbol ra file `.debug` (`objcopy --only-keep-debug`).
        

**Ngày 5: GDB attach vào process đang chạy**

*   Cơ chế
    
    *   `gdb -p <PID>` - GDB dùng `ptrace()` syscall để attach vào process sống, tạm dừng nó lại - cùng nền tảng `ptrace` mà `strace` (đã làm quen ở Tuần 5, sẽ đào sâu ở Tuần 22-23) dùng để trace syscall.
        
*   Thực hành và quan sát
    
    *   Attach vào 1 service đang chạy (không phải production thật!), đặt breakpoint, quan sát service "đứng hình" cho tới khi `continue`.
        
*   Advanced concepts
    
    *   Vì sao **không nên** attach GDB vào production service tùy tiện - process bị dừng hoàn toàn trong lúc GDB attach, có thể gây timeout/cascade failure ở service phụ thuộc.
        

**Ngày 6: Ôn tập tuần 16**

*   Cố tình viết 1 chương trình có bug (use-after-free hoặc buffer overflow), để nó tự crash tạo core dump, dùng `coredumpctl` + GDB xác định chính xác dòng code lỗi mà không nhìn source trước.
    

### Tuần 19: Git

**Ngày 1: Object model của Git**

*   Cơ chế
    
    *   Git lưu data dạng 4 loại object: blob (nội dung file), tree (thư mục), commit (snapshot + metadata), tag - mỗi object được hash bằng SHA (content-addressable storage, giống hệ thống file dedupe).
        
    *   Commit không lưu "diff" - mỗi commit là **snapshot toàn bộ** cây thư mục tại thời điểm đó (diff chỉ là cách hiển thị, tính toán lúc `git log -p`).
        
*   Thực hành và quan sát
    
    *   `git cat-file -p <hash>` - xem trực tiếp nội dung object (blob/tree/commit) không qua lệnh git thông thường.
        
    *   `git log --oneline --graph` để thấy cấu trúc DAG (Directed Acyclic Graph) của commit history.
        
*   Advanced concepts
    
    *   `.git/objects` - tự tìm 1 object bằng tay qua hash prefix, hiểu vì sao Git nén object cũ vào packfile định kỳ (`git gc`).
        

**Ngày 2: Branching & Merge**

*   Cơ chế
    
    *   Branch chỉ là 1 con trỏ (file text chứa commit hash) trỏ vào 1 commit - cực nhẹ, đây là lý do Git khuyến khích branch liên tục (khác SVN branch nặng).
        
    *   Merge commit có **2 parent** - khác fast-forward merge (không tạo commit mới, chỉ di chuyển con trỏ branch).
        
*   Thực hành và quan sát
    
    *   Tạo conflict thật giữa 2 branch, tự resolve, hiểu ký hiệu `<<<<<<<`, `=======`, `>>>>>>>`.
        
*   Advanced concepts
    
    *   `git merge --no-ff` - ép tạo merge commit dù có thể fast-forward, giữ lại lịch sử rõ ràng feature branch nào merge khi nào (dùng trong quy trình release nghiêm ngặt).
        

**Ngày 3: Rebase**

*   Cơ chế
    
    *   Rebase **viết lại lịch sử**: lấy từng commit trên branch của bạn, áp lại (replay) lên đầu 1 base mới - mỗi commit sau rebase có hash mới hoàn toàn (vì parent thay đổi → hash thay đổi, do commit hash tính từ content + parent).
        
    *   Vì sao **không rebase branch đã push public** - người khác đã pull commit cũ, rebase tạo commit trùng nội dung nhưng khác hash → history phân kỳ.
        
*   Thực hành và quan sát
    
    *   `git rebase -i HEAD~3` - interactive rebase: squash, reword, reorder commit.
        
*   Advanced concepts
    
    *   `git rebase --onto` - rebase 1 đoạn commit cụ thể sang base khác, không phải toàn bộ branch (dùng khi cherry-pick nguyên cụm commit sang nhánh khác).
        

**Ngày 4: Bisect**

*   Cơ chế
    
    *   `git bisect` dùng binary search trên lịch sử commit để tìm chính xác commit gây ra bug - O(log n) thay vì kiểm tra tuần tự từng commit.
        
*   Thực hành và quan sát
    
    *   Tạo 1 repo giả có bug được insert ở 1 commit giữa lịch sử 20 commit, dùng `git bisect start/good/bad` để tìm ra.
        
*   Advanced concepts
    
    *   `git bisect run <script>` - tự động hóa hoàn toàn: script trả exit code 0 (good) hoặc 1 (bad), Git tự chạy qua toàn bộ history và báo commit lỗi - dùng thật khi debug kernel regression (chính kernel dev dùng kỹ thuật này thường xuyên).
        

**Ngày 5: format-patch & am - quy trình gửi patch kiểu kernel**

*   Cơ chế
    
    *   Kernel Linux **không dùng Pull Request** như GitHub - quy trình chuẩn là tạo patch file bằng `git format-patch`, gửi qua email (`git send-email`) tới mailing list, maintainer review qua email, `git am` để apply patch nhận được.
        
*   Thực hành và quan sát
    
    *   `git format-patch -1 HEAD` - tạo file `.patch` từ 1 commit, xem cấu trúc file (header email-style + diff).
        
    *   `git am < file.patch` trên 1 repo khác - áp dụng patch nhận được, giữ nguyên author/message gốc.
        
*   Advanced concepts
    
    *   `git format-patch --cover-letter` - tạo patch series nhiều commit kèm 1 email mô tả tổng quan, đúng chuẩn khi gửi feature lớn lên LKML (sẽ dùng thật ở Phase 6 khi gửi patch kernel).
        

**Ngày 6: Ôn tập tuần 17**

*   Mini-project: tạo 1 repo giả lập quy trình kernel contribution - vài commit trên branch riêng, rebase sạch sẽ lên `main`, tạo patch series bằng `format-patch --cover-letter`, tự `git am` sang 1 clone khác để verify patch áp dụng đúng.
    

## Phase 3 - Linux Internals (9 tuần)

### Tuần 20: Process Model

**Ngày 1: Process là gì - đọc OSTEP**

*   Cơ chế
    
    *   Đọc OSTEP chương Process (miễn phí, remzi.github.io) - process là 1 abstraction: chương trình đang chạy có riêng virtual CPU + virtual memory, dù CPU vật lý bị chia sẻ giữa hàng trăm process qua context switch.
        
    *   Process Control Block (PCB) - kernel lưu toàn bộ state của 1 process (registers, program counter, memory mapping) ở đâu khi nó không chạy.
        
*   Thực hành và quan sát
    
    *   `ps -eLf` - đếm số process đang chạy trên máy, so sánh với số CPU core thật.
        
*   Advanced concepts
    
    *   Vì sao "process" và "program" là 2 khái niệm khác nhau - 1 program (file thực thi trên disk) có thể chạy thành nhiều process cùng lúc (VD mở 3 tab Chrome).
        

**Ngày 2: fork()/exec()/wait()**

*   Cơ chế
    
    *   `fork()` tạo bản sao gần như y hệt process gọi nó (copy-on-write, không copy thật ngay) - trả về 2 lần: 0 cho child, PID con cho parent.
        
    *   `exec()` thay thế hoàn toàn image của process hiện tại bằng chương trình mới (không tạo process mới) - đây là lý do `fork()` + `exec()` tách biệt cho phép làm việc ở giữa (redirect stdout trước khi exec, VD shell pipe).
        
*   Thực hành và quan sát
    
    *   Viết chương trình C nhỏ dùng `fork()`, phân biệt code chạy ở nhánh parent vs child dựa trên giá trị trả về, quan sát bằng `strace -f`.
        
*   Advanced concepts
    
    *   `wait()`/`waitpid()` - vì sao parent cần gọi `wait()` để "thu hoạch" exit status của child, nếu không child trở thành zombie (đã học Phase 1 Tuần 5, giờ thấy rõ cơ chế syscall gốc).
        

**Ngày 3: Process states & context switch**

*   Cơ chế
    
    *   Context switch: kernel lưu toàn bộ register state của process đang chạy vào PCB, load state của process tiếp theo - tốn thời gian thật (vài microsecond), đây là lý do quá nhiều thread/process gây overhead.
        
    *   Đọc `/proc/<pid>/status` - giải thích từng field (`State`, `VmRSS`, `Threads`, `voluntary_ctxt_switches`).
        
*   Thực hành và quan sát
    
    *   So sánh `voluntary_ctxt_switches` (process tự nhường CPU, VD chờ I/O) vs `nonvoluntary_ctxt_switches` (bị scheduler ngắt giữa chừng) của vài process khác nhau.
        
*   Advanced concepts
    
    *   Vì sao context switch giữa 2 thread cùng process rẻ hơn giữa 2 process khác nhau - thread chia sẻ page table, không cần flush TLB (Translation Lookaside Buffer).
        

**Ngày 4: Virtual memory khái niệm**

*   Cơ chế
    
    *   Đọc OSTEP chương Memory Virtualization - mỗi process có không gian địa chỉ ảo riêng (0 đến giới hạn), kernel dùng page table để dịch địa chỉ ảo sang địa chỉ vật lý thật.
        
    *   Paging: chia memory thành các trang cố định (thường 4KB) - vì sao paging tốt hơn cấp phát liên tục (tránh external fragmentation).
        
*   Thực hành và quan sát
    
    *   `getconf PAGE_SIZE` - xem kích thước page mặc định của hệ thống.
        
*   Advanced concepts
    
    *   Multi-level page table - vì sao không dùng 1 bảng dịch phẳng khổng lồ (tốn RAM khủng khiếp với không gian địa chỉ 64-bit), mà chia thành nhiều cấp (4 cấp trên x86-64).
        

**Ngày 5: Soi vùng nhớ process thật**

*   Cơ chế
    
    *   `/proc/<pid>/maps` liệt kê từng vùng nhớ (segment) của process: địa chỉ bắt đầu-kết thúc, quyền (rwxp), file backing (nếu có).
        
*   Thực hành và quan sát
    
    *   `/proc/<pid>/maps`, `pmap -x <pid>` - soi vùng nhớ 1 process thật (VD `sleep 100 &` rồi pmap PID đó), nhận diện heap, stack, shared library.
        
*   Advanced concepts
    
    *   Vùng `[stack]` phát triển xuống (địa chỉ giảm dần), heap phát triển lên - đây là lý do 2 vùng này thường được đặt ở 2 đầu không gian địa chỉ, tối đa hóa khoảng trống có thể grow.
        

**Ngày 6: mmap() trực tiếp**

*   Cơ chế
    
    *   `mmap()` map trực tiếp 1 vùng memory (có thể backing bởi file hoặc anonymous/RAM thuần) vào không gian địa chỉ process - `malloc()` với block lớn thực chất gọi `mmap()` bên dưới (glibc), block nhỏ dùng `brk()`/heap.
        
*   Thực hành và quan sát
    
    *   Viết chương trình dùng `mmap()` trực tiếp thay vì `malloc`, quan sát vùng nhớ mới xuất hiện trong `/proc/self/maps` ngay sau khi gọi.
        
*   Advanced concepts
    
    *   `MAP_SHARED` vs `MAP_PRIVATE` - shared memory giữa 2 process không liên quan dùng chính flag này (nền tảng của IPC shared memory sẽ học ở Tuần 27-28).
        

### Tuần 21: Memory sâu, OOM, Namespaces

**Ngày 1: OOM killer**

*   Cơ chế
    
    *   Khi hệ thống hết RAM (kể cả swap), kernel không thể từ chối cấp phát mãi - OOM killer chọn 1 process để kill nhằm giải phóng memory, tránh toàn hệ thống deadlock.
        
    *   `oom_score` được tính từ nhiều yếu tố (memory usage, thời gian chạy, `oom_score_adj`) - process dùng nhiều RAM nhất chưa chắc bị kill nếu có `oom_score_adj` thấp.
        
*   Thực hành và quan sát
    
    *   `/proc/<pid>/oom_score`, `/proc/<pid>/oom_score_adj` - chỉnh giảm khả năng bị kill cho 1 service quan trọng (VD database).
        
*   Advanced concepts
    
    *   `echo f > /proc/sysrq-trigger` - trigger OOM killer thủ công để test hành vi mà không cần thực sự làm cạn RAM.
        

**Ngày 2: Cgroups v2 & memory reclaim (góc nhìn kernel)**

*   Cơ chế
    
    *   Ôn lại cgroups (đã học sâu Phase 2 Tuần 12) từ góc độ kernel: `memory.max` không chỉ là con số giới hạn - khi chạm ngưỡng, kernel trigger reclaim (drop page cache, swap) trước khi tính đến OOM-kill trong cgroup đó.
        
*   Thực hành và quan sát
    
    *   Giới hạn memory 1 process bằng cgroup thủ công, quan sát `memory.stat` để thấy `reclaim` diễn ra trước khi process bị kill.
        
*   Advanced concepts
    
    *   Page cache (file cache) tính vào `memory.current` nhưng **có thể reclaim được** (không như anon memory) - đây là lý do `memory.max` không tương đương RSS thô.
        

**Ngày 3: Namespaces sâu hơn (góc nhìn kernel)**

*   Cơ chế
    
    *   Ôn lại (đã dùng thực chiến ở Phase 2 Tuần 14 Container Primitives) từ góc kernel: mỗi namespace type có 1 cấu trúc dữ liệu kernel riêng (`struct pid_namespace`, `struct net`...) - `clone()` với flag tương ứng (`CLONE_NEWPID`, `CLONE_NEWNET`...) là cách kernel thật sự tạo namespace mới.
        
*   Thực hành và quan sát
    
    *   `unshare --pid --fork bash`, quan sát `/proc/self/ns/` - mỗi symlink trỏ tới 1 inode đại diện cho namespace mà process đang thuộc về.
        
*   Advanced concepts
    
    *   2 process share cùng namespace nếu symlink trong `/proc/<pid>/ns/` có cùng inode number - đây là cách kernel/tool kiểm tra 2 container có cùng network namespace hay không.
        

**Ngày 4: Ôn tập process lifecycle**

*   Thực hành
    
    *   Giải thích bằng lời (không nhìn note) toàn bộ vòng đời 1 process từ `fork()` → `exec()` → chạy → `exit()` → `wait()` bởi parent, kèm theo trạng thái memory/namespace tại mỗi bước.
        

**Ngày 5: Buffer/ôn tập mở rộng**

*   Thực hành
    
    *   Viết 1 chương trình C dùng `fork()` + `mmap(MAP_SHARED)` để 2 process (parent/child) chia sẻ 1 vùng nhớ, giao tiếp qua đó (không dùng pipe) - bài tập kết hợp toàn bộ tuần.
        

### Tuần 22: Filesystem internals

**Ngày 1: VFS layer**

*   Cơ chế
    
    *   Đọc OSTEP chương File System - VFS (Virtual File System) là lớp abstraction cho phép `read()`/`write()` hoạt động giống nhau bất kể filesystem thật bên dưới là xfs, ext4, hay NFS - mỗi filesystem implement 1 bộ operations (`read`, `write`, `lookup`...) mà VFS gọi qua function pointer.
        
*   Thực hành và quan sát
    
    *   `cat /proc/filesystems` - liệt kê toàn bộ filesystem type kernel hiện tại hỗ trợ.
        
*   Advanced concepts
    
    *   VFS là lý do bạn có thể `mount` 1 USB FAT32, ext4 disk, và NFS share cùng lúc và dùng chung 1 bộ lệnh `ls`/`cp` - ứng dụng userspace hoàn toàn không biết filesystem thật bên dưới là gì.
        

**Ngày 2: inode vs dentry**

*   Cơ chế
    
    *   inode chứa metadata thật của file (owner, permission, size, con trỏ tới block dữ liệu) - **không chứa tên file**. dentry (directory entry) là cấu trúc map tên file → inode, đây là lý do 1 file có thể có nhiều tên (hardlink).
        
*   Thực hành và quan sát
    
    *   Tạo 2 hardlink cùng inode (`ln file1 file2`), quan sát bằng `stat file1 file2` (cùng số inode), `ls -i`.
        
*   Advanced concepts
    
    *   Xóa `file1` không xóa data thật - inode chỉ bị giải phóng khi **link count về 0** (`ls -l` cột thứ 2 chính là link count) - đây là cơ chế thật của "xóa file" trên Linux.
        

**Ngày 3: Journaling filesystem**

*   Cơ chế
    
    *   Journaling: trước khi ghi thay đổi thật vào filesystem, ghi 1 bản "ý định" (journal/log) trước - nếu crash giữa chừng, khi mount lại filesystem replay journal để hoàn tất hoặc rollback thao tác dang dở, tránh corrupt.
        
    *   xfs/ext4 đều journal metadata mặc định; ext4 có tùy chọn journal cả data (`data=journal`, chậm hơn nhưng an toàn hơn).
        
*   Thực hành và quan sát
    
    *   `dmesg | grep -i journal` sau khi reboot "cứng" 1 VM (force reset) - thấy filesystem tự động replay journal lúc mount.
        
*   Advanced concepts
    
    *   Journaling bảo vệ **filesystem consistency**, không bảo vệ **data mới nhất chưa flush** - đây là lý do `fsync()` vẫn cần thiết cho ứng dụng cần đảm bảo data đã ghi xuống đĩa thật.
        

**Ngày 4: Block layer & I/O scheduler**

*   Cơ chế
    
    *   Block layer nằm giữa filesystem và device driver - gom, sắp xếp lại I/O request (merge các request liền kề, reorder theo vị trí trên đĩa) trước khi gửi xuống device thật, đặc biệt quan trọng với HDD (giảm seek time).
        
*   Thực hành và quan sát
    
    *   `cat /sys/block/sda/queue/scheduler` - xem I/O scheduler đang dùng (`mq-deadline`, `none`/noop cho SSD/NVMe, `bfq` cho desktop).
        
*   Advanced concepts
    
    *   SSD/NVMe thường dùng scheduler `none` vì không có "seek time" thật để tối ưu - việc reorder request chỉ thêm overhead không cần thiết cho non-rotational storage.
        

**Ngày 5: strace sâu - đếm syscall I/O**

*   Cơ chế
    
    *   Ôn lại `strace` (đã làm quen Phase 1 Tuần 5) ở mức sâu hơn: mỗi lệnh `cp` thực chất là chuỗi syscall `openat` → nhiều lần `read`/`write` theo block size → `close`.
        
*   Thực hành và quan sát
    
    *   `strace -c cp bigfile.txt copy.txt` - đếm số lần gọi + tổng thời gian mỗi loại syscall, xác định `read`/`write` chiếm bao nhiêu % tổng thời gian.
        
*   Advanced concepts
    
    *   So sánh số lần syscall `read`/`write` khi `cp` file 100MB với block size mặc định của `cp` (thường 128KB) - tính ra số lần gọi lý thuyết, đối chiếu với `strace -c` thực tế.
        

**Ngày 6: dd & block size**

*   Cơ chế
    
    *   `dd` cho phép chỉnh `bs=` (block size) - block size nhỏ (4K) tạo nhiều syscall hơn (overhead per-call cao), block size lớn (1M) giảm số syscall nhưng có thể không tận dụng hết bandwidth nếu quá lớn so với queue depth của device.
        
*   Thực hành và quan sát
    
    *   `dd if=/dev/zero of=test bs=4K count=100000`, rồi `bs=1M count=400` (cùng tổng dung lượng) - đo throughput bằng flag `status=progress`, so sánh.
        
*   Advanced concepts
    
    *   Giải thích vì sao I/O size ảnh hưởng performance không tuyến tính - có 1 "sweet spot" (thường 128K-1M cho SSD) mà vượt qua đó throughput không tăng thêm đáng kể.
        

### Tuần 23: perf, ftrace & tổng hợp I/O path

**Ngày 1: perf cơ bản**

*   Cơ chế
    
    *   `perf` dùng hardware performance counter (CPU cycle, cache miss, branch misprediction) có sẵn trong CPU - cho phép đo hiệu năng ở mức rất thấp mà gần như không có overhead (khác `strace` phải dừng process ở mỗi syscall).
        
*   Thực hành và quan sát
    
    *   `perf top` - xem function nào đang tốn CPU nhất real-time trên toàn hệ thống.
        
    *   `perf stat <cmd>` trên 1 chương trình CPU-bound tự viết (VD tính số nguyên tố brute-force) - đọc `instructions`, `cycles`, `cache-misses`.
        
*   Advanced concepts
    
    *   IPC (Instructions Per Cycle) tính từ `instructions/cycles` - IPC thấp thường là dấu hiệu chương trình bị nghẽn ở cache miss hoặc branch misprediction, không phải thiếu CPU power.
        

**Ngày 2: ftrace giới thiệu**

*   Cơ chế
    
    *   `ftrace`: framework tracing tích hợp sẵn trong kernel (không cần công cụ ngoài), cho phép trace function call bên trong kernel qua `/sys/kernel/debug/tracing/` - nền tảng mà nhiều công cụ cấp cao hơn (kể cả `perf`) dựa vào.
        
*   Thực hành và quan sát
    
    *   `echo function > /sys/kernel/debug/tracing/current_tracer`, `cat /sys/kernel/debug/tracing/trace` - xem stream function call trong kernel (mức quan sát, chưa cần hiểu code).
        
*   Advanced concepts
    
    *   `trace-cmd` - wrapper thân thiện hơn cho ftrace, tương tự cách `bpftrace` (sẽ học Tuần 24) là wrapper thân thiện cho eBPF.
        

**Ngày 3: Vẽ lại toàn bộ I/O path**

*   Thực hành
    
    *   Tự vẽ sơ đồ (giấy hoặc tool vẽ) đầy đủ: app gọi `read()` → syscall trap → VFS → filesystem (xfs/ext4) → block layer/I/O scheduler → device driver → disk thật, và chiều ngược lại khi data trả về.
        

**Ngày 4: Checkpoint - viết bài giải thích**

*   Thực hành
    
    *   Viết 1 bài (không cần publish) giải thích "điều gì xảy ra khi bạn gõ `cat file.txt`" từ syscall đến disk, dùng đúng thuật ngữ đã học suốt Tuần 22-23 (VFS, inode, dentry, journal, block layer, scheduler).
        

**Ngày 5: Buffer/ôn tập**

*   Thực hành
    
    *   Chạy `perf record`/`perf report` trên chính chương trình `dd`/`cp` đã test ở Tuần 22, xác nhận phần lớn thời gian nằm ở syscall `read`/`write` (kernel-space) chứ không phải code userspace.
        

### Tuần 24: eBPF & bpftrace

**Ngày 1: Khái niệm eBPF**

*   Cơ chế
    
    *   eBPF (extended Berkeley Packet Filter): cho phép chạy chương trình nhỏ **bên trong kernel** một cách an toàn (verified bởi eBPF verifier trước khi load - không thể crash kernel hay vòng lặp vô hạn) mà không cần viết kernel module hay recompile kernel.
        
    *   So với `ftrace`/`perf` đã học: những công cụ đó có sẵn tracepoint cố định; eBPF cho phép viết logic **tùy ý** tại các hook point đó (filter, aggregate, histogram ngay trong kernel trước khi trả dữ liệu ra userspace - hiệu năng cao hơn nhiều so với trace toàn bộ rồi xử lý ở userspace).
        
*   Thực hành và quan sát
    
    *   `bpftool prog list` - xem các eBPF program đang chạy sẵn trong kernel (nhiều thứ RHEL đã dùng eBPF nội bộ, VD firewall/network policy hiện đại).
        
*   Advanced concepts
    
    *   eBPF dùng rộng rãi ngoài tracing: networking (Cilium trong Kubernetes), security (Falco), packet filtering hiệu năng cao (XDP - eXpress Data Path, xử lý packet trước cả khi vào network stack thông thường).
        

**Ngày 2: bpftrace - ngôn ngữ tracing cấp cao**

*   Cơ chế
    
    *   `bpftrace` là ngôn ngữ script cấp cao (giống `awk` cho tracing) compile xuống eBPF bytecode - không cần viết eBPF bằng C/verifier thủ công.
        
*   Thực hành và quan sát
    
    *   `bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'` - trace mọi lệnh gọi `open` trên toàn hệ thống real-time, in ra tên process + file đang mở.
        
*   Advanced concepts
    
    *   So sánh với `strace`: `strace` chỉ trace 1 process cụ thể và có overhead lớn (dừng process ở mỗi syscall); `bpftrace` trace **toàn hệ thống** với overhead thấp hơn nhiều - công cụ đúng cho production (không thể `strace` toàn bộ server, nhưng có thể chạy `bpftrace` an toàn).
        

**Ngày 3: One-liner thực chiến**

*   Thực hành và quan sát
    
    *   Chạy các bpftrace one-liner kinh điển (có sẵn trong `bpftrace` examples): đo latency syscall theo histogram, đếm số lần page fault theo process, trace TCP connection mới real-time toàn hệ thống.
        
*   Advanced concepts
    
    *   `bpftrace -l 'tracepoint:*'` - khám phá toàn bộ tracepoint khả dụng trong kernel đang chạy, hiểu đây chính là các "điểm móc" mà ftrace/perf/bpftrace đều dựa vào.
        

**Ngày 4: Viết script bpftrace cho use-case thật**

*   Thực hành - dự án
    
    *   Viết 1 script bpftrace theo dõi latency của 1 loại syscall cụ thể (VD `read`) cho 1 process cụ thể, xuất histogram phân bố latency - bài toán thật khi production cần biết "I/O có bao giờ bị spike latency không, tần suất thế nào" mà `strace` không trả lời được hiệu quả ở quy mô lớn.
        
*   Advanced concepts
    
    *   So sánh kết quả với `iostat`/`vmstat` đã học ở Phase 2 - eBPF cho độ chi tiết per-syscall, per-process mà công cụ aggregate truyền thống không có.
        

**Ngày 5: XDP khái niệm**

*   Cơ chế
    
    *   XDP: eBPF program chạy ở tầng driver network, **trước khi** packet đi vào kernel network stack - cho phép drop/redirect packet với latency cực thấp (dùng trong DDoS mitigation, load balancer hiệu năng cao như Cilium/Katran của Facebook).
        
*   Thực hành và quan sát
    
    *   Đọc 1 ví dụ XDP program đơn giản (drop packet theo điều kiện), không cần tự viết từ đầu - mục tiêu là hiểu vị trí XDP nằm ở đâu trong packet flow đã học Tuần 9-10 (Networking).
        
*   Advanced concepts
    
    *   Liên hệ với `nftables`/netfilter hooks đã học: XDP nằm **trước** netfilter hoàn toàn, packet có thể bị drop ở XDP mà không bao giờ chạm tới iptables/nftables rule.
        

**Ngày 6: Ôn tập tuần eBPF**

*   Mini-project: viết 1 bpftrace script hoàn chỉnh giải quyết 1 bài toán troubleshooting cụ thể tự đặt ra (VD "tìm process nào đang mở file nhiều nhất trong 10 giây"), so sánh cách làm tương đương (khó hơn nhiều) nếu chỉ dùng `strace`/`lsof`.
    

### Tuần 25: Scheduler & Boot process

**Ngày 1: CFS - Completely Fair Scheduler**

*   Cơ chế
    
    *   CFS không dùng time-slice cố định - nó theo dõi `vruntime` (virtual runtime) của từng process/thread, luôn chọn process có `vruntime` thấp nhất để chạy tiếp, đảm bảo mọi process nhận CPU "công bằng" theo thời gian.
        
    *   `nice` value ảnh hưởng đến **weight** dùng để tính tốc độ tăng `vruntime` - nice thấp hơn (ưu tiên cao hơn) khiến `vruntime` tăng chậm hơn, nên được chọn chạy thường xuyên hơn.
        
*   Thực hành và quan sát
    
    *   Chạy 2 process CPU-bound cùng lúc với `nice` khác nhau (VD 0 và 19), quan sát tỷ lệ CPU time bằng `top`.
        
*   Advanced concepts
    
    *   CFS dùng cấu trúc dữ liệu **red-black tree** để luôn tìm process có `vruntime` thấp nhất trong O(log n) - lựa chọn cấu trúc dữ liệu này là lý do CFS scale tốt với hàng nghìn process.
        

**Ngày 2: Realtime scheduling**

*   Cơ chế
    
    *   `SCHED_FIFO`/`SCHED_RR` (realtime) khác hẳn `SCHED_OTHER` (CFS mặc định) - process realtime **luôn được ưu tiên** hơn mọi process CFS, không bị `vruntime` chi phối, dùng cho ứng dụng cần latency cực thấp và có thể đoán trước (audio processing, control system).
        
*   Thực hành và quan sát
    
    *   `chrt -f 50 <cmd>` - chạy 1 chương trình với `SCHED_FIFO` priority 50, quan sát bằng `chrt -p <pid>`.
        
*   Advanced concepts
    
    *   Rủi ro thật: 1 process `SCHED_FIFO` chạy vòng lặp vô hạn có thể **chiếm CPU vĩnh viễn**, khiến toàn bộ process CFS khác (kể cả SSH daemon) không bao giờ được chạy - đây là lý do realtime scheduling cần dùng cực kỳ cẩn trọng trong production.
        

**Ngày 3: Boot process**

*   Cơ chế
    
    *   Chuỗi boot đầy đủ: BIOS/UEFI (POST, tìm bootloader) → GRUB2 (đọc config, load kernel + initramfs vào RAM) → kernel khởi tạo (mount root tạm từ initramfs) → chuyển sang root filesystem thật → systemd (PID 1) → target (multi-user/graphical).
        
*   Thực hành và quan sát
    
    *   `systemd-analyze`, `systemd-analyze blame` - đo thời gian từng giai đoạn boot thật trên VM.
        
*   Advanced concepts
    
    *   UEFI khác BIOS ở chỗ có thể đọc trực tiếp filesystem FAT32 (EFI System Partition) mà không cần MBR bootloader chain phức tạp - đây là lý do UEFI boot nhanh và linh hoạt hơn (secure boot, boot nhiều OS dễ hơn).
        

**Ngày 4: Thực hành rescue mode**

*   Cơ chế
    
    *   Rescue mode boot với target tối giản (`rescue.target`), single-user mode còn tối giản hơn - cả hai cho phép sửa hệ thống khi service bình thường không khởi động được.
        
*   Thực hành và quan sát
    
    *   Sửa GRUB config (thêm tham số kernel `systemd.unit=rescue.target` lúc boot), boot vào rescue mode, mount root filesystem ở chế độ read-write để sửa file.
        
*   Advanced concepts
    
    *   `rd.break` - dừng boot ngay trong initramfs (trước cả khi chuyển sang root thật) - dùng khi lỗi nghiêm trọng hơn (VD quên password root, cần reset qua chroot vào root filesystem thật).
        

**Ngày 5: initramfs**

*   Cơ chế
    
    *   initramfs: 1 filesystem tạm (nén, load vào RAM) chứa driver + tool tối thiểu cần để tìm và mount root filesystem thật - cần thiết vì driver cho disk/RAID/LVM/encryption có thể chưa được compile sẵn vào kernel, phải load như module trước khi root thật khả dụng.
        
*   Thực hành và quan sát
    
    *   `lsinitrd` - xem nội dung initramfs hiện tại của VM, tìm driver/module nào được đóng gói trong đó.
        
*   Advanced concepts
    
    *   `dracut` - công cụ RHEL dùng để build initramfs, tự động phát hiện driver cần thiết dựa trên phần cứng/cấu hình filesystem hiện tại - đây là lý do initramfs cần rebuild lại (`dracut -f`) sau khi đổi cấu hình storage quan trọng (VD thêm LUKS encryption).
        

**Ngày 6: Ôn tập - cố tình làm hỏng boot**

*   Thực hành
    
    *   Cố tình làm hỏng boot (sai entry trong `/etc/fstab` trỏ tới device không tồn tại), tự fix bằng rescue mode - bài tập rất thực tế cho production (đây là 1 trong những sự cố phổ biến nhất khi thao tác storage sai).
        

### Tuần 26: Networking stack internals

**Ngày 1: TCP/IP stack trong kernel**

*   Cơ chế
    
    *   Tổng quan tầng: application (socket API) → transport (TCP/UDP, quản lý port, reliability) → network (IP, routing) → link (device driver, Ethernet) - mỗi tầng thêm header riêng khi đóng gói packet đi xuống, bóc header khi nhận lên.
        
*   Thực hành và quan sát
    
    *   `ss -i` - xem chi tiết TCP internals của 1 connection đang mở (`cwnd`, `rtt`, `retrans`).
        
*   Advanced concepts
    
    *   Congestion control algorithm (`cubic` mặc định RHEL) - kernel tự điều chỉnh tốc độ gửi dựa trên packet loss/RTT, `sysctl net.ipv4.tcp_congestion_control` để xem/đổi.
        

**Ngày 2: Socket syscall**

*   Cơ chế
    
    *   `socket()` → `bind()` → `listen()` → `accept()` (server) hoặc `connect()` (client) - mỗi lệnh là 1 syscall thật, kernel tạo/quản lý toàn bộ state TCP bên trong, userspace chỉ giữ 1 file descriptor.
        
*   Thực hành và quan sát
    
    *   Viết 1 chương trình C dùng raw `socket()` + `bind()` + `listen()` (không dùng thư viện cao cấp), trace toàn bộ bằng `strace`, đối chiếu với lý thuyết TCP handshake đã học Phase 1 Tuần 6.
        
*   Advanced concepts
    
    *   `SO_REUSEADDR` - vì sao cần set option này khi restart nhanh 1 server (tránh lỗi "Address already in use" do connection cũ còn ở `TIME_WAIT`).
        

**Ngày 3: /proc/net**

*   Cơ chế
    
    *   `/proc/net/tcp`, `/proc/net/dev` - kernel expose toàn bộ connection tracking table và interface statistics trực tiếp qua procfs, không cần syscall riêng - đây là cách `ss`/`netstat` lấy dữ liệu thật.
        
*   Thực hành và quan sát
    
    *   `cat /proc/net/tcp` - đọc raw format (địa chỉ/port dạng hex), viết 1 script Python nhỏ parse nó thành dạng đọc được, đối chiếu với output `ss`.
        
*   Advanced concepts
    
    *   `/proc/net/dev` - đếm packet/byte theo interface ở mức kernel counter, chính xác hơn nhiều so với ước lượng qua công cụ ngoài.
        

**Ngày 4: Netfilter hooks**

*   Cơ chế
    
    *   Netfilter định nghĩa 5 hook point cố định trong packet flow (`PREROUTING`, `INPUT`, `FORWARD`, `OUTPUT`, `POSTROUTING`) - iptables/nftables (đã học Phase 2 Tuần 9-10) chỉ là 1 trong nhiều consumer đăng ký callback vào các hook này.
        
*   Thực hành và quan sát
    
    *   Vẽ lại vị trí 5 hook trong sơ đồ packet flow, đối chiếu với chain `INPUT`/`OUTPUT`/`FORWARD` trong `nft list ruleset` đã dùng trước đó.
        
*   Advanced concepts
    
    *   XDP (đã học Tuần 24) nằm **trước cả** `PREROUTING` - đây là lý do XDP nhanh hơn netfilter: packet có thể bị xử lý/drop trước khi netfilter framework thậm chí được khởi động cho packet đó.
        

**Ngày 5: Đo latency & RTT**

*   Cơ chế
    
    *   `ping` đo RTT (Round Trip Time) - ở mức kernel, mỗi gói ICMP request/reply đi qua interrupt (NIC báo có packet đến) → softirq (xử lý packet ở context không chặn hoàn toàn CPU) → deliver lên userspace.
        
*   Thực hành và quan sát
    
    *   `ping -c 100 <host>`, phân tích độ lệch chuẩn RTT - RTT dao động lớn thường là dấu hiệu network congestion hoặc CPU bị nghẽn xử lý softirq (`top` cột `%si`).
        
*   Advanced concepts
    
    *   `/proc/interrupts` - đếm interrupt theo từng CPU core cho từng NIC, mất cân bằng interrupt (`irqbalance`) là nguyên nhân phổ biến gây latency cao trên server nhiều core.
        

**Ngày 6: Ôn tập - vẽ full packet path**

*   Thực hành
    
    *   Vẽ sơ đồ full path 1 packet TCP đi từ app gửi (2 máy VM) đến app nhận: app → socket → TCP stack → netfilter hooks → routing → device driver → NIC → dây mạng → NIC máy nhận → device driver → netfilter → TCP stack → socket → app.
        

### Tuần 27-28: Kerrisk (The Linux Programming Interface)

**Ngày 1: Process Creation**

*   Cơ chế
    
    *   Đọc chương Process Creation - đào sâu hơn `fork()`/`vfork()`/`clone()`: `clone()` là syscall gốc thật sự, `fork()` chỉ là 1 cách gọi `clone()` với bộ flag mặc định (copy toàn bộ), thread (`pthread_create`) cũng dùng `clone()` với flag chia sẻ memory/file descriptor.
        
*   Thực hành và quan sát
    
    *   Code lại ví dụ trong sách: `fork()` nhiều lần liên tiếp, đếm chính xác số process con được tạo (bài toán kinh điển dễ nhầm với "fork bomb" nếu tính sai).
        
*   Advanced concepts
    
    *   `CLONE_VM`, `CLONE_FILES`, `CLONE_FS` - từng flag riêng lẻ của `clone()` kiểm soát việc chia sẻ gì giữa parent/child, đây chính xác là cách phân biệt "process" và "thread" ở mức kernel: thread chỉ là process share `CLONE_VM`.
        

**Ngày 2: Signals**

*   Cơ chế
    
    *   Ôn lại signal (Phase 1 Tuần 5) ở mức API sâu hơn: `sigaction()` thay `signal()` cũ - cho phép kiểm soát chính xác signal mask, block signal trong lúc handler đang chạy, tránh race condition.
        
*   Thực hành và quan sát
    
    *   Code demo signal handler custom dùng `sigaction()`, test race condition khi không block đúng signal (2 lần SIGINT liên tiếp gây gọi handler chồng lên nhau nếu không mask).
        
*   Advanced concepts
    
    *   Async-signal-safe function - danh sách hàm được phép gọi an toàn bên trong signal handler (VD `write()` an toàn, `malloc()` không an toàn vì có thể đang giữ lock khi signal đến).
        

**Ngày 3: Threads (pthread)**

*   Cơ chế
    
    *   `pthread_create()` tạo thread mới share toàn bộ address space với thread gốc - đây là lý do race condition dễ xảy ra: 2 thread cùng đọc/ghi 1 biến global mà không đồng bộ.
        
*   Thực hành và quan sát
    
    *   Viết chương trình multi-thread đơn giản (2 thread cùng tăng 1 biến counter global 1 triệu lần, không dùng mutex) - chạy nhiều lần, quan sát kết quả sai khác nhau mỗi lần (race condition thật).
        
*   Advanced concepts
    
    *   Fix bằng `pthread_mutex_lock`/`unlock`, đo lại thấy kết quả luôn đúng nhưng chậm hơn - đánh đổi correctness vs performance khi đồng bộ hóa.
        

**Ngày 4: Process Groups & Sessions**

*   Cơ chế
    
    *   Ôn lại job control (Phase 1 Tuần 5) ở mức khái niệm chính thức: session (thường 1 terminal login) chứa nhiều process group, mỗi process group có 1 process group leader - `Ctrl+C` gửi SIGINT tới toàn bộ foreground process group, không phải 1 process đơn lẻ.
        
*   Thực hành và quan sát
    
    *   `ps -o pid,pgid,sid,comm` - xem process group ID và session ID thật của các process trong 1 pipeline (`cmd1 | cmd2 | cmd3` - cả 3 cùng process group).
        
*   Advanced concepts
    
    *   Controlling terminal - vì sao daemon thật sự (viết đúng chuẩn) phải `setsid()` để tách khỏi session gốc, tránh nhận signal không mong muốn khi terminal đóng.
        

**Ngày 5: File I/O**

*   Cơ chế
    
    *   `O_DIRECT` - bypass hoàn toàn page cache, đọc/ghi thẳng xuống disk - dùng khi ứng dụng (VD database) tự quản lý cache riêng tốt hơn kernel generic page cache, tránh double caching.
        
*   Thực hành và quan sát
    
    *   So sánh throughput đọc file lớn với và không có `O_DIRECT` (dùng `dd` với `iflag=direct`) - lần đọc thứ 2 không có `O_DIRECT` nhanh hơn hẳn nhờ page cache, có `O_DIRECT` luôn đọc thật từ disk.
        
*   Advanced concepts
    
    *   `posix_fadvise()` - gợi ý cho kernel về pattern truy cập sắp tới (`POSIX_FADV_SEQUENTIAL`, `POSIX_FADV_RANDOM`) để kernel tối ưu readahead phù hợp mà không cần bypass cache hoàn toàn như `O_DIRECT`.
        

**Ngày 6: Ôn tập tuần 27**

*   Thực hành
    
    *   Tổng hợp lại toàn bộ Ngày 1-5: viết 1 chương trình multi-process + multi-thread kết hợp, có signal handling đúng chuẩn, dùng `O_DIRECT` cho phần ghi log - review code chéo với chính mình sau 1 ngày.
        

**Ngày 7: IPC - pipe & FIFO**

*   Cơ chế
    
    *   Pipe (đã dùng ở shell, Phase 1 Tuần 1) ở mức syscall: `pipe()` tạo 2 file descriptor (đọc/ghi) trỏ vào cùng 1 buffer kernel. FIFO (named pipe) là pipe có tên trên filesystem, cho phép 2 process **không quan hệ cha-con** giao tiếp qua pipe.
        
*   Thực hành và quan sát
    
    *   Code 2 process giao tiếp qua `mkfifo` + `open()`/`read()`/`write()`, chạy 2 terminal riêng biệt (không phải fork từ nhau).
        
*   Advanced concepts
    
    *   Pipe có buffer giới hạn (thường 64KB trên Linux) - `write()` sẽ block nếu buffer đầy và chưa có ai `read()`, đây là cơ chế "backpressure" tự nhiên trong pipeline shell.
        

**Ngày 8: Shared memory**

*   Cơ chế
    
    *   `shmget()`/`shmat()` (System V IPC cũ) vs `mmap()` với `MAP_SHARED` (POSIX, hiện đại hơn, đã chạm ở Tuần 20 Ngày 6) - cả 2 đều cho phép nhiều process không quan hệ họ hàng chia sẻ cùng 1 vùng memory vật lý, nhanh hơn pipe/socket vì không copy data qua kernel.
        
*   Thực hành và quan sát
    
    *   Code 2 process độc lập cùng `mmap()` 1 file với `MAP_SHARED`, ghi từ process A, đọc thấy ngay từ process B.
        
*   Advanced concepts
    
    *   Shared memory không tự đồng bộ - cần kết hợp semaphore/mutex (Ngày 9) để tránh race condition khi nhiều process đọc/ghi cùng lúc.
        

**Ngày 9: Semaphore/Mutex liên-process**

*   Cơ chế
    
    *   Mutex thường (pthread) chỉ hoạt động giữa các thread cùng process; để đồng bộ giữa **nhiều process** cần semaphore đặt trong shared memory (`sem_init` với `pshared=1`) hoặc named semaphore (`sem_open`).
        
*   Thực hành và quan sát
    
    *   Đồng bộ hóa 2 process truy cập shared memory (Ngày 8) bằng semaphore, verify không còn race condition dù chạy nhiều lần.
        
*   Advanced concepts
    
    *   Deadlock kinh điển: 2 process cùng giữ 1 semaphore và chờ semaphore của process kia - cách phát hiện (`ps` state, hoặc timeout khi acquire) và tránh (luôn acquire theo cùng 1 thứ tự).
        

**Ngày 10: Mini-project 3 - Producer-Consumer**

*   Thực hành - dự án tổng hợp Tuần 27-28
    
    *   Viết 1 chương trình đa tiến trình (producer-consumer) hoàn chỉnh: producer ghi data vào shared memory (`mmap MAP_SHARED`), consumer đọc ra, đồng bộ bằng semaphore, không dùng bất kỳ thư viện IPC high-level nào (tự tay dùng đúng syscall gốc đã học suốt 2 tuần).
        

## Phase 4 - Kernel Source & Build (10 tuần)

### Tuần 29: Setup build environment & compile kernel

**Ngày 1: Clone kernel source**

*   Cơ chế
    
    *   Kernel Linux là 1 git repo khổng lồ (~30 triệu dòng, hàng chục nghìn file) - `git clone` full history rất nặng, `--depth=1` chỉ lấy snapshot mới nhất (đủ dùng cho mục đích build/học, không cần full history trừ khi làm việc với `git bisect` trên kernel thật sau này).
        
*   Thực hành và quan sát
    
    *   `git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git`, cài dependencies build (`gcc`, `make`, `bc`, `bison`, `flex`, `openssl-devel`, `elfutils-libelf-devel`).
        
*   Advanced concepts
    
    *   Kernel source có nhiều "tree" khác nhau (mainline của Linus, stable, linux-next chứa code sắp merge) - clone mainline là điểm khởi đầu chuẩn cho việc học.
        

**Ngày 2: menuconfig**

*   Cơ chế
    
    *   `make menuconfig` đọc file `Kconfig` rải rác khắp source tree (mỗi subsystem có `Kconfig` riêng định nghĩa option của nó) - kết quả chọn lưu vào `.config`, quyết định phần nào của 30 triệu dòng code thực sự được compile.
        
*   Thực hành và quan sát
    
    *   Đi qua từng mục lớn (Processor type, Device Drivers, File systems, Networking) - không cần bật hết, chỉ cần hiểu cấu trúc phân cấp menu.
        
*   Advanced concepts
    
    *   `y` (built-in), `m` (module, compile riêng .ko load sau), hoặc để trống (không compile) - 3 trạng thái này cho mỗi option, ảnh hưởng trực tiếp kích thước kernel image và thời gian boot.
        

**Ngày 3: Build**

*   Cơ chế
    
    *   `make -j$(nproc)` build song song theo dependency graph (giống Make đã học Phase 2.5 Tuần 17, nhưng ở quy mô hàng chục nghìn file) - lần build đầu tiên chậm nhất vì chưa có object file nào được cache.
        
*   Thực hành và quan sát
    
    *   Build kernel lần đầu trên VM (dùng nhiều CPU core, chuẩn bị chờ 30 phút - vài giờ tùy cấu hình `.config` và số core).
        
*   Advanced concepts
    
    *   `ccache` - cache compiler output giữa các lần build khác nhau (kể cả sau `make clean`), giảm đáng kể thời gian rebuild khi chỉ sửa vài file.
        

**Ngày 4: Cài đặt kernel mới**

*   Cơ chế
    
    *   `make modules_install` copy toàn bộ `.ko` module vào `/lib/modules/<version>/`, `make install` copy kernel image + tự động chạy `dracut`/`grub2-mkconfig` để tạo initramfs mới và thêm entry GRUB mới - kernel cũ vẫn còn nguyên trong GRUB menu.
        
*   Thực hành và quan sát
    
    *   `make modules_install && make install`, kiểm tra GRUB config có entry kernel mới, reboot vào đó.
        
*   Advanced concepts
    
    *   GRUB mặc định boot entry mới nhất - nếu kernel mới lỗi, cần biết cách chọn entry cũ ngay tại màn hình GRUB (phím mũi tên trước khi countdown hết) mà không cần rescue mode.
        

**Ngày 5: Debug boot lỗi**

*   Cơ chế
    
    *   Đây là bài học thực tế quan trọng nhất tuần: build kernel tự custom rất dễ thiếu driver cần thiết (VD driver cho virtio disk trên VM) nếu tắt nhầm option trong `menuconfig`, dẫn đến kernel boot nhưng không tìm thấy root filesystem.
        
*   Thực hành và quan sát
    
    *   Nếu boot lỗi (rất hay xảy ra lần đầu) - debug bằng GRUB rescue (chọn kernel cũ ở boot menu), so sánh `.config` cũ/mới để tìm option bị thiếu.
        
*   Advanced concepts
    
    *   `zcat /proc/config.gz` (nếu kernel hỗ trợ) - đọc `.config` của kernel **đang chạy** để so sánh chính xác với `.config` bạn vừa build, tìm ra option nào bị tắt nhầm.
        

**Ngày 6: Ôn tập - tự động hóa build**

*   Thực hành
    
    *   Build lại từ đầu, ghi lại toàn bộ quy trình (clone → menuconfig → build → install → reboot) thành 1 script bash tự động, để lần build sau không cần nhớ lại từng bước.
        

### Tuần 30: QEMU boot-test & debug config

**Ngày 1: QEMU thay VM thật**

*   Cơ chế
    
    *   Boot-test kernel mới trực tiếp bằng QEMU (`qemu-system-x86_64 -kernel bzImage -initrd initramfs -append "console=ttyS0"`) nhanh hơn nhiều so với cài vào VM thật rồi reboot - không cần GRUB, không risk phá VM chính.
        
*   Thực hành và quan sát
    
    *   Cấu hình QEMU boot thẳng kernel vừa build với 1 rootfs tối giản (busybox), lặp lại chu trình sửa-build-test trong vài giây thay vì vài phút.
        
*   Advanced concepts
    
    *   `-append "console=ttyS0"` - redirect kernel console output ra serial port thay vì VGA, cho phép xem log ngay trong terminal host mà không cần giao diện đồ họa.
        

**Ngày 2: printk debug**

*   Cơ chế
    
    *   `printk()` là "printf của kernel" - ghi message vào kernel ring buffer (đọc bằng `dmesg`), có log level (`KERN_INFO`, `KERN_ERR`...) quyết định message có hiện ra console ngay hay chỉ nằm trong buffer.
        
*   Thực hành và quan sát
    
    *   Thêm `printk(KERN_INFO "checkpoint reached\n")` vào 1 file kernel bất kỳ (VD trong hàm khởi tạo 1 driver), build lại, xem trong `dmesg` sau khi boot.
        
*   Advanced concepts
    
    *   `pr_info()`/`pr_err()` - macro wrapper hiện đại hơn `printk` trực tiếp, tự động thêm prefix tên module/file, là convention chuẩn trong kernel code ngày nay.
        

**Ngày 3: Custom version string**

*   Cơ chế
    
    *   `EXTRAVERSION` trong file `Makefile` gốc của kernel source cho phép thêm hậu tố tùy chỉnh vào version string - hữu ích để phân biệt kernel tự build với kernel chính thức khi có nhiều bản cùng cài trên 1 máy.
        
*   Thực hành và quan sát
    
    *   Đổi `EXTRAVERSION = -mylab`, build, verify bằng `uname -r` sau khi boot vào kernel này.
        
*   Advanced concepts
    
    *   `scripts/setlocalversion` - script tự động thêm hash git commit vào version string nếu build từ git repo có thay đổi chưa commit, giúp trace chính xác kernel đang chạy được build từ commit/state nào.
        

**Ngày 4: Config tối giản**

*   Cơ chế
    
    *   `make localmodconfig` - tự động tạo `.config` chỉ chứa module đang thực sự được load trên hệ thống hiện tại (dựa trên `lsmod`), loại bỏ hàng nghìn driver không liên quan - giảm đáng kể thời gian build và kích thước kernel.
        
*   Thực hành và quan sát
    
    *   Build kernel với config tối giản này, so sánh thời gian build và boot time với kernel full-config ở Tuần 29.
        
*   Advanced concepts
    
    *   Đánh đổi: config tối giản build nhanh nhưng kém "portable" (thiếu driver nếu chuyển kernel image sang máy khác có phần cứng khác) - phù hợp cho lab/VM cố định, không phù hợp cho kernel distro chính thức.
        

**Ngày 5: Checkpoint đo boot time**

*   Thực hành
    
    *   So sánh boot time (dùng `systemd-analyze`) giữa kernel full-config (Tuần 29) và kernel tối giản (Ngày 4) - định lượng cụ thể tác động của việc giảm số driver/module được compile.
        

**Ngày 6: Buffer/ôn tập QEMU workflow**

*   Thực hành
    
    *   Viết script tự động hóa toàn bộ chu trình QEMU boot-test (build → copy kernel image → khởi động QEMU → capture log) thành 1 lệnh duy nhất, chuẩn bị workflow nhanh cho các tuần module/syscall tiếp theo.
        

### Tuần 31: Kernel module - Hello World & proc entry

**Ngày 1: Module Hello World**

*   Cơ chế
    
    *   Kernel module (`.ko`) là code có thể load/unload vào kernel đang chạy mà không cần reboot - `module_init()`/`module_exit()` là 2 macro đăng ký hàm callback chạy khi `insmod`/`rmmod`.
        
*   Thực hành và quan sát
    
    *   Viết module "Hello World" kinh điển (in message qua `printk` khi load/unload), build bằng `Makefile` kiểu kernel module (dùng `obj-m`, gọi vào kernel build system qua `KDIR`).
        
*   Advanced concepts
    
    *   Module phải build với **đúng version kernel** đang chạy (dùng chung header/symbol) - đây là lý do `.ko` build cho kernel A thường không load được vào kernel B khác version.
        

**Ngày 2: insmod/rmmod/lsmod**

*   Cơ chế
    
    *   `insmod` load module thô (cần đường dẫn đầy đủ, không tự resolve dependency); `modprobe` thông minh hơn (tự tìm trong `/lib/modules/`, tự load dependency).
        
*   Thực hành và quan sát
    
    *   `insmod hello.ko`, `lsmod | grep hello`, `rmmod hello`, xem log qua `dmesg` ở cả 2 sự kiện load/unload.
        
*   Advanced concepts
    
    *   `modinfo hello.ko` - xem metadata module (author, license, dependency) mà không cần load nó - kernel từ chối load module thiếu `MODULE_LICENSE()` khai báo đúng (tainted kernel warning nếu license không phải GPL-compatible).
        

**Ngày 3: Module tham số**

*   Cơ chế
    
    *   `module_param()` cho phép truyền tham số lúc `insmod` (VD `insmod hello.ko count=5`) - giá trị này map trực tiếp vào biến C trong module, không cần hardcode.
        
*   Thực hành và quan sát
    
    *   Thêm 1 tham số integer vào module, load với giá trị khác nhau, verify qua `printk` in ra giá trị nhận được.
        
*   Advanced concepts
    
    *   `/sys/module/hello/parameters/` - tham số có thể đọc (và 1 số ghi được runtime) qua sysfs sau khi module đã load, không cần unload/reload để xem giá trị hiện tại.
        

**Ngày 4: proc entry**

*   Cơ chế
    
    *   Module có thể tạo file ảo trong `/proc` (`proc_create()`) với callback `read`/`write` tùy chỉnh - đây chính là cơ chế mà rất nhiều thông tin hệ thống bạn đã dùng suốt các phase trước (`/proc/meminfo`, `/proc/net/tcp`...) được implement.
        
*   Thực hành và quan sát
    
    *   Tạo 1 proc entry đơn giản (VD `/proc/hello_counter`) tăng giá trị mỗi lần bị đọc, verify bằng `cat /proc/hello_counter` nhiều lần.
        
*   Advanced concepts
    
    *   `seq_file` API - cách chuẩn hiện đại để implement proc entry trả về nhiều dòng output an toàn (tránh lỗi buffer nếu output lớn hơn 1 page, vấn đề của API cũ hơn).
        

**Ngày 5: Character device driver**

*   Cơ chế
    
    *   `register_chrdev()` đăng ký 1 major number cho driver, kernel route mọi `open()`/`read()`/`write()` trên file device tương ứng (`/dev/mydevice`) vào các hàm callback của driver - đây là cách driver thật (VD driver bàn phím, chuột) hoạt động ở mức khái niệm.
        
*   Thực hành và quan sát
    
    *   Viết character device driver đơn giản, `mknod /dev/mydevice c <major> 0` để tạo file device thủ công.
        
*   Advanced concepts
    
    *   `udev` tự động tạo file trong `/dev` khi driver đăng ký đúng chuẩn (`class_create`/`device_create`) thay vì phải `mknod` thủ công - đây là cách driver thật hoạt động trong production.
        

**Ngày 6: Test device từ userspace**

*   Thực hành
    
    *   Viết chương trình user-space đơn giản dùng `open()`/`read()`/`write()` vào `/dev/mydevice`, verify data đi qua đúng chiều (ghi từ userspace, driver nhận được trong `printk`; đọc từ userspace, nhận đúng data driver trả về).
        

### Tuần 32: Đọc source

**Ngày 1: kernel/sched/core.c**

*   Cơ chế
    
    *   Đọc source `kernel/sched/core.c` có định hướng (không cần hiểu 100%) - tìm hàm `schedule()`, đối chiếu với lý thuyết CFS đã học Phase 3 Tuần 25 (`vruntime`, red-black tree).
        
*   Thực hành và quan sát
    
    *   `grep -n "vruntime" kernel/sched/fair.c | head -30` - tìm các đoạn code trực tiếp thao tác `vruntime`, đọc comment xung quanh.
        
*   Advanced concepts
    
    *   Kernel source có rất nhiều comment giải thích "vì sao" (design rationale), không chỉ "làm gì" - đọc comment thường hiệu quả hơn đọc từng dòng code logic khi mới bắt đầu.
        

**Ngày 2: mm/page\_alloc.c**

*   Cơ chế
    
    *   Đọc `mm/page_alloc.c` - tìm hiểu buddy allocator ở mức khái niệm: kernel quản lý free memory theo block kích thước lũy thừa 2 (1, 2, 4, 8... page), khi cần block nhỏ mà chỉ còn block lớn, chia đôi (split) - khi free, gộp lại (coalesce) nếu "buddy" (block liền kề cùng kích thước) cũng free.
        
*   Thực hành và quan sát
    
    *   `cat /proc/buddyinfo` - xem trực tiếp số lượng free block theo từng "order" (kích thước) hiện tại trên hệ thống, đối chiếu với lý thuyết buddy allocator.
        
*   Advanced concepts
    
    *   External fragmentation - vì sao hệ thống chạy lâu có thể "còn nhiều RAM free tổng" nhưng không cấp phát được 1 block lớn liên tục (không đủ buddy pair để gộp lên order cao).
        

**Ngày 3: fs/read\_write.c**

*   Cơ chế
    
    *   Đọc `fs/read_write.c` - trace đường đi từ syscall `read()` (`SYSCALL_DEFINE3(read, ...)`) vào VFS (`vfs_read()`) rồi gọi tới `file->f_op->read` (con trỏ hàm cụ thể của filesystem đang mount).
        
*   Thực hành và quan sát
    
    *   Đối chiếu đường đi vừa đọc với sơ đồ I/O path đã tự vẽ ở Phase 3 Tuần 23 - giờ thấy rõ từng bước tương ứng với dòng code cụ thể nào.
        
*   Advanced concepts
    
    *   `struct file_operations` - đây chính là cơ chế "polymorphism kiểu C" (dùng function pointer trong struct) mà toàn bộ VFS dựa vào để hoạt động thống nhất trên nhiều filesystem khác nhau.
        

**Ngày 4: Module hoàn chỉnh - viết**

*   Thực hành - dự án
    
    *   Viết 1 module hoàn chỉnh kết hợp toàn bộ Tuần 31: có proc entry (đọc/ghi), nhận module parameter, character device driver, log đầy đủ qua `printk`/`pr_info`.
        

**Ngày 5: Module hoàn chỉnh - document**

*   Thực hành
    
    *   Document lại code: comment giải thích từng hàm, viết README mô tả cách build/load/test module, chuẩn bị style tương tự code kernel thật (Kerrisk-style comment, không phải comment tự nhiên ngôn ngữ tùy tiện).
        

**Ngày 6: Checkpoint**

*   Thực hành
    
    *   Review lại module đã viết sau 1 ngày nghỉ - tự đặt câu hỏi "nếu người khác đọc code này mà không có tôi giải thích, họ có hiểu được luồng chạy không?".
        

### Tuần 33: Thêm syscall mới - phần 1

**Ngày 1: Cơ chế syscall table**

*   Cơ chế
    
    *   Tìm hiểu cơ chế syscall table: `arch/x86/entry/syscalls/syscall_64.tbl` là file ánh xạ số hiệu syscall → tên hàm kernel thật (`sys_xxx`) - đây là bảng mà CPU tra cứu khi userspace gọi `syscall` instruction với số hiệu tương ứng trong register.
        
*   Thực hành và quan sát
    
    *   `grep sys_read arch/x86/entry/syscalls/syscall_64.tbl` - tìm số hiệu syscall của `read` (0), đối chiếu với `man 2 syscalls` liệt kê toàn bộ syscall có sẵn.
        
*   Advanced concepts
    
    *   Số hiệu syscall **không được đổi thứ tự hay xóa** sau khi đã public - đây là ABI (Application Binary Interface) ổn định mà Linux cam kết duy trì vĩnh viễn để không phá vỡ binary cũ.
        

**Ngày 2: Viết định nghĩa syscall**

*   Cơ chế
    
    *   `SYSCALL_DEFINE0()` (không tham số) là macro chuẩn để định nghĩa 1 syscall mới, xử lý đúng calling convention và kernel/userspace boundary tự động thay vì viết hàm C thường.
        
*   Thực hành và quan sát
    
    *   Viết định nghĩa syscall mới đơn giản (VD trả về 1 số cố định, dùng `SYSCALL_DEFINE0`), đặt trong 1 file `.c` mới trong kernel source.
        
*   Advanced concepts
    
    *   Vì sao không thể định nghĩa syscall bằng hàm C thường - macro `SYSCALL_DEFINEn` tự sinh thêm code kiểm tra/chuyển đổi tham số đúng chuẩn kernel calling convention.
        

**Ngày 3: Thêm vào bảng & build**

*   Cơ chế
    
    *   Sau khi viết hàm, cần đăng ký nó vào `syscall_64.tbl` (gán số hiệu mới) và khai báo prototype trong header tương ứng (`include/linux/syscalls.h`) trước khi build lại kernel.
        
*   Thực hành và quan sát
    
    *   Thêm syscall vào bảng, build lại kernel (dùng workflow QEMU đã tự động hóa ở Tuần 30 để test nhanh).
        
*   Advanced concepts
    
    *   Số hiệu syscall test/lab nên chọn ở vùng chưa dùng (thường cuối bảng) để tránh xung đột với syscall thật - kernel thật thêm syscall mới rất hiếm và qua quy trình review LKML nghiêm ngặt.
        

**Ngày 4: Gọi syscall từ userspace**

*   Cơ chế
    
    *   Chưa có wrapper glibc cho syscall tự viết - phải gọi trực tiếp qua `syscall(number, ...)` (hàm generic trong glibc nhận số hiệu bất kỳ) thay vì gọi tên hàm quen thuộc.
        
*   Thực hành và quan sát
    
    *   Viết chương trình C gọi syscall mới qua `syscall()` trực tiếp, verify kết quả trả về đúng như hàm kernel đã viết.
        
*   Advanced concepts
    
    *   Vì sao syscall thật (đã ổn định) luôn có wrapper glibc (VD `read()`) - wrapper xử lý thêm việc set `errno` đúng chuẩn C library, syscall tự viết của bạn phải tự làm việc này nếu muốn dùng như hàm C bình thường.
        

**Ngày 5: Debug lỗi build/runtime**

*   Cơ chế
    
    *   Đây mới là phần học thật của bài tập - lỗi thường gặp: quên khai báo prototype đúng header, sai số tham số trong macro `SYSCALL_DEFINEn`, hoặc build thành công nhưng gọi ra `-ENOSYS` (chưa được register đúng vào bảng đang dùng thật).
        
*   Thực hành và quan sát
    
    *   Cố tình gây 2-3 lỗi phổ biến kể trên, quan sát thông báo lỗi build/runtime tương ứng, ghi chú lại cách nhận diện.
        
*   Advanced concepts
    
    *   `strace` lên chính chương trình gọi syscall tự viết - xem syscall number thật được gọi, đối chiếu với bảng đã đăng ký để chắc chắn không nhầm số hiệu.
        

**Ngày 6: Buffer/ôn tập**

*   Thực hành
    
    *   Review lại toàn bộ chu trình Ngày 1-5, viết checklist cá nhân "các bước thêm 1 syscall mới" để dùng lại nhanh ở Tuần 34.
        

### Tuần 34: Thêm syscall mới - phần 2 (tham số & validate)

**Ngày 1: Syscall nhận tham số**

*   Cơ chế
    
    *   Mở rộng: syscall nhận tham số từ user-space dùng `copy_from_user()` - đây là hàm **bắt buộc** khi kernel cần đọc dữ liệu từ con trỏ userspace, vì kernel không được tin tưởng địa chỉ đó hợp lệ (userspace có thể truyền con trỏ rác/độc hại).
        
*   Thực hành và quan sát
    
    *   Mở rộng syscall Tuần 33 để nhận 1 chuỗi string từ userspace qua `copy_from_user()`, in ra bằng `printk`.
        
*   Advanced concepts
    
    *   `copy_to_user()` - chiều ngược lại, dùng khi syscall cần trả dữ liệu về userspace qua con trỏ - cả 2 hàm đều tự kiểm tra địa chỉ hợp lệ và trả lỗi thay vì crash kernel nếu con trỏ sai.
        

**Ngày 2: Validate input**

*   Cơ chế
    
    *   Test edge case: tham số NULL, tham số sai (con trỏ trỏ vào vùng nhớ kernel thay vì userspace, độ dài âm...) - hiểu vì sao kernel luôn validate input nghiêm ngặt: 1 lỗi validate ở kernel-space có thể crash toàn hệ thống hoặc mở lỗ hổng bảo mật, khác lỗi ở userspace chỉ crash 1 process.
        
*   Thực hành và quan sát
    
    *   Test syscall tự viết với tham số NULL, con trỏ sai - verify hàm trả về lỗi đúng chuẩn (`-EFAULT`, `-EINVAL`) thay vì kernel panic.
        
*   Advanced concepts
    
    *   `access_ok()` - hàm kiểm tra nhanh 1 con trỏ có nằm trong vùng địa chỉ userspace hợp lệ trước khi thực sự copy, tránh side-effect không cần thiết nếu tham số rõ ràng sai ngay từ đầu.
        

**Ngày 3: So sánh với syscall thật**

*   Cơ chế
    
    *   So sánh cách bạn implement với 1 syscall thật đơn giản trong kernel (VD `getpid` - gần như không làm gì ngoài đọc `current->pid`) - nhận ra syscall thật trong kernel thường **rất ngắn gọn**, phần lớn logic phức tạp nằm ở layer khác (VFS, scheduler) mà syscall chỉ gọi vào.
        
*   Thực hành và quan sát
    
    *   `grep -A 5 "SYSCALL_DEFINE0(getpid)" kernel/sys.c` - đọc implementation thật của `getpid`, so sánh độ phức tạp với syscall tự viết của bạn.
        
*   Advanced concepts
    
    *   `current` macro - cách kernel luôn biết "process nào đang gọi syscall này" mà không cần truyền tham số - trỏ tới `task_struct` của process hiện tại, lưu trong 1 vùng đặc biệt (thường qua register hoặc per-CPU variable) mà kernel maintain tại mọi thời điểm.
        

**Ngày 4: Viết note tổng hợp flow**

*   Thực hành
    
    *   Viết note giải thích toàn bộ flow: user gọi `syscall()` → CPU trap vào kernel mode (đổi privilege level) → kernel tra syscall table → gọi hàm tương ứng → xử lý (có thể `copy_from_user`/`copy_to_user`) → trả về → CPU quay lại user mode.
        

**Ngày 5: Test case & stability**

*   Thực hành
    
    *   Checkpoint: syscall của bạn hoạt động ổn định qua nhiều lần gọi liên tiếp, có test case cụ thể (input hợp lệ, input NULL, input biên) - chạy loop gọi syscall hàng nghìn lần không gây leak/crash.
        

**Ngày 6: Ôn tập tổng hợp Tuần 33-34**

*   Thực hành
    
    *   Mini-review: giải thích lại toàn bộ (không nhìn note) sự khác biệt giữa syscall bạn thêm ở đây và syscall bạn sẽ thêm vào xv6 sau này (Phase 5 Tuần 40) - dự đoán trước cái nào dễ hơn và vì sao.
        

### Tuần 35: Debug kernel - panic & oops

**Ngày 1: kgdb setup**

*   Cơ chế
    
    *   `kgdb` cho phép GDB (đã học Phase 2.5 Tuần 18) attach trực tiếp vào **kernel đang chạy** trong QEMU qua serial/network - khác GDB thường chỉ debug được userspace process.
        
*   Thực hành và quan sát
    
    *   Cấu hình QEMU + kernel debugging qua `kgdb` (boot kernel với tham số `kgdboc=`, kết nối GDB từ host).
        
*   Advanced concepts
    
    *   `kgdb` dừng **toàn bộ hệ thống** khi breakpoint trúng (không chỉ 1 process như GDB userspace) - vì kernel không có khái niệm "process khác vẫn chạy độc lập" ở mức thấp nhất khi đang debug chính nó.
        

**Ngày 2: Gây kernel panic**

*   Cơ chế
    
    *   Kernel panic xảy ra khi kernel gặp lỗi không thể phục hồi (null pointer dereference trong kernel-space, không giống userspace chỉ segfault 1 process) - toàn hệ thống dừng lại, in ra oops message.
        
*   Thực hành và quan sát
    
    *   Cố tình gây kernel panic (null pointer dereference trong module tự viết ở Tuần 31-32), đọc oops message hiện ra trên console.
        
*   Advanced concepts
    
    *   Oops (recoverable, kernel có thể tiếp tục nếu lỗi ở 1 subsystem không quan trọng) khác panic (không thể tiếp tục) - đọc kỹ message để phân biệt 2 loại.
        

**Ngày 3: Đọc call stack trong oops**

*   Cơ chế
    
    *   Oops message chứa call stack thô (địa chỉ instruction) - cần `addr2line`/`scripts/decode_stacktrace.sh` (đi kèm kernel source) để dịch địa chỉ đó về tên hàm + số dòng source thật.
        
*   Thực hành và quan sát
    
    *   Dùng `addr2line` với đúng `vmlinux` (kernel image có debug symbol) để dịch địa chỉ trong oops vừa gây ra, xác nhận đúng dòng code đã cố tình gây lỗi.
        
*   Advanced concepts
    
    *   Kernel build với `CONFIG_DEBUG_INFO=y` mới giữ đủ debug symbol để `addr2line` hoạt động chính xác - production kernel thường build không có debug info (để giảm kích thước), cần symbol table riêng để debug crash thật.
        

**Ngày 4: crash utility**

*   Cơ chế
    
    *   `crash` là công cụ chính thức RHEL dùng để phân tích kernel dump (khác GDB, `crash` hiểu cấu trúc dữ liệu kernel như `task_struct`, có thể liệt kê toàn bộ process, memory state tại thời điểm crash).
        
*   Thực hành và quan sát
    
    *   `crash` utility - phân tích kernel dump (RHEL dùng công cụ này cho production debugging thật, đây là công cụ Red Hat Support yêu cầu khi xử lý ticket liên quan kernel crash).
        
*   Advanced concepts
    
    *   `crash` cần đúng `vmlinux` + dump file **khớp version** - đây là lý do RHEL luôn giữ lại debuginfo package tương ứng từng kernel version đã release, để có thể phân tích dump từ khách hàng bất kỳ lúc nào.
        

**Ngày 5: Thực hành phân tích dump**

*   Thực hành và quan sát
    
    *   Tạo kernel panic có chủ đích (khác lỗi Ngày 2, dùng `crash` để phân tích lại dump thay vì đọc oops message trực tiếp trên console) - so sánh 2 cách tiếp cận.
        

**Ngày 6: Ôn tập checklist panic**

*   Thực hành
    
    *   Viết checklist "khi server RHEL bị kernel panic, làm gì đầu tiên" (kiểm tra kdump có được cấu hình không → thu thập dump → `sosreport` đã học Phase 2 → phân tích bằng `crash` hoặc gửi Red Hat Support).
        

### Tuần 36: kdump

**Ngày 1: Đọc tài liệu kdump chính thức**

*   Cơ chế
    
    *   Ôn lại khái niệm đã giới thiệu ở Phase 2 Tuần 16 (kexec/kdump bridge note) - giờ đọc `Documentation/admin-guide/kdump/` để hiểu đầy đủ cấu hình thật: `crashkernel=` reserve 1 vùng RAM riêng cho kernel dự phòng, `kdump.service` tự động trigger khi panic xảy ra.
        
*   Thực hành và quan sát
    
    *   Đọc tài liệu, xác định dung lượng `crashkernel=` phù hợp với RAM của VM lab (thường vài trăm MB tối thiểu).
        
*   Advanced concepts
    
    *   Vì sao cần reserve RAM **trước** (lúc boot kernel chính), không thể cấp phát sau khi panic xảy ra - kernel chính đã panic nghĩa là memory allocator của nó không còn tin cậy được.
        

**Ngày 2: Cấu hình kdump thật**

*   Thực hành và quan sát
    
    *   Thêm `crashkernel=256M` vào GRUB kernel parameter, enable + start `kdump.service`, verify bằng `kdumpctl status`/`systemctl status kdump`.
        
*   Advanced concepts
    
    *   `/etc/kdump.conf` - cấu hình nơi lưu dump (local disk, NFS, SSH remote) - production thường lưu dump ra remote để tránh trường hợp local disk cũng bị ảnh hưởng bởi nguyên nhân gây crash.
        

**Ngày 3: Test kdump**

*   Cơ chế
    
    *   `sysrq trigger` (`echo c > /proc/sysrq-trigger`) là cách chuẩn để test kdump có hoạt động đúng hay không mà không cần chờ crash thật xảy ra tự nhiên.
        
*   Thực hành và quan sát
    
    *   Test kdump: trigger crash bằng `sysrq trigger`, verify dump được ghi lại đúng vị trí đã cấu hình sau khi hệ thống tự reboot.
        
*   Advanced concepts
    
    *   SysRQ có nhiều lệnh debug khác ngoài trigger crash (`echo w` in stack trace mọi task, `echo m` in memory info) - hữu ích để chẩn đoán hệ thống "đứng hình" (hang) mà chưa tới mức panic thật.
        

**Ngày 4: Phân tích dump vừa tạo**

*   Thực hành và quan sát
    
    *   Phân tích dump vừa tạo bằng `crash` (đã học cách dùng Tuần 35), xác nhận thấy đúng call stack tại điểm `sysrq trigger` được gọi.
        

**Ngày 5: So sánh 2 luồng debug**

*   Thực hành
    
    *   Đối chiếu luồng "đọc oops trực tiếp trên console" (Tuần 35) với luồng "kdump → phân tích offline bằng crash" - nhận ra production luôn cần luồng thứ 2 vì console log thường không được giữ lại sau khi hệ thống tự reboot.
        

**Ngày 6: Mini-project 4 - Production incident giả lập**

*   Thực hành - dự án tổng hợp
    
    *   Toàn bộ flow "production incident giả lập": kernel panic (trigger có chủ đích) → kdump tự động capture → dùng `crash` phân tích dump → viết root cause report đầy đủ (thời điểm crash, call stack, nguyên nhân, khuyến nghị khắc phục) như 1 báo cáo incident thật gửi cho team.
        

### Tuần 37-38: Đọc Linux Kernel Development (Robert Love) song song thực hành

**Ngày 1: Chương Introduction & Process Management**

*   Cơ chế
    
    *   Đọc chương giới thiệu kiến trúc tổng thể kernel + chương Process Management - đối chiếu trực tiếp với `task_struct`, `fork()`/`clone()` đã thực hành ở Phase 3 Tuần 20-21.
        
*   Thực hành và quan sát
    
    *   Tự tóm tắt chương bằng 5 dòng, highlight điểm khác với những gì đã tự tìm hiểu qua source code trước đó.
        

**Ngày 2: Chương Process Scheduling**

*   Cơ chế
    
    *   Đọc sâu hơn về CFS so với những gì đã học Phase 3 Tuần 25 - sách giải thích thêm về scheduling class (`SCHED_NORMAL`, `SCHED_FIFO`, `SCHED_RR`) và cách kernel chọn class nào chạy trước.
        
*   Thực hành và quan sát
    
    *   Đối chiếu nội dung sách với code `kernel/sched/core.c` đã đọc ở Tuần 32.
        

**Ngày 3: Chương System Calls**

*   Cơ chế
    
    *   Đọc chương System Calls - so sánh cách sách giải thích quy trình thêm syscall mới với kinh nghiệm thực tế bạn đã làm ở Tuần 33-34, ghi chú lại điểm sách nói mà bạn chưa để ý lúc thực hành.
        

**Ngày 4: Chương Kernel Data Structures**

*   Cơ chế
    
    *   Đọc về `linked list`, `red-black tree` (đã chạm khi học CFS), `radix tree` - cấu trúc dữ liệu kernel dùng nội bộ, khác thư viện chuẩn C thường dùng ở userspace (kernel không có STL/glibc container).
        

**Ngày 5: Chương Memory Management**

*   Cơ chế
    
    *   Đọc sâu hơn buddy allocator (đã đọc source Tuần 32) và giới thiệu thêm **slab allocator** (cấp phát object nhỏ, cùng kích thước, tần suất cao - VD `task_struct` mới mỗi lần fork) - buddy allocator không hiệu quả cho việc này vì luôn cấp theo page.
        

**Ngày 6: Ôn tập tuần 37**

*   Thực hành
    
    *   Tổng hợp 5 chương đã đọc, viết bản đồ liên kết giữa lý thuyết sách và phần thực hành tương ứng đã làm ở Phase 3-4.
        

**Ngày 7: Chương Virtual Filesystem**

*   Cơ chế
    
    *   Đọc sâu hơn VFS (đã đọc source Tuần 32, khái niệm Phase 3 Tuần 22) - sách giải thích rõ hơn mối quan hệ giữa `struct super_block`, `struct inode`, `struct dentry`, `struct file`.
        

**Ngày 8: Chương Block I/O**

*   Cơ chế
    
    *   Đọc sâu hơn block layer (đã học khái niệm Phase 3 Tuần 22) - `struct bio` là đơn vị I/O request cơ bản kernel dùng nội bộ, không phải "1 lệnh read/write" đơn giản như userspace nhìn thấy.
        

**Ngày 9: Chương Kernel Synchronization**

*   Cơ chế
    
    *   Đọc về spinlock, semaphore, RCU (Read-Copy-Update) - kernel cần nhiều cơ chế đồng bộ khác nhau tùy ngữ cảnh (interrupt context không được ngủ, nên phải dùng spinlock chứ không phải mutex).
        
*   Thực hành và quan sát
    
    *   Đối chiếu với race condition đã tự tạo và fix ở Phase 3 Tuần 27 (pthread mutex ở userspace) - nhận ra kernel-space có nhiều ràng buộc hơn hẳn (không được block tùy tiện trong 1 số context).
        

**Ngày 10: Tổng kết Phase 4**

*   Thực hành
    
    *   Viết 1 tài liệu tổng kết cá nhân: liệt kê toàn bộ khái niệm kernel đã học qua Phase 3-4 (process, memory, scheduler, VFS, block I/O, syscall, module, debug), đánh giá mức độ tự tin với từng mảng trước khi bước vào Phase 5 (xv6).
        

## Phase 5 - Capstone: Tự xây OS (8 tuần)

Đây là phần trả lời trực tiếp câu hỏi "không tự làm OS thì không hiểu hết". Không cần viết từ số 0 hoàn toàn - dùng **xv6** (kernel giáo dục của MIT, ~10.000 dòng C, dùng trong khóa 6.1810) làm nền, qua bộ lab chính thức của MIT (miễn phí, public: `pdos.csail.mit.edu/6.1810/`).

### Tuần 39: Làm quen xv6

**Ngày 1: Clone & build**

*   Cơ chế
    
    *   xv6-riscv build bằng RISC-V cross-compiler (không phải x86 như toàn bộ Phase 1-4) - chạy trên QEMU giả lập RISC-V, đây là lựa chọn kiến trúc đơn giản hơn x86 rất nhiều (ít legacy, ít mode chuyển đổi phức tạp), phù hợp mục đích giáo dục.
        
*   Thực hành và quan sát
    
    *   Clone xv6-riscv, cài `riscv64-unknown-elf-gcc` toolchain, `make qemu` - build và chạy trên QEMU trong 1 lệnh.
        
*   Advanced concepts
    
    *   So sánh độ phức tạp toolchain: build kernel Linux thật (Phase 4 Tuần 29) cần hàng chục dependency và `.config` khổng lồ; xv6 build chỉ cần 1 `Makefile` ngắn gọn - đây chính là lý do xv6 phù hợp để đọc hết trong vài tuần.
        

**Ngày 2: Đọc toàn bộ source 1 lượt**

*   Cơ chế
    
    *   Đọc toàn bộ source (chỉ ~10k dòng, khả thi) một lượt để có bức tranh tổng thể - không cần hiểu sâu từng dòng, mục tiêu là nhận diện cấu trúc thư mục (`kernel/`, `user/`) và tên file tương ứng với subsystem nào (`proc.c`, `vm.c`, `fs.c`, `trap.c`).
        
*   Thực hành và quan sát
    
    *   `wc -l kernel/*.c | sort -n` - liệt kê file theo độ dài, ưu tiên đọc file ngắn trước để làm quen coding style.
        
*   Advanced concepts
    
    *   So sánh trực tiếp với Linux thật (30 triệu dòng vs 10 nghìn dòng) - xv6 loại bỏ gần như toàn bộ driver, tối ưu hóa, và edge-case handling mà Linux thật cần cho production, chỉ giữ lại "bộ khung" khái niệm cốt lõi.
        

**Ngày 3: xv6 book - OS interfaces**

*   Cơ chế
    
    *   Đọc "xv6 book" (tài liệu chính thức đi kèm) chương 1-2: OS interfaces - giới thiệu triết lý thiết kế Unix-like (mọi thứ là file descriptor, `fork`/`exec` tách biệt) mà bạn đã thực hành trên Linux thật ở Phase 3 Tuần 20, giờ xem lại từ góc độ "implement nó từ đầu trông như thế nào".
        
*   Thực hành và quan sát
    
    *   Đọc song song source code `kernel/proc.c` khi đọc chương sách tương ứng, đối chiếu code thật với mô tả trong sách.
        

**Ngày 4: Trace boot process xv6**

*   Cơ chế
    
    *   Boot process xv6 đơn giản hơn Linux thật nhiều (không có BIOS/UEFI/GRUB phức tạp - QEMU load thẳng kernel image vào RAM ở địa chỉ cố định) - dễ theo dõi từng dòng từ `entry.S` (assembly đầu tiên chạy) đến `main()` trong C.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/entry.S` → `kernel/main.c`, đặt `printf` debug ở đầu mỗi hàm khởi tạo subsystem (`kinit`, `procinit`, `trapinit`...), quan sát thứ tự khởi tạo thật khi boot.
        
*   Advanced concepts
    
    *   So sánh với boot process Linux thật đã học Phase 3 Tuần 25 (BIOS/UEFI → GRUB → initramfs → systemd) - xv6 bỏ qua toàn bộ phần "tìm và load kernel", chỉ tập trung vào phần "kernel tự khởi tạo chính nó" mà Linux thật cũng phải làm tương tự sau khi GRUB load xong.
        

**Ngày 5: Utilities lab**

*   Thực hành - lab chính thức MIT
    
    *   **Utilities lab** - viết vài chương trình user-space cho xv6 (VD `sleep`, `pingpong`, `find`, `xargs`) dùng đúng syscall xv6 cung cấp - bài tập làm quen viết code chạy trên OS tự build, không phải Linux thật.
        
*   Advanced concepts
    
    *   xv6 cung cấp tập syscall rất nhỏ (khoảng 20 syscall) so với Linux thật (hơn 300) - nhưng đủ để viết được `find`/`xargs` cơ bản, cho thấy phần lớn syscall Linux thật là để tối ưu/tiện lợi, không phải "bắt buộc" về mặt nguyên lý.
        

**Ngày 6: Ôn tập - giải thích flow boot đến shell**

*   Thực hành
    
    *   Giải thích được toàn bộ flow boot → init → shell chạy được lệnh đầu tiên (không nhìn note) - đây là nền tảng bắt buộc trước khi bắt đầu sửa/mở rộng kernel ở các tuần sau.
        

### Tuần 40: System calls trong xv6

**Ngày 1-2: Đọc chương Traps**

*   Cơ chế
    
    *   Đọc xv6 book chương Traps - cơ chế syscall trong xv6 đơn giản hơn Linux thật (không có nhiều lớp tối ưu như vDSO), dễ nắm cơ chế gốc: user gọi `ecall` (RISC-V instruction tương đương `syscall` trên x86) → trap vào kernel → `usertrap()` xử lý → dispatch tới hàm syscall tương ứng qua bảng `syscalls[]`.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/syscall.c`, đối chiếu bảng `syscalls[]` với `arch/x86/entry/syscalls/syscall_64.tbl` đã dùng ở Phase 4 Tuần 33 - cùng khái niệm, quy mô nhỏ hơn rất nhiều.
        

**Ngày 3-5: System calls lab**

*   Thực hành - lab chính thức MIT, dự án chính của tuần
    
    *   **System calls lab** - tự thêm syscall mới vào xv6 (giống bài tập Phase 4 Tuần 33-34 nhưng trên kernel nhỏ hơn nhiều, dễ debug hơn: không cần chờ build 30 phút, không cần lo thiếu driver gây boot lỗi).
        
*   Advanced concepts
    
    *   Vì xv6 không có `copy_from_user`/`copy_to_user` phức tạp như Linux thật (dùng hàm đơn giản hơn `copyin`/`copyout` do chính bạn đọc được toàn bộ implementation), đây là cơ hội hiểu **tại sao** Linux thật cần các cơ chế bảo vệ phức tạp hơn (nhiều kiến trúc CPU, nhiều edge case bảo mật) mà xv6 lược bỏ.
        

**Ngày 6: So sánh xv6 vs Linux thật**

*   Thực hành
    
    *   So sánh: syscall bạn thêm vào xv6 vs syscall bạn thêm vào Linux thật (Phase 4 Tuần 33-34) - ghi chú khác biệt cụ thể (độ phức tạp code, thời gian build-test cycle, số bước validate cần thiết).
        

### Tuần 41: Page tables (Memory)

**Ngày 1-2: Đọc chương Page tables**

*   Cơ chế
    
    *   Đọc xv6 book chương Page tables - RISC-V dùng page table 3 cấp (Sv39), đơn giản hơn 4 cấp của x86-64 (đã học khái niệm Phase 3 Tuần 20) nhưng cùng nguyên lý dịch địa chỉ ảo → vật lý.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/vm.c`, tìm hàm `walk()` (hàm duyệt page table qua từng cấp) - đối chiếu với lý thuyết multi-level page table đã đọc ở `mm/page_alloc.c` (Phase 4 Tuần 32).
        

**Ngày 3-6: Page tables lab**

*   Thực hành - lab chính thức MIT
    
    *   **Page tables lab** - implement thêm tính năng liên quan virtual memory (VD: in ra nội dung page table của 1 process ở dạng đọc được, hoặc superpages tùy version lab hiện tại của MIT).
        
*   Advanced concepts
    
    *   Sau khi implement, bạn có thể **tận mắt in ra** đúng những gì `/proc/<pid>/maps` (Phase 3 Tuần 20) hiển thị trên Linux thật - nhưng lần này là code bạn tự viết để tạo ra thông tin đó, không phải đọc output có sẵn.
        

### Tuần 42: Traps & Interrupts

**Ngày 1-2: Đọc chương Traps sâu hơn**

*   Cơ chế
    
    *   Interrupt, exception, và syscall đều đi qua cùng 1 cơ chế trap trong xv6 - CPU chuyển từ user mode sang supervisor mode, lưu lại state, nhảy vào 1 điểm entry cố định (`kernelvec`/`uservec`), sau đó kernel phân loại đây là loại trap nào (timer interrupt, page fault, syscall...) để xử lý đúng.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/trap.c`, đối chiếu cách phân loại trap trong `usertrap()` với khái niệm "unified trap mechanism" - giải thích được vì sao 1 cơ chế duy nhất có thể xử lý cả 3 loại sự kiện rất khác nhau này.
        

**Ngày 3-6: Traps lab**

*   Thực hành - lab chính thức MIT
    
    *   **Traps lab** - thường bao gồm: implement stack backtrace (in call stack thủ công không dùng debugger, tự đọc frame pointer), xử lý alarm (kernel gọi lại 1 hàm userspace định kỳ dựa trên timer interrupt - tương tự khái niệm `SIGALRM` trên Linux thật).
        
*   Advanced concepts
    
    *   Implement backtrace thủ công ở đây giúp hiểu sâu hơn cách GDB (Phase 2.5 Tuần 18) tự động làm việc tương tự khi bạn gõ `backtrace` - bạn vừa tự tay làm cái mà GDB làm ẩn đằng sau.
        

### Tuần 43: Copy-on-write/Multithreading

**Ngày 1-2: Đọc chương Scheduling**

*   Cơ chế
    
    *   xv6 dùng round-robin scheduler đơn giản (khác hẳn CFS phức tạp của Linux thật đã học Phase 3 Tuần 25) - mỗi process được cấp 1 time-slice cố định theo vòng, không có khái niệm "fairness" tinh vi dựa trên `vruntime`.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/proc.c` hàm `scheduler()` - so sánh độ đơn giản với `kernel/sched/core.c` (Linux thật) đã đọc ở Phase 4 Tuần 32.
        

**Ngày 3-6: Copy-on-write fork lab**

*   Thực hành - lab chính thức MIT, bài kinh điển
    
    *   **Copy-on-write fork lab** - tối ưu hóa `fork()`: thay vì copy toàn bộ memory của parent ngay lập tức (như implementation xv6 gốc rất ngây thơ), chỉ copy page table và đánh dấu page read-only + shared, thật sự copy dữ liệu (page fault handler xử lý) chỉ khi 1 trong 2 bên ghi vào.
        
*   Advanced concepts
    
    *   Đây chính là cơ chế **thật sự** mà `fork()` trên Linux dùng (đã học khái niệm ở Phase 1 Tuần 5 khi nói "copy-on-write, không copy thật ngay") - giờ bạn tự tay implement chính xác cơ chế đó, hiểu rõ vì sao `fork()` trên Linux thật rất nhanh dù process có hàng GB memory.
        

### Tuần 44: Lock & Multiprocessor

**Ngày 1-2: Đọc chương Lock**

*   Cơ chế
    
    *   Race condition trong kernel thật (không phải chỉ userspace pthread đã học Phase 3 Tuần 27) - tại sao cần spinlock: trên hệ thống nhiều CPU core (xv6 hỗ trợ multiprocessor giả lập qua QEMU), 2 core có thể cùng lúc chạy code kernel và đụng độ cấu trúc dữ liệu chung (VD `struct proc` list).
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/spinlock.c` - implementation spinlock rất ngắn gọn (dùng atomic instruction `amoswap` của RISC-V), so sánh khái niệm với `pthread_mutex` (userspace, có thể ngủ) đã dùng Phase 3 Tuần 27.
        

**Ngày 3-6: Lock lab**

*   Thực hành - lab chính thức MIT
    
    *   **Lock lab** - fix race condition trong xv6 (thường liên quan tới memory allocator hoặc buffer cache dùng chung 1 lock cho toàn bộ hệ thống, gây bottleneck khi nhiều core cùng cần cấp phát/đọc block) - bài tập bắt buộc hiểu concurrency ở mức kernel.
        
*   Advanced concepts
    
    *   Kỹ thuật chia nhỏ lock (per-CPU allocator thay vì 1 global lock) - chính là ý tưởng cốt lõi mà Linux thật dùng ở quy mô lớn hơn rất nhiều (per-CPU variable, RCU đã đọc khái niệm ở Phase 4 Tuần 37-38) để scale tới hàng trăm core.
        

### Tuần 45: File system

**Ngày 1-2: Đọc chương File System**

*   Cơ chế
    
    *   inode, log-based crash recovery trong xv6 (bản mini của journaling thật đã học khái niệm Phase 3 Tuần 22) - xv6 filesystem có layer rõ ràng: disk → buffer cache → logging layer (đảm bảo crash-safety) → inode layer → directory layer → file descriptor layer, mỗi layer 1 file `.c` riêng, dễ đọc tuần tự.
        
*   Thực hành và quan sát
    
    *   Đọc `kernel/fs.c`, `kernel/log.c` theo đúng thứ tự layer trong sách - đối chiếu trực tiếp với khái niệm VFS/inode/journaling đã học ở Linux thật.
        

**Ngày 3-6: File system lab**

*   Thực hành - lab chính thức MIT
    
    *   **File system lab** - thêm tính năng vào xv6 fs (VD large files - hỗ trợ indirect block cấp 2 để file lớn hơn giới hạn gốc, hoặc symlinks - liên kết mềm giống `ln -s` đã dùng suốt các phase trước).
        
*   Advanced concepts
    
    *   Sau khi implement large file support, bạn hiểu chính xác vì sao filesystem thật (ext4/xfs) cần cấu trúc block pointer phức tạp hơn nhiều (multi-level indirect, extent-based ở xfs) để hỗ trợ file terabyte mà vẫn tra cứu nhanh.
        

### Tuần 46: Tổng kết & (tùy chọn) bare-metal OSDev

**Ngày 1-3: Ôn tập toàn bộ xv6**

*   Thực hành
    
    *   Viết 1 tài liệu tổng kết kiến trúc xv6 hoàn chỉnh bằng lời của bạn: process (fork/exec/scheduling), memory (page table/copy-on-write), fs (inode/log), syscall (trap mechanism), lock (spinlock/concurrency) - đồng thời chú thích mỗi phần tương ứng với khái niệm nào đã học ở Linux thật (Phase 3-4).
        
*   Advanced concepts
    
    *   Đây là bài tập tổng hợp quan trọng nhất Phase 5: khả năng **map** kiến thức từ 1 kernel nhỏ (xv6, đọc hết được) sang kernel lớn (Linux, không đọc hết được) chính là kỹ năng thật sự hữu ích - bạn sẽ dùng kỹ năng "map khái niệm" này suốt sự nghiệp khi gặp bất kỳ hệ thống phức tạp mới nào.
        

**Ngày 4-6: Tùy chọn nâng cao - bare-metal OSDev**

*   Thực hành (tùy chọn)
    
    *   Nếu muốn trải nghiệm viết bootloader thật từ x86 assembly (điều xv6 đã làm sẵn cho bạn qua QEMU boot thẳng), theo OSDev.org "Bare Bones" tutorial - viết bootloader + nhảy vào 32-bit protected mode + in ra "Hello World" lên màn hình VGA trực tiếp bằng assembly + C.
        
*   Advanced concepts
    
    *   Đây là trải nghiệm "từ số 0 tuyệt đối" - nhưng làm **sau** khi đã hiểu xv6 sẽ hiệu quả hơn nhiều vì bạn đã biết bức tranh lớn (process/memory/fs/syscall) và chỉ còn thiếu mảnh ghép "làm sao CPU đi từ trạng thái vừa bật nguồn đến chạy được C code" - đúng phạm vi mà bare-metal tutorial này lấp đầy.
        

## Phase 6 - Tự định hướng

Không còn lịch ngày cụ thể - đây là giai đoạn tự định hướng dựa trên toàn bộ nền tảng đã xây dựng qua Phase 1-5:

*   Theo dõi **LKML** (lore.kernel.org) hàng tuần, đọc thread liên quan mảng bạn thích - giờ bạn đã có đủ vốn từ vựng kỹ thuật (process, memory, VFS, syscall, lock) để hiểu phần lớn thảo luận thay vì chỉ đọc lướt.
    
*   Chọn 1 mảng để đào sâu thành chuyên gia: networking stack (Phase 3 Tuần 26 + Phase 3 Tuần 24 eBPF), cgroups/namespaces (Phase 2 Tuần 12 + Tuần 14), hoặc filesystem (Phase 3 Tuần 22 + Phase 5 Tuần 45) - mỗi mảng đều đã có nền tảng vững từ lộ trình.
    
*   Sửa 1 bug nhỏ trong kernel thật (coding style/doc typo), gửi patch đầu tiên qua `git send-email` - quy trình đóng góp thật, dùng đúng kỹ năng Git đã học ở Phase 2.5 Tuần 19 (`format-patch`, `am`).
    
*   Tham gia **kernelnewbies.org** - cộng đồng dành riêng cho người mới muốn đóng góp kernel.
