首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Linux打开一个文件并读取内容的详细流程【inode结构体、fd文件描述符、`struct file`结构体、一个打开普通文件和一个打开设备文件的详细流程分析】

  • 25-03-05 03:28
  • 4405
  • 5775
blog.csdn.net

目录

  • inode结构体
    • inode结构体概述
    • `struct inode` 的基本结构
    • 关键字段说明
    • inode 的核心作用
    • **小结**:
  • 文件描述符(fd)及与文件描述符直接关联的结构体`struct file`
    • 文件描述符(fd)
    • 结构体`struct file`
      • **`struct file` 的作用**
      • **`struct file` 和 `struct inode` 的区别**
      • **`struct file` 的关键字段**
        • 主要字段解释:
      • **打开文件的核心流程**
      • **`struct file` 在文件操作中的作用**
      • **小结**
  • 访问一个文件的内容,通常的流程
    • 流程概述
    • 一个打开并操作普通文件的例子
    • 一个打开驱动程序的设备文件的例子

inode结构体

inode结构体概述

Linux打开一个文件中的核心点是inode结构体,所以我们就从inode结构体开始讲这个问题。

inode应该来源于“index node”的缩写,所以发音为 [aɪ noʊd]

在Linux系统中,一个文件通常对应一个inode(索引节点),特殊情况下可能有多个文件名与一个inode对应,或者一个文件涉及多个inode,特殊情况我们不作考虑,在这里,我们只需要知道,在Linux系统中,一个文件通常对应一个inode。

inode(索引节点)是文件系统中的数据结构,存储文件的元信息(例如文件大小、权限、所有者、时间戳等),但不包含文件名。

每个文件在文件系统中都会分配一个唯一的inode。

具体来说,inode 在 Linux 内核中是一个结构体,定义在内核的源代码中,具体在头文件 include/linux/fs.h 中。它是文件系统的核心数据结构之一,用于表示文件的元数据。

struct inode 的基本结构

inode 结构体的定义包含许多字段,负责描述文件的各种属性和操作。例如:

struct inode {
    umode_t          i_mode;       /* 文件类型和权限 */
    unsigned short   i_opflags;    /* 操作相关标志 */
    kuid_t           i_uid;        /* 文件所有者的用户ID */
    kgid_t           i_gid;        /* 文件所有者的组ID */
    unsigned int     i_flags;      /* 文件标志 */

    const struct inode_operations *i_op; /* 文件操作方法 */
    struct super_block *i_sb;      /* 所属超级块 */
    struct address_space *i_mapping; /* 地址空间 */

    loff_t           i_size;       /* 文件大小 */
    struct timespec64 i_atime;     /* 最后访问时间 */
    struct timespec64 i_mtime;     /* 最后修改时间 */
    struct timespec64 i_ctime;     /* 状态更改时间 */

    unsigned int     i_nlink;      /* 硬链接计数 */
    ino_t            i_ino;        /* inode编号 */
    dev_t            i_rdev;       /* 主设备号和次设备号(如果是设备文件) */

	const struct file_operations *i_fop; /* 文件操作结构体,存储着诸如open,read,write等函数 */

    /* ...更多字段,用于缓存、锁、权限检查等... */
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

关键字段说明

  1. i_mode:

    • 保存文件的类型(如普通文件、目录、设备文件)和权限信息。
    • 通过掩码可以检查文件类型,例如 S_ISREG 检查是否是普通文件。
  2. i_uid 和 i_gid:

    • 文件的所有者用户ID和组ID。
  3. i_size:

    • 文件的大小(以字节为单位)。
  4. i_atime / i_mtime / i_ctime:

    • 分别表示最后访问时间、最后修改时间和元数据更改时间。
  5. i_nlink:

    • 硬链接计数,指向同一个inode的目录项数量。当计数降为0时,文件数据会被释放。
  6. i_op:

    • 指向一个 inode_operations 结构体,用于定义文件系统对该inode支持的操作(例如创建、删除)。
  7. i_mapping:

    • 表示文件的地址空间,通常用来管理文件数据的缓存。
  8. i_ino:

    • inode编号,文件系统中每个文件的唯一标识。
  9. i_rdev:

    • 里面存储着主设备号和次设备号信息。
  10. i_fop:

    • 文件操作结构体,它实际上就是链接着文件操作结构体,比如驱动程序中的文件操作结构体,里面定义着文件的底层打开函数、读函数、写函数等。

inode 的核心作用

  1. 存储元信息:

    • 保存文件的基本属性,比如权限、大小、时间戳等。
  2. 连接文件数据:

    • inode 不直接保存文件数据,而是通过其他结构体(如块地址映射表)指向数据块。
  3. 支持文件操作:

    • 内核通过 struct inode 来调度文件系统操作。

小结:

inode 是 Linux 文件系统中用于描述文件的核心结构体,它包含了文件的所有元信息以及与文件数据的关联。虽然用户空间程序看不到 inode 结构体的细节,但文件系统操作(如open、read)背后都依赖于它。

当我们获得了文件的inode后,就可以借由inode来进一步获取文件中的信息。

文件描述符(fd)及与文件描述符直接关联的结构体struct file

文件描述符(fd)

如果我们要访问一个文件的内容,通常的流程是这样的:
文件系统首先通过文件路径和文件名在目录中查找对应的inode,并根据inode中的元信息定位文件的存储位置。
接着,文件系统打开文件并返回一个文件描述符(通常用fd表示),文件描述符是进程内的一个整数标识符,在这个进程中可以用来进一步访问文件内容,而不用每次都再去打开文件。

请注意文件描述符只是进程内的一个整数标识,它对应的文件信息其实是存储在结构体struct file中的,所以我们需要详细了解下结构体struct file。

结构体struct file

在 Linux 内核中,打开文件时会涉及到另一个重要的结构体 struct file,它与 struct inode 一起协同工作,负责文件的实际操作和管理。


struct file 的作用

  • struct file 表示一个已打开的文件,即文件打开后的状态。
  • 它与用户空间的文件描述符(file descriptor)直接关联。
  • 每次调用 open 系统调用时,内核会为该文件创建一个新的 struct file 实例,用来管理这个打开的文件。

struct file 和 struct inode 的区别

struct inodestruct file
表示文件在文件系统中的元信息和属性表示一个已打开的文件实例(与进程相关)
一个文件在文件系统中只有一个 inode每次打开文件会创建一个新的 struct file
包含文件权限、大小、时间戳等信息包含文件的当前偏移量、模式、操作等信息
共享于多个文件描述符(通过硬链接等)文件描述符对应唯一的 struct file 实例

struct file 的关键字段

struct file 定义在内核头文件 include/linux/fs.h 中,主要字段包括:

struct file {
    const struct file_operations *f_op;   /* 文件操作方法 */
    struct inode        *f_inode;        /* 指向对应的 inode */
    void                *private_data;   /* 私有数据(通常是驱动使用) */
    loff_t              f_pos;           /* 文件的当前偏移量 */
    unsigned int        f_flags;         /* 打开文件的标志(如读写模式) */
    struct path         f_path;          /* 文件路径信息 */
    atomic_long_t       f_count;         /* 引用计数 */
    /* 其他字段省略 */
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
主要字段解释:
  1. f_op:

    • 指向 struct file_operations,定义了文件支持的操作(如 read、write、ioctl 等)。
    • 不同的文件类型(如普通文件、设备文件)会提供不同的操作实现。
  2. f_inode:

    • 指向文件对应的 struct inode,通过它可以访问文件的元信息。
  3. f_pos:

    • 文件的当前读写位置(偏移量)。
  4. f_flags:

    • 打开文件时的标志(如 O_RDONLY, O_WRONLY, O_RDWR)。
  5. private_data:

    • 文件的私有数据,常用于驱动程序中存储设备相关信息。
  6. f_path:

    • 表示文件的路径信息(包括文件的 dentry 和 vfsmount)。

打开文件的核心流程

当调用 open 系统调用时,内核的处理流程如下:

  1. 路径解析:

    • 根据文件路径解析到 inode,找到对应的 struct inode。
  2. 创建 struct file:

    • 为每个打开的文件分配一个新的 struct file,并初始化其字段。
    • struct file 的 f_inode 字段会指向文件的 inode。
  3. 文件描述符与 struct file 的关联:

    • 将 struct file 注册到当前进程的文件表中,并分配一个文件描述符(整数值)。
    • 文件描述符用于用户空间访问,而 struct file 是内核的实际表示。

struct file 在文件操作中的作用

struct file 主要用于管理打开文件的状态和操作。例如:

  • 读写操作:
    • 当调用 read(fd) 或 write(fd) 时,内核会根据文件描述符找到对应的 struct file,并调用其 f_op 字段中定义的操作函数。
  • 偏移量管理:
    • f_pos 保存了文件的当前偏移量,支持顺序读写操作。

小结

在 Linux 内核中:

  • struct inode 是文件的静态表示,存储文件的元信息。
  • struct file 是打开文件的动态表示,描述了文件打开后的状态(如偏移量、模式等)。
  • 两者协作完成文件系统的读写和管理操作。每次打开文件,都会生成一个新的 struct file,但它们可能共享同一个 struct inode(例如通过硬链接)。

访问一个文件的内容,通常的流程

流程概述

当调用系统函数 open 打开一个文件时,内核的处理流程如下:

  1. 路径解析:

    • 根据文件路径解析到 inode,找到对应的 struct inode。
  2. 创建 struct file:

    • 为每个打开的文件分配一个新的 struct file,并初始化其字段。
    • struct file 的 f_inode 字段会指向文件的 inode。
  3. 文件描述符与 struct file 的关联:

    • 将 struct file 注册到当前进程的文件表中,并分配一个文件描述符(整数值)。
    • 文件描述符用于用户空间访问,而 struct file 是内核的实际表示。

调用系统函数 open 打开一个文件后,最终返回了文件描述符,文件描述符是进程内的一个整数标识符,它与一个结构体 struct file 相关联,里面存储着文件及对文件操作的各种上下文信息,从而,在这个进程后续的操作中,可以用文件描述符内进一步访问文件内容或向文件写内容。

一个打开并操作普通文件的例子

一个操作普通文件的例子如下:

#include 
#include 
#include 

int main() {
    int fd = open("/home/user/file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    char buffer[1024];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
    if (bytesRead > 0) {
        buffer[bytesRead] = '\0';
        printf("File content: %s\n", buffer);
    } else {
        perror("read");
    }

    close(fd);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在上面的例子中,通过文件路径/home/user/file.txt找到文件file.txt对应的inode,找到文件file.txt的inode后,我们就知道了文件file.txt的存储位置,如果inode中的信息显示这个文件有可以打开权限,那么系统的open()函数便能打开它,并且返回属于这个进程的文件描述fd,相关代码如下:

    int fd = open("/home/user/file.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
  • 1
  • 2
  • 3
  • 4
  • 5

借由这个文件描述符,那么在该进程内我们需要再次操作这个文件时,便不需要再去打开操作了。
比如上面的示例中,代码:

 ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
  • 1

便是进程内借由文件描述符fd去读取文件内容。之所以要少读一个,原因见 http://iyenn.com/rec/1709343.html

写文件的流程这里就再叙述了,理解了inode和文件描述符的概念后,再配合一个读的示例,这个问题基本就搞清了。

一个打开驱动程序的设备文件的例子

假设驱动程序的文件操作结构体file_operations如下定义:

static struct file_operations hello_drv_fops = {
    .owner = THIS_MODULE,
    .open = hello_drv_open,
    .release = hello_drv_close,
};
  • 1
  • 2
  • 3
  • 4
  • 5

设备底层打开函数hello_drv_open的代码如下:

static int hello_drv_open(struct inode *node, struct file *file)
{
    dev_t dev = node->i_rdev;
    int major = MAJOR(dev);
    int minor = MINOR(dev);

    printk("Opening device: major=%d, minor=%d\n", major, minor);

    // 为每次打开操作分配私有数据
    file->private_data = kmalloc(sizeof(int), GFP_KERNEL);
    if (!file->private_data) {
        printk("Failed to allocate memory for private data\n");
        return -ENOMEM;
    }
    *(int *)file->private_data = 42; // 示例数据

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这个函数的两个参数便使用到了结构体inode和结构体file,
当系统函数open尝度打开设备文件/dev/hello时,即下面的代码:

    // 打开设备文件
    fd = open("/dev/hello", O_RDWR);
    if (fd < 0) {
        perror("Failed to open device");
        return errno;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

系统会根据设备文件路径/dev/hello找到文件的inode实例,然后准备利用inode结构体打开文件。
打开文件前首先会为准备找开的文件分配一个 struct file实例,这个结构体在上文中已经讲清楚了,这里不再多述。
然后正式开始打开文件,打开文件时找到inode结构体的成员i_fop,其定义如下:

const struct file_operations *i_fop; /* 文件操作结构体,存储着诸如open,read,write等函数 */
  • 1

它实际上指向着我们在驱动程序中定义的设备结构体,代码如下:

static struct file_operations hello_drv_fops = {
    .owner = THIS_MODULE,
    .open = hello_drv_open,
    .release = hello_drv_close,
};
  • 1
  • 2
  • 3
  • 4
  • 5

这个结构体成员中便存储着设备底层的打开函数,然后通过它获得设备的底层打开函数,在这里就是函数hello_drv_open(),并且把系统自动生成的结构体struct inode 和结构体 struct file的实例作为参数传递给函数hello_drv_open(),函数hello_drv_open()最终利用这两个参数完成设备文件的打开操作,当然,函数hello_drv_open()的函数其实并不一定非得用到这两个参数,不用也可以,只是系统的open()函数会把这两个参数传递给设备结构体hello_drv_fops 中的打开函数hello_drv_open()。在我们这里给出的例子中,用到了这两个参数,我们利用参数struct inode *node打印出了设备号,利用参数struct file *file记录了一些私有数据,这些数据可以根据我们的需要用作不同的用途。

如果一切正常,系统的open()函数会为此次设备文件的打开分配一个文件描述符(fd),并将文件描述符(fd)的值记录到结构体 struct file的实例中。在进程的后续操作中,便可利用这个文件文件描述符(fd)对文件进行一系列操作。

以上便是一个打开驱动程序的设备文件的例子的详细流程。

昊虹嵌入式技术交流群
QQ群名片
注:本文转载自blog.csdn.net的昊虹AI笔记的文章"https://blog.csdn.net/wenhao_ir/article/details/144931005"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top