CHƯƠNG 01 · FOUNDATION · ~90 phút

OS là gì?
Kernel & System Calls

Trước khi đi vào process, thread, memory — phải hiểu OS làm gì và đâu là ranh giới giữa "code của bạn" với "kernel". System call là cánh cửa duy nhất giữa hai thế giới đó. Hiểu chương này, bạn nắm được nguyên lý hoạt động của mọi chương sau.

1.1 OS là gì?

Operating System (OS) là phần mềm đứng giữa phần cứngứng dụng. Nó là "lớp trung gian" cung cấp:

  • Trừu tượng hoá phần cứng: app không cần biết RAM hãng nào, ổ cứng giao thức gì — chỉ cần gọi read/write
  • Quản lý tài nguyên: chia CPU, RAM, disk, network cho nhiều process công bằng
  • Bảo vệ & cô lập: process này không thể "đọc trộm" RAM của process khác hoặc làm crash hệ thống
  • Cung cấp API thống nhất: open/close/read/write — cùng API cho file, socket, device
┌─────────────────────────────────────────────────┐ │ Application Layer │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ Chrome │ │ VS Code│ │ Node.js│ ... │ │ └────────┘ └────────┘ └────────┘ │ └────────────────┬────────────────────────────────┘ │ system calls ▼ ┌─────────────────────────────────────────────────┐ │ Operating System (Kernel) │ │ Process Mgmt │ Memory Mgmt │ FS │ Net │ Driver │ └────────────────┬────────────────────────────────┘ │ machine instructions ▼ ┌─────────────────────────────────────────────────┐ │ Hardware │ │ CPU │ RAM │ Disk │ NIC │ GPU │ Keyboard ... │ └─────────────────────────────────────────────────┘

Không có OS, mỗi app sẽ phải tự viết code điều khiển từng loại phần cứng — chuyện không tưởng. OS giải quyết bài toán đó một lần.

1.2 4 chức năng chính của OS

  1. Process Management: tạo, hủy, lập lịch, đồng bộ, giao tiếp giữa các process. Đây là chương 2-7 của giáo trình.
  2. Memory Management: cấp phát RAM cho process, tạo virtual memory, paging, swap. Chương 8-9.
  3. File System & Storage: tổ chức dữ liệu trên disk, file/directory, permission. Chương 10.
  4. I/O & Device Driver: giao tiếp với keyboard, mouse, disk, network, GPU thông qua driver. Liên quan chương 10.

OS hiện đại còn có: networking stack, security/permission, virtualization (KVM, container), GUI...

1.3 User Space vs Kernel Space

Đây là khái niệm cốt lõi của OS. CPU hỗ trợ ít nhất 2 mức privilege (chế độ thực thi):

Kernel Space (Ring 0 trên x86)

  • Có quyền tối cao: truy cập mọi RAM, mọi I/O port, mọi instruction
  • Chỉ kernel chạy ở đây
  • Lỗi ở đây = kernel panic (crash toàn hệ)

User Space (Ring 3 trên x86)

  • Quyền hạn chế: chỉ truy cập memory của process mình, không gọi được instruction nguy hiểm
  • App của bạn (Chrome, VS Code, Node.js) chạy ở đây
  • Lỗi ở đây = segmentation fault (chỉ crash 1 process, OS vẫn sống)

CPU mode bit (1 flag trong CPU register) xác định đang chạy mode nào. Khi syscall, CPU tự động chuyển từ user mode sang kernel mode (mode switch). Khi kernel xử lý xong, chuyển ngược lại.

⚠️ Đừng nhầm Mode Switch và Context Switch
  • Mode Switch: chuyển từ user mode sang kernel mode (hoặc ngược lại) — vẫn cùng một process. Rất nhanh.
  • Context Switch: chuyển CPU từ process A sang process B — phải lưu/restore trạng thái CPU. Tốn hơn.
Phỏng vấn hay hỏi sự khác biệt này.

1.4 System Call — cầu nối user và kernel

User app không thể tự đọc/ghi disk, gửi packet network, tạo process — vì những việc đó đòi hỏi quyền kernel. Vậy app làm cách nào?

