【linux】NIO中的FileChannel与mmap

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)                       = 1read(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.txtjava    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.OverlappingFileLockExceptionat 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.hqwc.cn/news/345732.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bootloader学习笔记及SD卡启动盘制作

Bootloader介绍 在操作系统运行之前运行的一小段代码,用于将软硬件环境初始化到一个合适的状态,为操作系统的加载和运行做准备(其本身不是操作系统) Bootloader基本功能 1、初始化软硬件环境 2、引导加载linux内核 3、给linux…

019、错误处理:不可恢复错误与panic!

鉴于上一篇文章过长,不方便大家阅读和理解,因此关于Rust中的错误处理, 我将分以下3篇来讲。 另外,随着我们学习的不断深入,难度也会越来越大,但不用担心。接下来只需要让自己的脚步慢一些,认真搞…

FreeRtos Queue (一)

本篇主要讲队列的数据结构和初始化 一、队列的数据结构 二、队列初始化完是什么样子的 队列初始化的函数调用关系:xQueueGenericCreate->prvInitialiseNewQueue->xQueueGenericReset 所以,最终初始化完的队列是这样的 假设申请了4个消息体&…

如何异地链接Pycharm服务器进行远程开发并实现与公司服务器资源同步

文章目录 一、前期准备1. 检查IDE版本是否支持2. 服务器需要开通SSH服务 二、Pycharm本地链接服务器测试1. 配置服务器python解释器 三、使用内网穿透实现异地链接服务器开发1. 服务器安装Cpolar2. 创建远程连接公网地址 四、使用固定TCP地址远程开发 本文主要介绍如何使用Pych…

面试题:说一下缓存穿透?缓存击穿?缓存雪崩?

文章目录 面试题1:怎么解决缓存穿透问题的?那我们怎样来解决这种缓存穿透问题呢?布隆过滤器的优缺点 面试题2:说一下缓存击穿吧,你们是怎么解决的?解决方案: 面试题3:那缓存雪崩说说…

【开源】基于JAVA+Vue+SpringBoot的桃花峪滑雪场租赁系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 游客服务2.2 雪场管理 三、数据库设计3.1 教练表3.2 教练聘请表3.3 押金规则表3.4 器材表3.5 滑雪场表3.7 售票表3.8 器材损坏表 四、系统展示五、核心代码5.1 查询教练5.2 教练聘请5.3 查询滑雪场5.4 滑雪场预定5.5 新…

每日一题 2182. 构造限制重复的字符串(中等,贪心)

贪心,每次都尽量取大的,除非连续取的次数超出限制,此时取一个下一个字符 class Solution:def repeatLimitedString(self, s: str, repeatLimit: int) -> str:N 26count [0] * Nfor c in s:count[ord(c) - ord(a)] 1ret []i, j, m N …

弹性布局(Flex)

目录 1、概述 2、基本概念 3、布局方向 4、布局换行 5、主轴对齐方式 6、交叉轴对齐方式 6.1、容器组件设置交叉轴对齐 6.2、子组件设置交叉轴对齐 7、内容对齐 8、自适应拉伸 9、相关实例 1、概述 弹性布局(Flex)提供更加有效的方式对容器中…

【⭐AI工具⭐】AI工具导航推荐

目录 零 工具导航👉【[AI工具集导航](https://ai-bot.cn/)】👈👉【[iForAI](https://iforai.com/)】👈👉【[AInav](https://www.ainav.cn/)】👈👉【[Navi AI 导航](https://www.naviai.cn/)】&a…

openssl快速生成自签名证书

系统:Centos 7.6 确保已安装openssl openssl version生成私钥文件 private.key (文件名自定义) openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048-out private.key:生成的私钥文件-algorithm RS…

博途PLC增量式PID和脉冲轴组合控制阀门开度(算法介绍)

这篇博客我们以S7-1200PLC平台来举例,介绍我们的PID闭环控制器如何控制脉冲轴实现阀门角度控制。SMART PLC PID控制器控制伺服驱动器实现关节角度控制详细内容请参考下面文章: https://rxxw-control.blog.csdn.net/article/details/129658364https://rxxw-control.blog.csdn…

IntersectionObserver

IntersectionObserver 这个API主要实现图片懒加载、加载更多等等。 该API作用是观察两个元素之间有没有交叉,有没有重叠 现在要做的是当图片跟视口有交叉的情况下,把data-src的图片路径替换给src属性 //第一个参数是 回调,第二个参数的 配置…