1. Core Concept of Unix/Linux File Abstraction
“Everything is a file” is one of the core design philosophies of Unix/Linux systems, which essentially means:
- Unified Interface: Provides a consistent access interface through the file system
- Abstract Encapsulation: Abstracts resources such as devices and processes as files
- Simplified Operations: Uses a unified file operation API (open/read/write/ioctl/close)
2. Overview of Linux File Types
graph TD
A[File] --> B[Regular File]
A --> C[Directory File]
A --> D[Device File]
D --> E[Character Device]
D --> F[Block Device]
A --> G[Symbolic Link]
A --> H[Pipe File]
A --> I[Socket File]
A --> J[procfs File]
A --> K[sysfs File]
3. Code Examples: Accessing Various Resources via File Interface
1. Accessing Hardware Devices (Character Device Example)
#include <fcntl.h>
#include <unistd.h>
// Operate serial port device like a regular file
int main() {
int serial_fd = open("/dev/ttyS0", O_RDWR);
if (serial_fd < 0) {
perror("Failed to open serial port");
return 1;
}
char buf[256];
ssize_t n = read(serial_fd, buf, sizeof(buf)); // Read serial data
write(serial_fd, "ATZ\r", 4); // Send serial command
close(serial_fd);
return 0;
}
2. Accessing Process Information (procfs Example)
// Get process information by reading /proc file
void print_process_meminfo(pid_t pid) {
char path[256];
sprintf(path, "/proc/%d/status", pid);
FILE* f = fopen(path, "r");
if (!f) {
perror("Cannot open process status file");
return;
}
char line[256];
while (fgets(line, sizeof(line), f)) {
if (strstr(line, "VmRSS")) { // Find physical memory usage
printf("Process %d memory usage: %s", pid, line);
break;
}
}
fclose(f);
}
3. System Information Monitoring (sysfs Example)
// Read CPU temperature from /sys
float get_cpu_temperature() {
FILE* f = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
if (!f) return -1;
int temp;
fscanf(f, "%d", &temp);
fclose(f);
return temp / 1000.0f; // Convert to Celsius
}
4. Detailed Explanation of Special File Types
1. Principles of Device File Operations
// Device control through ioctl at the low level
int set_serial_baudrate(int fd, int baud) {
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, baud);
cfsetospeed(&options, baud);
return tcsetattr(fd, TCSANOW, &options);
}
2. Named Pipe (FIFO) Communication Example
Process A (Writer):
mkfifo("/tmp/myfifo", 0666); // Create named pipe
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "Hello FIFO!", 11);
close(fd);
Process B (Reader):
int fd = open("/tmp/myfifo", O_RDONLY);
char buf[256];
read(fd, buf, sizeof(buf));
printf("Received: %s\n", buf);
close(fd);
3. Memory-Mapped File Example
// Map file to memory region
int fd = open("data.bin", O_RDWR);
void* addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// Direct memory access to file content
int* data = (int*)addr;
data[0] = 0x12345678; // Modification will directly write to file
munmap(addr, 4096);
close(fd);
5. Kernel Implementation Mechanism Analysis
1. VFS (Virtual File System) Architecture
graph BT
A[User Space] -->|System Call| B(VFS Layer)
B --> C(Ext4)
B --> D(ProcFS)
B --> E(DevFS)
B --> F(NFS)
C --> G[Disk]
D --> H[Kernel Data Structure]
E --> I[Hardware Device]
2. File Operation Abstraction (file_operations Structure)
// Kernel drivers must implement these operations
struct file_operations {
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
long (*ioctl)(struct file *, unsigned int, unsigned long);
int (*open)(struct inode *, struct file *);
int (*release)(struct inode *, struct file *);
// ...
};
// Example: Simple Character Device Driver
static const struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.read = mydev_read,
.write = mydev_write,
.open = mydev_open,
.release = mydev_release,
.unlocked_ioctl = mydev_ioctl,
};
6. Modern Linux Extended Applications
1. cgroups Control Groups (via File System Interface)
# Actually controlled through file system operations
echo $$ > /sys/fs/cgroup/cpu/mygroup/tasks
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us
2. eBPF Debugging Interface
// Files under /sys/fs/bpf/ are used for eBPF interaction
int map_fd = bpf_obj_get("/sys/fs/bpf/my_map");
bpf_map_lookup_elem(map_fd, &key, &value);
3. User Space File System (FUSE Example)
// Implement custom file system operations
static struct fuse_operations myfs_ops = {
.getattr = myfs_getattr,
.readdir = myfs_readdir,
.open = myfs_open,
.read = myfs_read,
.write = myfs_write,
};
int main(int argc, char *argv[]) {
return fuse_main(argc, argv, &myfs_ops, NULL);
}
7. Analysis of Design Advantages
-
Uniformity: All resources use the same API for operations
// The same read function can operate on: read(disk_fd, buf, len); // Disk file read(socket_fd, buf, len); // Network socket read(proc_fd, buf, len); // Process information
-
Composability: Pipes connect different programs
# Combine multiple programs through pipes dmesg | grep "error" | wc -l
-
Extensibility: Easily add new resource types
// New devices only need to implement file_operations static const struct file_operations newdev_fops = { .read = newdev_read, // ... };
8. Practical Considerations
-
File Descriptor Limit Check
#include <sys/resource.h> struct rlimit lim; getrlimit(RLIMIT_NOFILE, &lim); // Get maximum number of file descriptors
-
Non-blocking IO Handling
int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); // Set non-blocking
-
File Permission Control
// Check actual file permissions if (access("/dev/gpio", W_OK) == -1) { perror("No write permission"); }
9. Performance Tuning Techniques
-
Batch Read/Write Optimization
struct iovec iov[2]; iov[0].iov_base = buf1; iov[0].iov_len = len1; iov[1].iov_base = buf2; iov[1].iov_len = len2; ssize_t n = readv(fd, iov, 2); // Scatter read
-
File Prefetch Configuration
posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); // Enable sequential prefetch
-
Direct IO Bypass Cache
int fd = open("data.bin", O_RDWR | O_DIRECT); // Direct disk access
The design philosophy of “Everything is a file” keeps Linux systems remarkably simple and extensible. Although this abstraction adds a slight performance overhead, the development convenience and system consistency it brings are irreplaceable. Understanding this design concept is key to mastering Linux system programming.