【Linux】文件系统与设备文件

发布于:2024-12-08 ⋅ 阅读:(112) ⋅ 点赞:(0)

在这里插入图片描述

Linux操作系统支持多种文件系统,并且通过设备文件来访问硬件设备。理解Linux文件系统的工作原理以及设备文件是如何与硬件交互的,对于系统管理员和开发人员来说非常重要。

1. 引言

Linux支持多种文件系统,每种文件系统都有自己的特点和适用场景。文件系统不仅用于存储数据,还用于组织和管理这些数据。此外,Linux通过设备文件来与硬件设备进行交互,设备文件可以被视为一种特殊的文件,它们代表了物理设备或虚拟设备。

2. Linux文件系统概述

2.1 文件系统的概念

文件系统是一种用于组织和管理计算机数据的方法,它定义了数据存储、检索和更新的方式。Linux支持多种文件系统,每种文件系统都有自己的优点和适用范围。

2.1.1 文件系统层次结构

Linux文件系统层次结构通常从根目录 / 开始,所有的文件和目录都是相对于根目录的。文件系统层次结构遵循一定的标准,如 FHS(Filesystem Hierarchy Standard),它规定了各个目录的用途。

2.2 文件系统的类型

Linux支持多种类型的文件系统,包括但不限于:

  1. Ext文件系统

    • Ext2:第二代扩展文件系统,不支持日志记录。
    • Ext3:第三代扩展文件系统,支持日志记录。
    • Ext4:第四代扩展文件系统,改进了性能和可靠性。
  2. Btrfs

    • Btrfs(B-tree文件系统)是一种较新的文件系统,支持子卷、快照、校验和等功能。
  3. XFS

    • XFS(eXtended File System)是一种高性能的日志文件系统,适用于大型文件和高吞吐量的场景。
  4. ReiserFS

    • ReiserFS 是一种基于树形结构的文件系统,设计用于提高性能。
  5. FAT/FAT32

    • FAT/FAT32 文件系统主要用于兼容性要求较高的场景,如 U 盘、SD 卡等。
  6. NTFS

    • NTFS(New Technology File System)是 Windows 操作系统默认的文件系统,Linux 也可以通过第三方驱动支持。
  7. tmpfs

    • tmpfs 是一种基于内存的文件系统,主要用于临时存储数据。
  8. OverlayFS

    • OverlayFS 允许在不修改原始文件系统的情况下创建一个新的文件系统视图。
  9. UnionFS

    • UnionFS 允许多个文件系统层次结构合并在一起,形成一个单一的文件系统视图。

2.3 文件系统的挂载

文件系统必须挂载到 Linux 系统的一个目录上才能被访问。挂载过程涉及到将文件系统连接到文件层次结构中的一个点。

2.3.1 挂载命令

使用 mount 命令挂载文件系统:

sudo mount /dev/sda1 /mnt
2.3.2 自动挂载

可以将文件系统的挂载配置写入 /etc/fstab 文件,这样每次系统启动时都会自动挂载指定的文件系统。

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/dev/sda1 /mnt                ext4    defaults        0       2
2.3.3 挂载选项

挂载文件系统时可以指定一些选项,常见的选项包括:

  • rw:可读写模式。
  • ro:只读模式。
  • defaults:使用默认选项。
  • noauto:不自动挂载。
  • usrquota:启用用户配额。
  • grpquota:启用组配额。
  • exec:允许执行二进制文件。
  • noexec:不允许执行二进制文件。
  • suid:允许设置 UID 位。
  • nosuid:不允许设置 UID 位。
  • dev:允许设备节点。
  • nodev:不允许设备节点。
2.3.4 查看挂载信息

使用 mount 命令查看当前挂载的文件系统:

mount

2.4 文件系统的卸载

使用 umount 命令卸载文件系统:

sudo umount /mnt

3. 文件系统的底层原理

3.1 文件系统结构

文件系统的基本结构通常包括:

  • 超级块(Superblock):描述文件系统的全局信息,如总块数、已用块数、剩余块数等。
  • 索引节点(Inode):存储文件的属性和指向数据块的指针。
  • 数据块(Data Blocks):实际存储文件数据的地方。
  • 目录项(Directory Entries):存储文件名和对应的索引节点号。

3.2 超级块

超级块是文件系统的重要组成部分,它包含了文件系统的基本信息。超级块的位置通常固定在文件系统的起始位置。

3.2.1 超级块的结构

以 Ext4 文件系统为例,超级块的结构如下:

