The Design Philosophy of ‘Everything is a File’ in Linux

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:

  1. Unified Interface: Provides a consistent access interface through the file system
  2. Abstract Encapsulation: Abstracts resources such as devices and processes as files
  3. 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

  1. 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
    
  2. Composability: Pipes connect different programs

    # Combine multiple programs through pipes
    dmesg | grep "error" | wc -l
    
  3. 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

  1. File Descriptor Limit Check

    #include <sys/resource.h>
    
    struct rlimit lim;
    getrlimit(RLIMIT_NOFILE, &lim);  // Get maximum number of file descriptors
    
  2. Non-blocking IO Handling

    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);  // Set non-blocking
    
  3. File Permission Control

    // Check actual file permissions
    if (access("/dev/gpio", W_OK) == -1) {
        perror("No write permission");
    }
    

9. Performance Tuning Techniques

  1. 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
    
  2. File Prefetch Configuration

    posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);  // Enable sequential prefetch
    
  3. 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.

Leave a Comment