Câu trả lời: System Call (syscall). App "yêu cầu" kernel làm việc thay mình bằng cách gọi syscall. Đây là cánh cửa duy nhất giữa user space và kernel.

// Trong C, mọi I/O đều qua syscall
#include <unistd.h>
int main() {
    write(1, "Hello\n", 6);  // syscall write(fd, buf, count)
    return 0;
}

Khi gọi write:

  1. App đặt syscall number (vd 1 cho write trên Linux x86_64) vào CPU register
  2. App đặt arguments vào các register khác
  3. App thực thi instruction syscall (hoặc int 0x80 trên 32-bit)
  4. CPU tự động: lưu user state, chuyển sang kernel mode, nhảy đến syscall handler
  5. Kernel tra bảng syscall, gọi hàm tương ứng (vd sys_write)
  6. Kernel thực hiện công việc (ghi vào terminal, vào file...)
  7. Kernel restore user state, chuyển về user mode
  8. App tiếp tục chạy

Trong JavaScript / Node.js?

// Mọi I/O trong Node đều cuối cùng cũng là syscall
const fs = require('fs');
fs.writeFileSync('/tmp/hello.txt', 'Hi!');
// Node → libuv → C call write() → kernel sys_write() → device driver → disk

Khi bạn dùng console.log, fs.readFile, http.get — tất cả cuối cùng đều là syscall.

1.5 Syscall Lifecycle — chi tiết

USER SPACE KERNEL SPACE ────────── ───────────── app calls write(1, "Hi", 2) │ ▼ libc wrapper: - rax ← 1 (syscall_nr SYS_write) - rdi ← 1 (fd) - rsi ← "Hi" - rdx ← 2 │ ▼ syscall instruction │═════════════ MODE SWITCH ═════════════▶ ┌──────────────────┐ │ syscall handler │ │ (entry point) │ └────────┬─────────┘ ▼ tra bảng syscall │ ▼ gọi sys_write(fd, buf, n) │ ▼ VFS → driver → device │ ▼ return value (số byte đã ghi) │ ◀════════════ MODE SWITCH ═════════════════│ │ ▼ write() returns to caller

Tốn bao nhiêu thời gian? 1 syscall trên CPU hiện đại tốn ~100-300 nanoseconds. Nghe nhanh, nhưng nếu app gọi syscall trong vòng lặp triệu lần, có thể trở thành bottleneck. Đây là lý do fwrite (buffered) nhanh hơn write (unbuffered) — nó gom nhiều thao tác thành 1 syscall.

1.6 Common Syscalls — phải biết

SyscallChức năngChương liên quan
forkTạo process con (copy parent)Ch.02
execveThay thế process hiện tại bằng program khácCh.02
waitParent đợi child kết thúcCh.02
exitProcess tự kết thúcCh.02
cloneTạo thread (Linux-specific)Ch.03
open / closeMở / đóng file, trả về file descriptorCh.10
read / writeĐọc / ghi qua fdCh.10
mmap / munmapMap file/memory vào address spaceCh.08
brk / sbrkMở rộng heapCh.08
pipeTạo pipe IPCCh.05
killGửi signal đến processCh.05
socket / connect / acceptNetworkCh.05, 10
select / poll / epollI/O multiplexingCh.10
ioctlThao tác đặc biệt với device

Linux x86_64 có ~330 syscalls. Bạn không cần thuộc hết — hiểu top 20 là đủ.

1.7 Trace syscall thực tế với strace

strace là công cụ Linux ghi lại mọi syscall mà 1 process gọi. Cực hữu ích để debug và học OS.

# Trace lệnh ls
strace ls /tmp 2>&1 | head -30

# Output (đã rút gọn):
execve("/usr/bin/ls", ["ls", "/tmp"], 0x...) = 0
brk(NULL)                               = 0x...
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY) = 3
read(3, "...", 832)                     = 832
close(3)                                = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY) = 3
mmap(NULL, 145296, PROT_READ, ...)      = 0x...
...
openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
getdents64(3, /* 5 entries */, 32768)   = 144
write(1, "file1.txt  file2.txt\n", 21)  = 21
close(3)                                = 0
exit_group(0)                           = ?

