【linux】NIO中的FileChannel与mmap

news2024/9/30 21:35:44

FileChannel是Java NIO库中的一个类,用于对文件进行读写操作。它提供了一种高效的方式来读取、写入和操作文件。

使用FileChannel,你可以执行以下操作:

  1. 从文件读取数据到缓冲区(Buffer):你可以使用FileChannel的read()方法将数据从文件读取到缓冲区中。
  2. 将数据从缓冲区写入到文件:你可以使用FileChannel的write()方法将数据从缓冲区写入到文件中。
  3. 文件位置操作:你可以使用FileChannel的position()方法获取或设置文件的当前位置。
  4. 文件截取操作:你可以使用FileChannel的truncate()方法截取文件的大小。
  5. 强制数据同步到磁盘:你可以使用FileChannel的force()方法将数据强制刷新到磁盘上。

文件的顺序读写

要使用FileChannel,首先需要通过FileInputStream或FileOutputStream获取一个FileChannel实例,然后可以使用该实例进行文件的读写操作。

下面是一个简单的示例代码,展示了如何使用FileChannel读取文件内容并写入到另一个文件中:

package com.morris.io;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;

/**
 * FileChannel实现随机读取文件
 */
public class RandomAccessDemo {
    public static void main(String[] args) throws IOException {
        // 创建文件对象和FileChannel对象
        File file = new File("RandomAccessDemo.txt");
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();

        // 设置文件的当前位置
        long readPosition = 10;
        fileChannel.position(readPosition);

        // 创建缓冲区
        ByteBuffer readBuffer = ByteBuffer.allocate(5);
        // 从当前位置读取数据到缓冲区
        int bytesRead = fileChannel.read(readBuffer);
        System.out.println(new String(readBuffer.array()));

        long writePosition = 20; // 这个位置文件中没数据
        fileChannel.position(writePosition);
        // 创建缓冲区,一个char两个字节
        ByteBuffer writeBuffer = ByteBuffer.allocate(6);
        writeBuffer.put("xyz".getBytes(StandardCharsets.UTF_8));
        writeBuffer.flip();
        fileChannel.write(writeBuffer);

        // 关闭通道和文件
        fileChannel.close();
        randomAccessFile.close();
    }
}

产生的系统调用如下:

openat(AT_FDCWD, "input.txt", O_RDONLY) = 4
fstat(4, {st_mode=S_IFREG|0777, st_size=13, ...}) = 0
openat(AT_FDCWD, "output.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7
fstat(7, {st_mode=S_IFREG|0777, st_size=0, ...}) = 0
read(4, "abc\nooxx\nefg\n", 1024)       = 13
write(7, "abc\nooxx\nefg\n", 13)        = 13
read(4, "", 1024)                       = 0
close(4)
close(7)

文件的随机读写

FileChannel提供了随机读写文件的功能,可以通过position()方法来设置文件的当前位置,然后使用read()方法从该位置开始读取数据,使用write()方法从该位置开始写入数据。

下面是一个示例代码,展示了如何使用FileChannel进行随机读写文件的操作:

package com.morris.io;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;

/**
 * FileChannel实现随机读取文件
 */
public class RandomAccessDemo {
    public static void main(String[] args) throws IOException {
        // 创建文件对象和FileChannel对象
        File file = new File("RandomAccessDemo.txt");
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();

        // 设置文件的当前位置
        long readPosition = 10;
        fileChannel.position(readPosition);

        // 创建缓冲区
        ByteBuffer readBuffer = ByteBuffer.allocate(5);
        // 从当前位置读取数据到缓冲区
        int bytesRead = fileChannel.read(readBuffer);
        System.out.println(new String(readBuffer.array()));

        long writePosition = 20;
        fileChannel.position(writePosition);
        // 创建缓冲区,一个char两个字节
        ByteBuffer writeBuffer = ByteBuffer.allocate(6);
        writeBuffer.put("xyz".getBytes(StandardCharsets.UTF_8));
        writeBuffer.flip();
        fileChannel.write(writeBuffer);

        // 关闭通道和文件
        fileChannel.close();
        randomAccessFile.close();
    }
}

产生的系统调用如下:

openat(AT_FDCWD, "RandomAccessFileDemo.txt", O_RDWR|O_CREAT, 0666) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0
lseek(4, 10, SEEK_SET)                  = 10
read(4, "j\nklm", 5)                    = 5
write(1, "j\nklm\n", 6)                 = 6
lseek(4, 20, SEEK_SET)                  = 20
write(4, "xyz", 3)                      = 3
close(4)

系统调用使用lseek来移动定位position在文件中的位置。

如果访问文件的position大于文件的长度会怎么样?
程序执行前的文件内容:

abcde
fghij
klmno

程序执行后的文件内容:

abcde
fghij
klmno
^@^@xyz

可以看到访问文件的position大于文件的长度后,中间会用空来填充。

内存映射文件mmap

FileChannel还提供了内存映射文件的功能,通过使用map()方法,可以将文件映射到内存中的一个ByteBuffer对象,从而实现对文件的高效读写操作。

在这里插入图片描述

下面是一个示例代码,展示了如何使用内存映射文件的方式读取和写入文件:

package com.morris.io;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * mmap,将文件映射到内存中
 */
public class MemoryMappedFileDemo {
    public static void main(String[] args) throws IOException {
        // 创建文件对象和FileChannel对象
        File file = new File("MemoryMappedFileDemo.txt");
        FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();

        // 将文件映射到内存中
        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());

        // 读取文件内容
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get()); // 输出数据
        }

        System.in.read();

        // 修改文件内容
        buffer.put(0, (byte) 'H');
        buffer.put(1, (byte) 'e');
        buffer.put(2, (byte) 'l');
        buffer.put(3, (byte) 'l');
        buffer.put(4, (byte) 'o');

        // 刷新缓冲区到磁盘
        buffer.force();

        // 关闭通道和文件
        fileChannel.close();
    }
}

