找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

549

积分

0

好友

69

主题
发表于 3 天前 | 查看: 12| 回复: 0

技术文章装饰示意图

一、文件和目录

在日常开发或系统管理中,我们频繁地与文件和目录打交道。在Windows这类图形界面系统中,目录常以“文件夹”的直观形式展现,易于理解。而在工程领域,我们则更习惯称其为“目录”。

从直观感受上看,目录像一棵树,而文件是树上的叶子。虽然这个比喻在存储层面并不完全精确,但它为理解文件系统的基本架构提供了一个很好的起点。本文将深入探讨文件与目录在底层设计上的本质与区别。

二、本质:一切都是文件

你是否思考过,目录和文件在本质上有什么区别?在遵循“一切皆文件”理念的Linux系统中,它们的底层基础其实是相同的——都由inode(索引节点)和数据块构成。

当我们操作一个文件时,系统最终操作的对象就是这个文件的inode。inode之于文件,就如同身份证之于个人,它是文件在系统中的唯一标识。每当创建一个新文件,就会在磁盘的特定区域生成一个inode节点(正常情况下与文件一一对应)。每个inode拥有唯一的ID号,用于系统查找和操作。

那么,inode里究竟存储了哪些信息呢?它主要包含两大部分:文件的元数据和指向实际数据块的指针。

1. 元数据

inode中存储的元数据是文件的“身份信息”,主要包括:

  • 文件编号:文件的唯一标识符。
  • 文件类型:例如普通文件、目录、符号链接或设备文件等。
  • 文件权限和所有者:包括所属用户、组以及读写执行权限。
  • 时间戳:文件的创建时间、最后修改时间和最后访问时间。
  • 文件大小:以字节为单位的文件数据总量。
  • 链接数:指向该inode的硬链接数量。
  • 其它属性:如扩展属性等。

如果你对内核实现感兴趣,可以参考以下inode结构体的定义:

struct inode {
    umode_t         i_mode;
    unsigned short      i_opflags;
    kuid_t          i_uid;
    kgid_t          i_gid;
    unsigned int        i_flags;

#ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl    *i_acl;
    struct posix_acl    *i_default_acl;
#endif

    const struct inode_operations   *i_op;
    struct super_block  *i_sb;
    struct address_space    *i_mapping;

#ifdef CONFIG_SECURITY
    void            *i_security;
#endif

    /* Stat data, not accessed from path walking */
    unsigned long       i_ino;
    /*
     * Filesystems may only read i_nlink directly.  They shall use the
     * following functions for modification:
     *
     *    (set|clear|inc|drop)_nlink
     *    inode_(inc|dec)_link_count
     */
    union {
        const unsigned int i_nlink;
        unsigned int __i_nlink;
    };
    dev_t           i_rdev;
    loff_t          i_size;
    struct timespec64   __i_atime;
    struct timespec64   __i_mtime;
    struct timespec64   __i_ctime; /* use inode_*_ctime accessors! */
    spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */
    unsigned short          i_bytes;
    u8          i_blkbits;
    enum rw_hint        i_write_hint;
    blkcnt_t        i_blocks;

#ifdef __NEED_I_SIZE_ORDERED
    seqcount_t      i_size_seqcount;
#endif

    /* Misc */
    unsigned long       i_state;
    struct rw_semaphore i_rwsem;

    unsigned long       dirtied_when;   /* jiffies of first dirtying */
    unsigned long       dirtied_time_when;

    struct hlist_node   i_hash;
    struct list_head    i_io_list;  /* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
    struct bdi_writeback    *i_wb;      /* the associated cgroup wb */

    /* foreign inode detection, see wbc_detach_inode() */
    int         i_wb_frn_winner;
    u16         i_wb_frn_avg_time;
    u16         i_wb_frn_history;
#endif
    struct list_head    i_lru;      /* inode LRU list */
    struct list_head    i_sb_list;
    struct list_head    i_wb_list;  /* backing dev writeback list */
    union {
        struct hlist_head   i_dentry;
        struct rcu_head     i_rcu;
    };
    atomic64_t      i_version;
    atomic64_t      i_sequence; /* see futex */
    atomic_t        i_count;
    atomic_t        i_dio_count;
    atomic_t        i_writecount;
#if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING)
    atomic_t        i_readcount; /* struct files open RO */
#endif
    union {
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
        void (*free_inode)(struct inode *);
    };
    struct file_lock_context    *i_flctx;
    struct address_space    i_data;
    struct list_head    i_devices;
    union {
        struct pipe_inode_info  *i_pipe;
        struct cdev     *i_cdev;
        char            *i_link;
        unsigned        i_dir_seq;
    };

    __u32           i_generation;

#ifdef CONFIG_FSNOTIFY
    __u32           i_fsnotify_mask; /* all events this inode cares about */
    struct fsnotify_mark_connector __rcu    *i_fsnotify_marks;
#endif

#ifdef CONFIG_FS_ENCRYPTION
    struct fscrypt_inode_info   *i_crypt_info;
#endif

#ifdef CONFIG_FS_VERITY
    struct fsverity_info    *i_verity_info;
#endif

    void            *i_private; /* fs or device private pointer */
} __randomize_layout;

以下是Ext4文件系统中目录项的结构定义,展示了文件名如何与inode关联:

struct ext4_dir_entry_2 {
    __le32  inode;          /* Inode number */
    __le16  rec_len;        /* Directory entry length */
    __u8    name_len;       /* Name length */
    __u8    file_type;      /* See file type macros EXT4_FT_* below */
    char    name[EXT4_NAME_LEN];    /* File name */
};