Bạn thấy gì? Một lệnh ls đơn giản gọi mấy chục syscall: load shared library, mở thư mục, read entries, in ra stdout, đóng.

Filter syscall theo tên

# Chỉ trace syscall liên quan đến file
strace -e openat,read,write,close ls /tmp

# Trace + đếm số lần mỗi syscall
strace -c ls /tmp
# % time     seconds  usecs/call     calls    errors syscall
# ------ ----------- ----------- --------- --------- ----------------
#  25.00    0.000125          12        10           openat
#  20.00    0.000100          11         9           mmap
#  ...

Trên macOS: dtrace / dtruss

# macOS không có strace, dùng dtruss (cần sudo + SIP off một phần)
sudo dtruss ls /tmp 2>&1 | head -20
💡 Bài tập "phải làm" trước khi qua chương sau
Mở terminal Linux (hoặc WSL) và chạy strace -c ls. Quan sát các syscall bạn vừa học (openat, read, write, close, mmap...). Đây là khoảnh khắc bạn "chạm" vào kernel lần đầu.

1.8 Monolithic vs Microkernel

Monolithic Kernel

Toàn bộ OS (scheduler, memory, FS, driver, network stack) chạy trong một không gian kernel duy nhất.

  • Ưu: nhanh — gọi function trong cùng kernel = direct call
  • Nhược: bug ở bất kỳ component nào = kernel panic toàn hệ
  • Ví dụ: Linux, BSD, Windows NT (hybrid nhưng nghiêng monolithic)

Microkernel

Kernel chỉ giữ tối thiểu: scheduler, IPC, memory cơ bản. Các service khác (FS, driver, network) chạy ở user space.

  • Ưu: độ tin cậy cao — driver crash không sập kernel; dễ test/verify (vd seL4 đã được formal verify)
  • Nhược: chậm hơn — gọi service phải qua IPC
  • Ví dụ: Minix, QNX, seL4, MINIX-based Intel ME

Hybrid Kernel

Cố trộn ưu điểm cả hai. macOS XNU, Windows NT.

📐 Tranh luận lịch sử
Năm 1992, Andrew Tanenbaum (tác giả Minix) và Linus Torvalds tranh luận trên Usenet: "Linux is obsolete" — Tanenbaum nói monolithic kernel đã lỗi thời. Linus phản bác. Cuối cùng: Linux thành công lớn dù monolithic. Lý do: pragmatic > academic purity. Đây là một trong những flame war nổi tiếng nhất lịch sử OS.

1.9 Boot Process — máy khởi động thế nào?

Từ lúc bạn bấm nút Power đến khi shell hiện ra:

  1. BIOS/UEFI chạy từ ROM trên mainboard. Kiểm tra hardware (POST), tìm boot device.
  2. Bootloader (vd GRUB) load từ MBR/EFI partition. Hiển thị menu chọn OS, load kernel image vào RAM.
  3. Kernel bắt đầu chạy: khởi tạo memory, detect hardware, load drivers, mount root filesystem.
  4. init/systemd (PID 1) chạy. Đây là process đầu tiên ở user space, làm parent cho mọi process khác. Khởi động services (cron, network, sshd...).
  5. Login screen hoặc shell hiện ra. Bạn đăng nhập, getty/login spawn shell.
# Xem PID 1 trên Linux
ps -p 1 -o comm=
# Output: systemd  (hoặc init trên hệ cổ)

# Toàn bộ process tree
ps auxf | head -20

Bài tập

Bài 1 — Strace your shell

Mở Linux/macOS terminal. Chạy strace -c ls (Linux) hoặc sudo dtruss ls (macOS). Liệt kê 5 syscall xuất hiện nhiều nhất.

Bài 2 — Hello World qua syscall trực tiếp

Viết một chương trình C in "Hello" mà KHÔNG dùng printf. Chỉ dùng write(1, ...).

Bài 3 — Đo overhead của syscall

Viết loop gọi getpid() 10 triệu lần. Đo thời gian. So sánh với 10 triệu phép toán i++. Sự khác biệt đo overhead syscall.