struct ext4_super_block {
    u32 s_inodes_count;       /* Total number of inodes */
    u32 s_blocks_count_lo;    /* Total blocks count */
    u32 s_r_blocks_count_lo;  /* Free blocks count */
    u32 s_free_blocks_count;  /* Free blocks beyond r_blocks */
    u32 s_free_inodes_count;  /* Free inodes count */
    u32 s_first_data_block;   /* First block of the filesystem */
    u32 s_log_block_size;     /* Block size */
    u32 s_log_frag_size;      /* Fragment size */
    u32 s_blocks_per_group;   /* # blocks per group */
    u32 s_frags_per_group;    /* # fragments per group */
    u32 s_inodes_per_group;   /* # inodes per group */
    u32 s_mtime;              /* Last mount time */
    u32 s_wtime;              /* Last write time */
    u16 s_mnt_count;          /* Mount count */
    u16 s_max_mnt_count;      /* Maximum mount count */
    u16 s_magic;              /* Magic signature */
    u16 s_state;              /* File system state */
    u16 s_errors;             /* Behaviour when detecting errors */
    u16 s_minor_rev_level;    /* minor revision level */
    u32 s_lastcheck;          /* time of last check */
    u32 s_checkinterval;      /* max. time between checks */
    u32 s_creator_os;         /* OS */
    u32 s_csum_seed;          /* checksum seed */
    u16 s_reserved_pad;       /* Padding */
    char s_volume_name[16];   /* Volume name */
    char s_uuid[16];          /* Filesystem uuid */
    char s_journal_uuid[16];  /* Journal uuid */
    u32 s_journal_inum;       /* Inode number of journal superblock */
    u32 s_journal_dev;        /* Device number of journal */
    u32 s_last_mounted;       /* Directory where fs was last mounted */
    u32 s_algorithm_usage_bitmap; /* Bitmap of hash algorithm usage */
    u32 s_prealloc_blocks;    /* Preallocate this many blocks */
    u32 s_prealloc_dir_blocks;/* Preallocate blocks for directories */
    u16 s_reserved_gdt_blocks; /* Reserved blocks at start of GDT */
    u16 s_jnl_blocks;         /* Number of blocks in journal buffer */
    u32 s_blocks_count_hi;    /* Total blocks count */
    u32 s_r_blocks_count_hi;  /* Free blocks count */
    u32 s_mount_opts;         /* Mount options */
    u32 s_default_mount_opts; /* Default mount options */
    u32 s_first_meta_bg;      /* First metablock block group */
    u32 s_mkfs_time;          /* Time filesystem was created */
    u32 s_jnl_backup_type;    /* Backup type for journal */
    u32 s_desc_size;          /* Size of block group descriptor */
    u32 s_default_dir_hash;   /* Default directory hash function */
    u32 s_pad1[2];            /* Padding */
    u16 s_inode_size;         /* Size of inode structure */
    u16 s_blocksize;          /* Block size */
    u16 s fragmentation_unit; /* Fragmentation unit */
    u16 s_pad2[2];            /* Padding */
    u32 s_encryption_default; /* Encryption default key type */
    u32 s_pad3[2];            /* Padding */
    u32 s_feature_incompat;   /* Incompatible features */
    u32 s_feature_compat;     /* Compatible features */
    u32 s_feature_ro_compat;  /* Read-only compatible features */
    u32 s_feature_ro_incompat;/* Read-only incompatible features */
    u32 s_uuid_high;          /* High half of uuid */
    u32 s_pad4[17];           /* Padding */
};

3.3 索引节点

索引节点(Inode)用于存储文件的元数据,如文件大小、权限、时间戳等。

3.3.1 索引节点的结构

以 Ext4 文件系统为例,索引节点的结构如下:

struct ext4_inode {
    u16 i_mode;               /* File mode */
    u16 i_uid;                /* Low 16 bits of Owner Uid */
    u32 i_size_lo;            /* Low 32 bits of Size in bytes */
    u32 i_atime;              /* Access time */
    u32 i_ctime;              /* Inode change time */
    u32 i_mtime;              /* Inode modification time */
    u32 i_dtime;              /* Deletion Time */
    u32 i_gid;                /* Low 16 bits of Group Id */
    u16 i_links_count;        /* Links count */
    u16 i_blocks_lo;          /* Low 16 bits of Blocks count */
    u32 i_flags;              /* File flags */
    u32 i_osd1;               /* OS dependent 1 */
    union {
        u32 l_i_block[15];    /* Pointers to blocks */
        struct {
            u32 l_i_extra_isize;
            u32 l_i_pad2;
            u32 l_i_extra_blocks[13];
        } __attribute__((packed));
    } __attribute__((packed));
    u32 i_block[EXT4_N_BLOCKS]; /* Pointers to blocks */
    u32 i_generation;          /* File version (for NFS) */
    u32 i_file_acl_lo;         /* Low 32 bits of file acl */
    u32 i_size_high;           /* High 32 bits of Size in bytes */
    u32 i_obso_pad1;           /* (unused) */
    u32 i_file_acl_high;       /* High 32 bits of file acl */
    u32 i_dir_acl;             /* Directory acl */
    u32 i_faddr;               /* Fragment address */
    u16 i_frag_size;           /* Fragment size */
    u16 i_frag_number;         /* Fragment number */
    u16 i_pad2;                /* Padding */
    u16 i_uid_high;            /* High 16 bits of Owner Uid */
    u16 i_gid_high;            /* High 16 bits of Group Id */
    u32 i_osd2;                /* OS dependent 2 */
};