2. 数据块的指针

指针是inode找到其“身体”(数据)的关键,主要分为三种类型:

  • 直接指针:直接指向存储文件真实数据的数据块。通常一个inode会包含12个直接指针。
  • 间接指针:可理解为二级指针,指向一个数据块,而这个数据块里存储着更多指向其他数据块的指针。这种设计主要用于支持大文件。
  • 扩展指针:某些现代文件系统(如Ext4)使用扩展树来优化大文件存储,以减少间接指针的层级,提升访问效率。

这里有一个关键点容易被忽略:文件名并不存储在inode中。文件名实际上记录在其所属目录的数据块里。目录的数据块包含一系列目录项,每个目录项就是一个“文件名-inode编号”的映射表。

这就像图书馆的索引系统:你不会去翻遍每一个书架找书,而是先查目录卡片(文件名),卡片会告诉你书在哪个具体位置(inode编号),然后你就能快速找到它(数据块)。

另外需要了解的是,inode的数量是有限的,它决定了磁盘上能创建的文件/目录总数。例如,在Ext4文件系统中,默认每4KB空间分配一个inode(可通过mkfs.ext4 -i调整)。这意味着,如果系统存在海量小文件(如日志、缩略图),可能会先耗尽inode数量,即使磁盘空间还有剩余,也无法创建新文件。在Linux中,可以使用 ls -alstatdf -i 等命令查看inode和文件详情。

三、区别

尽管目录和文件本质相同,但在具体实现和应用上存在显著差异:

  1. inode类型不同
    这是最根本的区别。通过stat命令查看时,普通文件的类型为S_IFREG,目录则为S_IFDIR。在ls -l的输出中,行首的第一个字符‘-’代表普通文件,而‘d’则代表目录。

  2. 数据内容不同
    普通文件的数据块存储的是文件的实际内容(文本、二进制数据等)。而目录的数据块存储的是目录项列表,即前面提到的文件名到inode的映射表。

  3. 操作行为不同
    由于其设计目的不同,允许的操作也不同。例如,文件可以使用readwrite函数进行读写,而目录则不能。目录有专门的操作函数,如opendirreaddirrmdir等。

  4. 链接处理不同
    一般情况下,操作系统不允许为目录创建硬链接(除了固有的...),但文件可以创建硬链接。

简单总结:文件由inode和数据块组成,inode存元数据和数据块指针,数据块存真实内容;目录也由inode和数据块组成,但其数据块存的是目录项(文件名-inode映射表)。

四、相关例程

以下是从Linux内核中摘录的,关于操作inode创建文件和特殊设备节点的简化代码,有助于理解上述原理在代码层面是如何实现的:

// 创建普通文件:涉及inode创建、更新目录项(建立映射)以及绑定操作函数
static int ext4_create(struct mnt_idmap *idmap, struct inode *dir,
               struct dentry *dentry, umode_t mode, bool excl)
{
    handle_t *handle;
    struct inode *inode;
    int err, credits, retries = 0;

    err = dquot_initialize(dir);
    if (err)
        return err;

    credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
           EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
retry:
    inode = ext4_new_inode_start_handle(idmap, dir, mode, &dentry->d_name,
                        0, NULL, EXT4_HT_DIR, credits);
    handle = ext4_journal_current_handle();
    err = PTR_ERR(inode);
    if (!IS_ERR(inode)) {
        inode->i_op = &ext4_file_inode_operations;
        inode->i_fop = &ext4_file_operations;
        ext4_set_aops(inode);
        err = ext4_add_nondir(handle, dentry, &inode);
        if (!err)
            ext4_fc_track_create(handle, dentry);
    }
    if (handle)
        ext4_journal_stop(handle);
    if (!IS_ERR_OR_NULL(inode))
        iput(inode);
    if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
        goto retry;
    return err;
}

// 用于创建特殊文件节点,如字符设备、块设备、FIFO和Socket文件
static int ext4_mknod(struct mnt_idmap *idmap, struct inode *dir,
              struct dentry *dentry, umode_t mode, dev_t rdev)
{
    handle_t *handle;
    struct inode *inode;
    int err, credits, retries = 0;

    err = dquot_initialize(dir);
    if (err)
        return err;

    credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
           EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
retry:
    inode = ext4_new_inode_start_handle(idmap, dir, mode, &dentry->d_name,
                        0, NULL, EXT4_HT_DIR, credits);
    handle = ext4_journal_current_handle();
    err = PTR_ERR(inode);
    if (!IS_ERR(inode)) {
        init_special_inode(inode, inode->i_mode, rdev);
        inode->i_op = &ext4_special_inode_operations;
        err = ext4_add_nondir(handle, dentry, &inode);
        if (!err)
            ext4_fc_track_create(handle, dentry);
    }
    if (handle)
        ext4_journal_stop(handle);
    if (!IS_ERR_OR_NULL(inode))
        iput(inode);
    if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
        goto retry;
    return err;
}

以上代码仅作原理示例,无需深入分析每一行。

五、总结

一个文件系统,其核心可以简化为三个部分:目录、inode和数据块。然而,看似简单的设计目标——“既要简单易用,又要快速高效,还要适配不断发展的硬件”——使得其底层实现变得极其复杂。这也正是文件系统种类繁多且持续演进的重要原因。深入理解inode和目录的基本原理,是掌握操作系统和进行系统级编程的坚实基础。




上一篇:向量归一化如何影响RAG系统检索精度:从踩坑到解决方案
下一篇:中国移动苏州研被曝暴力裁员?网传7大手段引争议
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 01:37 , Processed in 0.396889 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表