Bài 4 — Kernel panic vs Segfault

Tự nghiên cứu: trên Linux/macOS, làm thế nào để "tạo" segfault chủ ý? (Hint: dereference null pointer trong C). Quan sát output. Có ảnh hưởng đến process khác không? Đến OS không?

Bài 5 — PID 1

Trên máy bạn, PID 1 là gì? ps -p 1 -o comm= trên Linux. Trên Docker container thường là gì? Tại sao?

🧪 Quiz cuối chương

Câu 1. Syscall là gì?

  • Function bình thường trong C
  • Thư viện như libc, libm
  • Cách user app yêu cầu kernel làm việc — chuyển từ user mode sang kernel mode
  • API của OS để app cài đặt phần mềm

Đáp án: cách user app yêu cầu kernel. Syscall là cánh cửa duy nhất giữa user space và kernel space. CPU thực hiện mode switch khi syscall xảy ra.

Câu 2. Mode Switch khác Context Switch ở đâu?

  • Cả hai giống nhau
  • Mode switch = đổi user/kernel mode (cùng process); Context switch = đổi process
  • Mode switch chỉ trên Linux, Context switch chỉ trên Windows
  • Mode switch tốn hơn Context switch

Đáp án: Mode switch không đổi process, Context switch đổi. Context switch tốn hơn vì phải lưu/restore PCB.

Câu 3. Linux dùng kernel architecture gì?

  • Monolithic kernel (với loadable kernel modules)
  • Microkernel
  • Exokernel
  • Pure unikernel

Đáp án: Monolithic. Toàn bộ Linux kernel chạy cùng không gian, nhưng có hỗ trợ load module động (kmod).

Câu 4. Tại sao app không thể truy cập trực tiếp phần cứng?

  • Vì app viết bằng ngôn ngữ cấp cao
  • Vì hardware quá phức tạp
  • Vì user mode không có quyền instruction nguy hiểm — buộc phải qua kernel để cô lập & bảo vệ
  • Vì OS không support

Đáp án: vì cô lập & bảo vệ. CPU mode bit ngăn user app chạy instruction nguy hiểm. Kernel kiểm soát quyền truy cập.

Câu 5. Trong Linux, PID 1 thường là?

  • Kernel
  • init / systemd — process đầu tiên ở user space
  • shell user
  • Random process

Đáp án: init/systemd. PID 1 là parent của mọi process khác. Trên container, có thể là process bạn chạy (vd node app.js).

Câu 6. Khi C code gọi printf("Hi"), syscall thực sự xảy ra là?

  • write — ghi ra stdout (fd=1)
  • print
  • fork
  • open

Đáp án: write. printf là wrapper của libc, cuối cùng gọi syscall write(1, ...).

Câu 7. Lệnh strace dùng để?

  • Stress test CPU
  • Quản lý thread
  • Trace mọi syscall mà 1 process gọi
  • Compile code

Đáp án: trace syscall. Cực hữu ích để debug và học OS.

Câu 8. Microkernel "an toàn hơn" monolithic vì?

  • Code microkernel đơn giản hơn
  • Driver/service crash chỉ ảnh hưởng module đó (chạy ở user space), không sập kernel
  • Microkernel chống virus tốt hơn
  • Microkernel mã hoá memory

Đáp án: cô lập services. Driver crash trong monolithic = kernel panic. Trong microkernel = chỉ restart service.

Tổng kết chương 1

  • ✅ OS = lớp trung gian giữa app và hardware; 4 chức năng: process, memory, file, I/O
  • User space (app, ring 3) vs Kernel space (kernel, ring 0)
  • System call = cánh cửa duy nhất giữa hai thế giới
  • ✅ Mode switch ≠ Context switch
  • ✅ Common syscalls: fork, execve, open, read, write, mmap, pipe, socket
  • strace để quan sát syscall thật
  • ✅ Monolithic (Linux) vs Microkernel (seL4) — đánh đổi tốc độ vs an toàn
  • ✅ Boot: BIOS → bootloader → kernel → init/systemd → shell
← Quay lại Trang chủ OS Chương kế tiếp Chương 02: Process & PCB →