Khái niệm Tập tin Cơ bản
Tập tin trống vẫn chiếm không gian trên đĩa vật lý. Bản chất một tập tin bao gồm hai thành phần: dữ liệu và thuộc tính. Thao tác trên tập tin thực chất là tương tác với cả hai thành phần này. Hệ thống định danh tập tin duy nhất thông qua đường dẫn kết hợp tên tập tin.
Khi chương trình thực thi, quá trình xử lý tập tin diễn ra thông qua tiến trình đang chạy. Một tập tin chỉ có thể được truy cập sau khi được mở. Mối quan hệ then chốt được mô tả: Tiến trình ↔ Tập tin đã mở. Khi mở tập tin lần đầu, hệ điều hành nạp File Control Block (FCB) vào bộ nhớ chứ không tải toàn bộ nội dung tập tin.
Giao diện Nhập/Xuất trong C
Các ngôn ngữ lập trình đều cung cấp API thao tác tập tin, nhưng đều dựa trên hệ thống gọi hàm của hệ điều hành. Dù sử dụng ngôn ngữ nào, lớp dưới cùng luôn tương tác trực tiếp với kernel Linux.
// Mở tập tin với chế độ ghi
FILE* log_file = fopen("system_log.txt", "w");
Chế độ "w" sẽ xóa nội dung tập tin hiện có trước khi ghi mới. Hàm fprintf() được dùng để ghi dữ liệu định dạng vào tập tin thông qua con trỏ tập tin.
Giao diện Hệ thống I/O
Hệ thống gọi hàm gốc để thao tác tập tin:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mo_tap_tin(const char* duong_dan, int co_hieu);
int mo_tap_tin(const char* duong_dan, int co_hieu, mode_t quyen);
Tham số co_hieu sử dụng kỹ thuật bitmask để kết hợp các cờ:
#define GHI_CHI (1<<0)
#define TAO_MOI (1<<3)
#define XOA_TRUOC (1<<4)
Ví dụ mở tập tin với quyền ghi và tạo mới:
int fd = mo_tap_tin("system_log.txt", GHI_CHI | TAO_MOI, 0644);
Giá trị trả về là file descriptor (số nguyên) - bản chất là chỉ mục mảng trong bảng mô tả tập tin của tiến trình.
Cơ chế File Descriptor
Hệ điều hành quản lý tập tin đã mở thông qua cấu trúc struct file trong kernel. Mỗi tiến trình có bảng mô tả tập tin (file descriptor table) trong PCB (Process Control Block). File descriptor chính là chỉ mục của mảng con trỏ trỏ đến cấu trúc struct file tương ứng.
Khi tiến trình khởi tạo, ba file descriptor mặc định được cấp phát:
- 0: stdin (đầu vào chuẩn)
- 1: stdout (đầu ra chuẩn)
- 2: stderr (lỗi chuẩn)
Các file descriptor mới bắt đầu từ giá trị 3.
Định tuyến Lại Dòng Nhập/Xuất
Quy tắc cấp phát file descriptor: tìm chỉ mục trống nhỏ nhất trong bảng mô tả. Kỹ thuật định tuyến lại (redirection) thay đổi đích ghi của stdout:
int fd_moi = mo_tap_tin("output.log", GHI_CHI | TAO_MOI);
dup2(fd_moi, 1); // Ghi stdout vào file output.log
Hàm dup2() sao chép file descriptor nguồn vào đích, thay thế kết nối hiện tại. Trong môi trường tiến trình con, bảng mô tả tập tin được sao chép độc lập do tính cách ly của tiến trình.
Triết lý "Mọi thứ đều là Tập tin"
Linux biểu diễn tất cả thiết bị phần cứng dưới dạng tập tin ảo thông qua cấu trúc thống nhất:
struct file_vao {
int loai;
const char* ten;
int (*ghi)(void*, size_t);
int (*doc)(void*, size_t);
};
Mỗi thiết bị cài đặt riêng hàm ghi/doc. Ví dụ: bàn phím chỉ triển khai hàm đọc, card mạng triển khai cả hai. Cơ chế này áp dụng nguyên lý đa hình trong thiết kế hệ thống.
Bộ đệm Nhập/Xuất
Bộ đệm là vùng nhớ tạm lưu dữ liệu trước khi ghi ra thiết bị. Có ba chính sách làm mới chính:
- Làm mới tức thì (không đệm)
- Làm mới theo dòng (dành cho thiết bị hiển thị)
- Làm mới khi đầy bộ đệm (dành cho tập tin đĩa)
Bộ đệm được quản lý bởi thư viện C (cấu trúc FILE), không nằm trong nhân hệ điều hành. Hàm fflush() cho phép làm mới bộ đệm chủ động.
Cơ sở Hệ thống Tập tin
Đĩa cứng được tổ chức thành các khối 4KB (kích thước tối ưu). Hệ thống tập tin chia không gian thành các nhóm (block groups) với cấu trúc:
- Superblock: chứa thông tin hệ thống tập tin
- Bảng mô tả nhóm (Group Descriptor)
- Bản đồ khối (Block Bitmap)
- Bản đồ inode (Inode Bitmap)
- Bảng inode (lưu thuộc tính tập tin)
- Khối dữ liệu (lưu nội dung)
Mỗi tập tin có một inode duy nhất. Thư mục là tập tin đặc biệt lưu ánh xạ tên tập tin → số inode. Việc xóa tập tin thực chất là giải phóng inode và các khối dữ liệu.
Liên kết Cứng và Liên kết Mềm
Liên kết cứng tạo ánh xạ mới đến cùng inode:
ln file_goc file_lien_ket
Số lượng liên kết cứng được theo dõi qua bộ đếm tham chiếu. Tập tin chỉ bị xóa khi bộ đếm này về 0.
Liên kết mềm tạo inode mới trỏ đến đường dẫn đích:
ln -s file_goc lien_ket_mem
Liên kết mềm trở nên vô hiệu khi tập tin gốc bị xóa. Việc gỡ liên kết sử dụng lệnh unlink.