3.4 数据块

数据块用于存储文件的实际数据。每个文件的数据分布在一个或多个数据块中。

3.4.1 数据块的分配

文件系统在创建文件时会根据文件的大小分配相应数量的数据块。数据块的大小通常是固定的,例如 Ext4 中数据块大小通常是 4KB。

3.5 目录项

目录项用于存储文件名及其对应的索引节点号。每个目录也是一个文件,其中包含其他文件的目录项。

3.5.1 目录项的结构

以 Ext4 文件系统为例,目录项的结构如下:

struct ext4_dir_entry_2 {
    __le32 inode;             /* Inode number */
    __le16 rec_len;           /* Directory entry length */
    __le16 name_len;          /* Name length */
    char name[];              /* File name */
};

3.6 文件系统操作

文件系统的操作通常包括创建、删除、读写文件等。这些操作通过 VFS(Virtual File System)层来实现。

3.6.1 VFS 层

VFS 层提供了统一的接口来访问不同类型的文件系统。VFS 层定义了一系列操作,如 readwriteopen 等,这些操作通过 file_operations 结构体来实现。

struct file_operations {
    loff_t (*llseek) (struct file *, loff_t, int);
    int (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*readdir) (struct file *, void *, struct dir_context *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, int);
    int (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*direct_IO) (struct kiocb *, struct iovec *, unsigned long);
    int (*migrate_pages) (struct file *, struct migrant_huge_pages *);
    int (*poll) (struct file *, struct poll_table *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*flock) (struct file *, int, struct file_lock *);
    int (*migrate_pages_range) (struct file *, struct addr_space *, unsigned long, unsigned long);
    int (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long *);
    int (*check_bandwidth) (struct file *, int, unsigned long, unsigned long *);
    int (*fallocate) (struct file *, int, loff_t, loff_t);
};

4. 设备文件

4.1 设备文件的概念

设备文件是 Linux 系统中的一种特殊文件,它们代表了硬件设备或虚拟设备。设备文件通常位于 /dev 目录下,可以分为字符设备文件和块设备文件。

4.1.1 字符设备文件

字符设备文件用于与不支持随机访问的设备交互,如串口、声卡等。

4.1.2 块设备文件

块设备文件用于与支持随机访问的设备交互,如硬盘、SSD 等。

4.2 创建设备文件

设备文件可以通过 mknod 命令创建,也可以通过 udev 规则自动创建。

4.2.1 使用 mknod

创建一个字符设备文件:

sudo mknod /dev/my_char_dev c 240 0

创建一个块设备文件:

sudo mknod /dev/my_block_dev b 240 0
4.2.2 使用 udev 规则

可以编写 udev 规则来自动创建设备文件:

# /etc/udev/rules.d/99-my-device.rules

KERNEL=="my_device", MODE="0660", OWNER="root", GROUP="users", SYMLINK+="my_device"

4.3 设备文件的操作

设备文件可以通过用户空间程序来读写。下面是一个简单的示例,演示如何读写一个字符设备文件。

4.3.1 示例代码

假设我们有一个简单的字符设备驱动模块,该模块创建了一个设备文件 /dev/my_char_dev

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>

// 定义设备号
static dev_t dev_num = MKDEV(240, 0);
static struct cdev c_dev;
static struct class *class;
static struct device *device;
static char buf[PAGE_SIZE] = {0};

// 设备打开操作
static int dev_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device opened.\n");
    return 0;
}

// 设备关闭操作
static int dev_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device closed.\n");
    return 0;
}

// 设备读操作
static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    if (*ppos >= PAGE_SIZE)
        return 0;
    if (copy_to_user(buf, &buf[*ppos], count))
        return -EFAULT;
    *ppos += count;
    return count;
}

// 设备写操作
static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    if (*ppos >= PAGE_SIZE)
        return -ENOSPC;
    if (copy_from_user(&buf[*ppos], buf, count))
        return -EFAULT;
    *ppos += count;
    return count;
}

// 设备文件操作结构
static const struct file_operations fops = {
    .owner          = THIS_MODULE,
    .read           = dev_read,
    .write          = dev_write,
    .open           = dev_open,
    .release        = dev_release,
};

