深入探究Linux文件系统I/O

一、基础概念

什么是文件系统?

文件系统,即 File System,是指计算机使用的一类存储媒介的物理组织和逻辑管理方式,它维护了对存储媒介的访问和管理。

那么什么是 Linux 文件系统?

Linux 文件系统是在 Linux 操作系统上,对存储设备进行的一种逻辑组织形式。Linux 文件系统一般由三部分构成:超级块、索引节点(inode)、数据块。其中超级块记录着文件系统的元信息;索引节点记录着文件和目录的元数据;数据块储存着文件和目录的实际内容。

Linux 文件系统中的 I/O 是什么?

I/O 即 Input/Output,也就是 Linux 文件系统中的读写操作。读操作是指从存储设备中读取数据到内存中,而写操作则是将内存中的数据写入到存储设备中。I/O 操作是 Linux 文件系统中最基本的操作方式。

二、I/O 操作方式

Linux 文件系统中的 I/O 操作可以分为同步和异步两种方式。

1. 同步 I/O

同步 I/O 是指应用程序在执行 I/O 操作时,必须等待 I/O 操作结束后才能继续执行后续的操作。同步 I/O 的特点是操作可靠,但一旦发生阻塞,就会严重影响应用程序的性能。

#include 
#include 
#include 
#include 

int main(void)
{
    int fd;
    char buf[1024];

    fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        printf("File open error!\n");
        return -1;
    }

    read(fd, buf, 1024);

    printf("%s", buf);

    close(fd);
    return 0;
}

2. 异步 I/O

异步 I/O 是指应用程序在执行 I/O 操作时,无须等待 I/O 操作结束,可以先执行后续的操作,待 I/O 操作完成后再执行回调函数。异步 I/O 的特点是操作效率高,但开发难度较大。

#include 
#include 
#include 
#include 
#include 

void handle_read(int signum)
{
    printf("Asynchronous I/O operation completed!\n");
}

int main(void)
{
    int fd;
    char buf[1024];
    struct sigaction sa;

    fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        printf("File open error!\n");
        return -1;
    }

    sa.sa_flags = 0;
    sa.sa_handler = handle_read;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGIO, &sa, NULL) == -1) {
        printf("Signal handler registration error!\n");
        return -1;
    }

    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, O_ASYNC);

    while (1) {
        sleep(1);
    }

    close(fd);
    return 0;
}

三、I/O 多路复用

I/O 多路复用是指让一个或多个进程能够同时监听多个文件描述符的可读可写状态,从而实现多路 I/O 服务。在 Linux 文件系统中,I/O 多路复用主要有 select、poll 和 epoll 三种机制。

1. select

select 是最早实现 I/O 多路复用的方法之一。通过 select 实现 I/O 多路复用需要将待监控的文件描述符加入到 fd_set 集合中,并设置超时时间。select 函数将会阻塞进程,直到有文件描述符发生变化或超时。select 函数会返回发生变化的文件描述符的个数。

#include 
#include 
#include 
#include 
#include 

int main()
{
    fd_set rfds;
    struct timeval tv;
    int retval;

    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    tv.tv_sec = 5;
    tv.tv_usec = 0;

    retval = select(1, &rfds, NULL, NULL, &tv);
    if (retval == -1) {
        printf("select error!\n");
        exit(EXIT_FAILURE);
    } else if (retval) {
        printf("Data is available now.\n");
    } else {
        printf("No data within five seconds.\n");
    }

    return 0;
}

2. poll

poll 是比 select 更加高效的 I/O 多路复用方法之一。poll 的实现方式与 select 类似,但其使用链表来存储被监控的文件描述符集,解决了 select 方法中文件描述符数量的限制问题。

#include 
#include 
#include 
#include 

int main()
{
    struct pollfd fds[1];
    int retval;

    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;

    retval = poll(fds, 1, 5000);
    if (retval == -1) {
        printf("poll error!\n");
        exit(EXIT_FAILURE);
    } else if (retval) {
        printf("Data is available now.\n");
    } else {
        printf("No data within five seconds.\n");
    }

    return 0;
}

3. epoll

epoll 是 Linux 的高级 I/O 多路复用方法。epoll 通过 epoll_create 函数创建 epoll 实例,然后使用 epoll_ctl 函数来向 epoll 中添加、删除、修改被监控的文件描述符。epoll_wait 函数则是阻塞等待文件描述符状态变化的发生,并返回变化的文件描述符集。

#include 
#include 
#include 
#include 

#define MAX_EVENTS 10