产生的系统调用如下:

openat(AT_FDCWD, "MemoryMappedFileDemo.txt", O_RDWR|O_CREAT, 0666) = 4
fstat(4, {st_mode=S_IFREG|0777, st_size=18, ...}) = 0
mmap(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0x7f4fa4004000
write(1, "a", 1)                        = 1
write(1, "b", 1)                        = 1
write(1, "c", 1)                        = 1
write(1, "d", 1)                        = 1
write(1, "e", 1)                        = 1
write(1, "\n", 1)                       = 1
write(1, "f", 1)                        = 1
write(1, "g", 1)                        = 1
write(1, "h", 1)                        = 1
write(1, "i", 1)                        = 1
write(1, "j", 1)                        = 1
write(1, "\n", 1)                       = 1
write(1, "k", 1)                        = 1
write(1, "l", 1)                        = 1
write(1, "m", 1)                        = 1
write(1, "n", 1)                        = 1
write(1, "o", 1)                        = 1
write(1, "\n", 1)                       = 1

read(0, "\n", 8192)
msync(0x7f4fa4004000, 18, MS_SYNC)      = 0
close(4)

中间的代码System.in.read()可以让程序暂停,这时可以查看文件打开的描述符:

$ lsof -p 8964
COMMAND  PID USER   FD   TYPE             DEVICE  SIZE/OFF              NODE NAME
java    8964 root  mem    REG               0,50        18 10977524091816630 /io-demo/target/classes/MemoryMappedFileDemo.txt

java    8964 root    4u   REG               0,50        18 10977524091816630 /io-demo/target/classes/MemoryMappedFileDemo.txt

将文件映射到内存中可以指定长度,这样可以做到只映射文件的部分内容,如果映射的长度大于文件本身的长度,就会扩大文件的长度。这样就限制了文件无法完成拓展,因为mmap到内存的时候,所能操作的范围就确定了,无法增加文件的长度。

mmap的优点:

  • 高效访问:mmap使得文件的读写操作像访问内存一样高效,避免了频繁的系统调用和数据拷贝。
  • 文件共享:多个进程可以将同一个文件映射到各自的地址空间,实现文件共享,方便进程间通信和数据共享。
  • 零拷贝:与零拷贝技术结合,可以在网络传输中减少数据拷贝,提高传输性能。
  • 内存管理:支持用户空间的内存管理,例如内存映射和私有化。

FileChannel锁定文件

FileChannel还提供了文件锁定的功能,可以通过lock()方法来对文件进行加锁,以防止其他进程对文件的读写操作。

下面是一个示例代码,展示了如何使用文件锁定的方式对文件进行操作:

package com.morris.io;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;

/**
 * FileChannel可以对文件加锁
 */
public class FileLockDemo {

    public static void main(String[] args) throws IOException {
        // 创建文件对象和FileChannel对象
        File file = new File("FileLockDemo.txt");
        FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();

        // 对文件进行加锁
        FileLock lock = fileChannel.lock();

        new Thread(() -> {
            try {
                main(args);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        // 暂停下,让其他线程访问下文件,演示锁的效果
        System.in.read();

        // 执行文件操作(读取、写入等)
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put("file lock".getBytes(StandardCharsets.UTF_8));
        byteBuffer.flip();
        fileChannel.write(byteBuffer);

        // 解锁文件
        lock.release();

        // 关闭通道和文件
        fileChannel.close();
    }
}

产生的系统调用如下:

openat(AT_FDCWD, "FileLockDemo.txt", O_RDWR|O_CREAT, 0666) = 4
fstat(4, {st_mode=S_IFREG|0777, st_size=0, ...}) = 0
fcntl(4, F_SETLKW, {l_type=F_WRLCK, l_whence=SEEK_SET, l_start=0, l_len=0}) = 0
read(0, "\n", 8192)
write(4, "file lock", 9)                = 9
fcntl(4, F_SETLK, {l_type=F_UNLCK, l_whence=SEEK_SET, l_start=0, l_len=0}) = 0
close(4)

中间的代码System.in.read()可以让程序暂停,这时可以查看文件打开的描述符:

java    9355 root    4uW  REG               0,50        12 14073748835656455 /io-demo/target/classes/FileLockDemo.txt

可以看到文件描述符4后面多了个W

其他线程访问已加锁的文件会抛出异常:

Exception in thread "Thread-0" java.nio.channels.OverlappingFileLockException
        at java.base/sun.nio.ch.FileLockTable.checkList(FileLockTable.java:229)
        at java.base/sun.nio.ch.FileLockTable.add(FileLockTable.java:123)
        at java.base/sun.nio.ch.FileChannelImpl.lock(FileChannelImpl.java:1276)
        at java.base/java.nio.channels.FileChannel.lock(FileChannel.java:1089)
        at com.morris.io.FileLockDemo.main(FileLockDemo.java:22)
        at com.morris.io.FileLockDemo.lambda$main$0(FileLockDemo.java:26)
        at java.base/java.lang.Thread.run(Thread.java:833)

但是其他进程访问这个已经加锁的文件,可以成功,执行如下的命令可以成功,why???

$ echo xxx > FileLockDemo.txt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1378643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT 小组件 列表框以及微调框

.cpp文件 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);QListWidgetItem *pPhone new QListWidgetItem;pPhone->setText("西瓜");pPhone->…

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多…

eclipse ADT安装及abap cds模版创建

文章目录 1.前提2.安装3.创建cds模版 abap cds 常用语法 https://blog.csdn.net/weixin_49198221/article/details/135531478?spm1001.2014.3001.5501 1.前提 需要了解版本关系: **1.eclipse:**2023-06 (4.28), 2023-09 (4.29), 2023-12 (4.30) 2.Windows: ​ 1.Windows …

java多线程(并发)夯实之路-线程池深入浅出

线程池 Thread Pool:线程池,存放可以重复使用的线程(消费者) Blocking Queue:阻塞队列,存放等待执行的任务(生产者) poll方法(有时限地获取任务)相对take注…

【每日小bug】——mybatis-plus拼接sql空格报错,根据时间聚合查询

mybatis-plus拼接sql报错 复制报错sql语句到navicat,字段之间缺少空格,补上就可以了 聚合sql 根据时间 json接收JsonFormat(timezone "GMT8", pattern "yyyy-MM-dd")DateTimeFormat(pattern "yyyy-MM-dd")private Date startTim…

如何为数据保护加上“安全锁”?

伴随着数字经济的日趋活跃,数据安全和隐私保护成为了各国政府和企业都十分重视的问题,纷纷加强了数据安全防护。但实际上,近几年数据泄露问题接连不断,虽然没有造成严重的后果,但也足以证明目前数据安全防护的紧迫性。…

使用 Github、Hugo 搭建个人博客

Hugo 静态网站构建手册:https://jimmysong.io/hugo-handbook/ 关键字:开源 博客 框架 1、GitHub Pages 官网:https://pages.github.com/ 文档:https://docs.github.com/zh Github Pages 简介 Websites for you and your project…

京东年度数据报告-2023全年度游戏本十大热门品牌销量(销额)榜单

同笔记本市场类似,2023年度游戏本市场的整体销售也呈下滑态势。根据鲸参谋电商数据分析平台的相关数据显示,京东平台上游戏本的年度销量累计超过350万,同比下滑约6%;销售额将近270亿,同比下滑约11%。 鲸参谋综合了京东…

2.idea查看不到git的提交文件

(1)查看日志 使用idea工具查看git提交日志,如下:项目名上右击,选择git->Show History (2)预期结果 (3)实际结果 只能看见此次提交的commit id,看不见所修…

leetcode 动态规划(爬楼梯、零钱兑换、完全平方数)

70. 爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯(opens new window) 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正…

用单片机设计PLC电路图

自记&#xff1a; 见另一篇文章&#xff0c;MOS驱动差了一个充电电容&#xff0c;栅极电容充电会有问题&#xff1b; 光耦用的直插&#xff0c;但板子用的贴片&#xff0c;此文档仅供参考 基本列出了PCB板情况&#xff0c;基础元器件&#xff0c;部分连接&#xff0c;原理等…

大厂设计师都在用Figma中文替代

设计原型别再只知道 Figma 了&#xff0c;现在百万设计师都在用 Figma 的中文替代——即时设计。即时设计是国内第一款基于 Web 的 UI 设计工具&#xff0c;它的出现的弥补了很多 Figma 在国内使用的局限性&#xff0c;凭借本土化的优势&#xff0c;免费使用的版本、丰富免费的…

【Leetcode】2696. 删除子串后的字符串最小长度

文章目录 题目思路代码 题目 2696. 删除子串后的字符串最小长度 思路 计算通过删除字符串中的 “AB” 和 “CD” 子串后&#xff0c;可获得的最终字符串的最小长度。 主要思路是使用一个栈来模拟字符串的处理过程&#xff0c;每次遍历字符串时&#xff0c;如果当前字符和栈…

Openstack组件glance对接swift

2、glance对接swift &#xff08;1&#xff09;可直接在数据库中查看镜像存放的位置、状态、id等信息 &#xff08;2&#xff09;修改glance-api的配置文件&#xff0c;实现对接swift存储&#xff08;配置文件在/etc/glance/glance-api.conf&#xff0c;建议先拷贝一份&#x…

野牛物联网-阿里云配置流程

1、 概述&#xff1a; 本文围绕阿里云物联网平台&#xff0c;实现设备上云、设备上报消息、云端订阅设备消息、云端下发指令到设备等服务&#xff0c;以野牛物联网YNK-MN316设备接入物联网平台为例&#xff0c;介绍设备如何接入物联网平台&#xff0c;向平台上报消息等。帮助您…

Bug:Goland左侧丢失项目结构(Goland常用快捷键)

Goland快捷键&小tips 1 常用快捷键 # 格式化代码 optioncommandL# 在项目中搜索文件中的内容 commandshiftF# 搜索.go文件 shiftshift&#xff08;按两次shift&#xff09;# 修改方法、变量&#xff08;同时替换引用处的名称&#xff09; fnshiftF6# 将选中代码抽取为方法…

验证端口连通性的工具 telent nc

验证端口连通性的工具 telent nc 1、怎么验证端口连通性的工具2、telnet3、nc 1、怎么验证端口连通性的工具 telent nc这2个工具都可以验证端口连通性 2、telnet 命令格式 默认是验证tcp端口连通性 telnet ip port如果需要验证udp端口连通性 需要加上 -u telnet -u ip por…

操作系统期末考复盘

简答题4题*5 20分计算题2题*5 10分综合应用2题*10 20分程序填空1题10 10分 1、简答题&#xff08;8抽4&#xff09; 1、在计算机系统上配置OS的目标是什么&#xff1f;作用主要表现在哪个方面&#xff1f; 在计算机系统上配置OS&#xff0c;主要目标是实现:方便性、…

电脑/设备网络共享给其他设备上网

文章目录 一、概述二、设置网络共享2.1 电脑可以上网&#xff0c;通过网络共享让其他设备也可以上网2.2 手机如何使用USB数据线共享网络给电脑 一、概述 现在有如下几种情况&#xff1a; 设备本身不能上网&#xff0c;需要通过电脑上网 笔记本WIFI连热点上网&#xff0c;然后…

【计算机组成原理】IEEE 754 标准定义的浮点数表示格式

IEEE 754 IEEE 754是一种由美国电气和电子工程师协会&#xff08;IEEE&#xff09;制定的标准&#xff0c;用于定义浮点数的表示和运算。这个标准定义了浮点数的格式、舍入规则、特殊值的处理以及算术操作的执行方式。 IEEE 754浮点数标准主要定义了两种浮点数格式&#xff1…