// 模块初始化函数
static int __init dev_init(void)
{
    // 注册字符设备
    register_chrdev_region(dev_num, 1, "my_char_dev");

    // 初始化字符设备结构
    cdev_init(&c_dev, &fops);

    // 添加字符设备到设备类
    class = class_create(THIS_MODULE, "my_char_class");
    device = device_create(class, NULL, dev_num, NULL, "my_char_dev");

    // 注册字符设备
    cdev_add(&c_dev, dev_num, 1);

    return 0;
}

// 模块退出函数
static void __exit dev_exit(void)
{
    // 删除字符设备
    cdev_del(&c_dev);

    // 移除设备
    device_destroy(class, dev_num);

    // 销毁设备类
    class_unregister(class);

    // 注销字符设备区域
    unregister_chrdev_region(dev_num, 1);
}

// 模块初始化函数声明
module_init(dev_init);

// 模块退出函数声明
module_exit(dev_exit);

// 指定模块的许可证
MODULE_LICENSE("GPL");
4.3.2 用户空间程序示例

编写一个简单的用户空间程序来读写设备文件:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int fd;
    char buffer[256];
    ssize_t bytes_written, bytes_read;

    // 打开设备文件
    fd = open("/dev/my_char_dev", O_RDWR);
    if (fd == -1) {
        perror("Failed to open device");
        return 1;
    }

    // 写入数据
    bytes_written = write(fd, "Hello, World!", 13);
    if (bytes_written == -1) {
        perror("Failed to write to device");
        close(fd);
        return 1;
    }

    // 读取数据
    bytes_read = read(fd, buffer, sizeof(buffer));
    if (bytes_read == -1) {
        perror("Failed to read from device");
        close(fd);
        return 1;
    }

    printf("Read from device: %.*s\n", (int)bytes_read, buffer);

    // 关闭设备文件
    close(fd);

    return 0;
}

4.4 设备文件的管理

设备文件通常由系统管理员管理,包括创建、删除和权限设置等。

4.4.1 权限设置

可以使用 chmod 命令更改设备文件的权限:

sudo chmod 660 /dev/my_char_dev
4.4.2 删除设备文件

可以使用 rm 命令删除设备文件:

sudo rm /dev/my_char_dev

5. 文件系统与设备文件的交互

文件系统与设备文件之间的交互通常涉及文件系统的挂载点和设备文件的关联。

5.1 文件系统的挂载点

每个文件系统都有一个挂载点,这个挂载点是一个目录,文件系统通过挂载点连接到文件层次结构中。

5.1.1 挂载点的选择

选择合适的挂载点很重要,它应该符合 FHS 标准并避免与其他挂载点冲突。

5.2 设备文件的关联

设备文件通常关联到一个具体的硬件设备,例如硬盘或 USB 设备。当一个设备被连接到系统时,系统会自动创建相应的设备文件。

5.2.1 动态创建设备文件

使用 udev 可以动态创建设备文件,例如当插入 USB 存储设备时,udev 会自动创建 /dev/sdb1 这样的设备文件。

6. 文件系统的高级特性

6.1 文件系统的配额管理

文件系统的配额管理允许限制用户或用户组对磁盘空间的使用。

6.1.1 设置用户配额

使用 quotatool 设置用户配额:

quotatool -u user_name -b soft 1G /mnt
6.1.2 设置组配额

使用 quotatool 设置组配额:

quotatool -g group_name -b soft 1G /mnt

6.2 文件系统的快照

文件系统的快照功能允许创建文件系统在某一时刻的状态副本。

6.2.1 创建快照

在 Btrfs 文件系统中创建快照:

btrfs subvolume snapshot /mnt /mnt/snapshot

6.3 文件系统的加密

文件系统加密保护数据的安全性,即使磁盘被盗也能防止数据泄露。

6.3.1 LUKS 加密

使用 cryptsetupLUKS 对文件系统进行加密:

cryptsetup luksFormat /dev/sda1
cryptsetup open /dev/sda1 my_crypt --type luks
mkfs.ext4 /dev/mapper/my_crypt
mount /dev/mapper/my_crypt /mnt

7. 文件系统的故障恢复

7.1 文件系统的检查和修复

文件系统可能会因为硬件故障或其他原因损坏,需要定期检查并修复。

7.1.1 使用 fsck 检查

使用 fsck 工具检查文件系统:

fsck -f /dev/sda1

7.2 日志文件系统的恢复

日志文件系统(如 Ext3、Ext4)在断电后可以通过日志记录自动恢复。

7.2.1 Ext4 日志记录

Ext4 文件系统使用日志记录来确保数据的一致性:

mount -o journal /dev/sda1 /mnt

8. 总结

Linux 文件系统和设备文件是操作系统中非常重要的组成部分。文件系统用于组织和管理数据,而设备文件用于与硬件设备交互。理解文件系统的工作原理和设备文件的管理方式,可以帮助系统管理员和开发人员更好地管理和优化 Linux 系统。希望本文能帮助读者更好地掌握 Linux 文件系统和设备文件的相关知识。