int main(int argc, char *argv[])
{
    int sockfd;
    struct epoll_event event;
    struct epoll_event event_array[MAX_EVENTS];
    int epoll_fd, nfds, i;

    sockfd = create_and_bind("8080");
    if (sockfd == -1) exit(EXIT_FAILURE);

    if (make_socket_non_blocking(sockfd) == -1) exit(EXIT_FAILURE);

    if (listen(sockfd, SOMAXCONN) == -1) exit(EXIT_FAILURE);

    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) exit(EXIT_FAILURE);

    event.data.fd = sockfd;
    event.events = EPOLLIN | EPOLLET;

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event) == -1) exit(EXIT_FAILURE);

    while (1) {
        nfds = epoll_wait(epoll_fd, event_array, MAX_EVENTS, -1);
        for (i = 0; i < nfds; i++) {
            if (event_array[i].events & EPOLLIN) {
                if (event_array[i].data.fd == sockfd) {
                    while (1) {
                        int conn_sock = accept(sockfd, NULL, NULL);
                        if (conn_sock == -1) {
                            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                break;
                            } else {
                                perror("accept");
                                break;
                            }
                        }

                        make_socket_non_blocking(conn_sock);

                        event.data.fd = conn_sock;
                        event.events = EPOLLIN | EPOLLET;

                        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) exit(EXIT_FAILURE);
                    }
                } else {
                    do_use_fd(event_array[i].data.fd);
                }
            }
        }
    }

    close(sockfd);
    return 0;
}

四、文件映射技术

文件映射(Memory Mapping)是指操作系统将文件的一段内容直接映射到进程的地址空间,使得进程可以直接访问文件内容,从而加快了文件 I/O 的速度。在 Linux 文件系统中,文件映射可以使用 mmap 函数来实现。

#include 
#include         
#include           
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int fd;
    char *mapped;
    struct stat file_stat;

    fd = open(argv[1], O_RDONLY);
    if (fd == -1) exit(EXIT_FAILURE);

    if (fstat(fd, &file_stat) == -1) exit(EXIT_FAILURE);

    mapped = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped == MAP_FAILED) exit(EXIT_FAILURE);

    printf("%s", mapped);

    if (munmap(mapped, file_stat.st_size) == -1) exit(EXIT_FAILURE);

    close(fd);
    return 0;
}

原创文章,作者:FABYO,如若转载,请注明出处:https://www.506064.com/n/370137.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
FABYOFABYO
上一篇 2025-04-18 13:40
下一篇 2025-04-18 13:40

相关推荐

  • 分布式文件系统数据分布算法

    数据分布算法是分布式文件系统中的重要技术之一,它能够实现将文件分散存储于各个节点上,提高系统的可靠性和性能。在这篇文章中,我们将从多个方面对分布式文件系统数据分布算法进行详细的阐述…

    编程 2025-04-27
  • 如何在Linux中添加用户并修改配置文件

    本文将从多个方面详细介绍在Linux系统下如何添加新用户并修改配置文件 一、添加新用户 在Linux系统下创建新用户非常简单,只需使用adduser命令即可。使用以下命令添加新用户…

    编程 2025-04-27
  • 如何解决linux jar包 invalid or corrupt jarfile问题

    对于许多开发人员和系统管理员在Linux环境下使用Java开发过程中遇到的一个常见的问题是 invalid or corrupt jarfile(无效或损坏的jar文件)错误。当您…

    编程 2025-04-27
  • 在Linux上安装JRE并配置环境变量

    本文将从以下几个方面为您详细阐述如何在Linux系统上,通过自己账户安装JRE,并且配置环境变量。 一、安装JRE 在进行安装前,我们需要下载JRE的安装包并解压,可以从官方网站下…

    编程 2025-04-27
  • GTKAM:Linux下的照片管理器

    GTKAM是用于Linux操作系统的一款照片管理器,它支持多种相机及存储设备,并提供了一系列强大的工具,让用户可以方便地浏览、管理、编辑和导出照片。本文将从多个方面对GTKAM进行…

    编程 2025-04-27
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • Linux修改文件名命令详解

    在Linux系统中,修改文件名是一个很常见的操作。Linux提供了多种方式来修改文件名,这篇文章将介绍Linux修改文件名的详细操作。 一、mv命令 mv命令是Linux下的常用命…

    编程 2025-04-25
  • 深入解析Vue3 defineExpose

    Vue 3在开发过程中引入了新的API `defineExpose`。在以前的版本中,我们经常使用 `$attrs` 和` $listeners` 实现父组件与子组件之间的通信,但…

    编程 2025-04-25
  • 深入理解byte转int

    一、字节与比特 在讨论byte转int之前,我们需要了解字节和比特的概念。字节是计算机存储单位的一种,通常表示8个比特(bit),即1字节=8比特。比特是计算机中最小的数据单位,是…

    编程 2025-04-25
  • 深入理解Flutter StreamBuilder

    一、什么是Flutter StreamBuilder? Flutter StreamBuilder是Flutter框架中的一个内置小部件,它可以监测数据流(Stream)中数据的变…

    编程 2025-04-25

发表回复

登录后才能评论