【linux】NIO中的FileChannel与mmap

这篇具有很好参考价值的文章主要介绍了【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对象,从而实现对文件的高效读写操作。

【linux】NIO中的FileChannel与mmap,操作系统,linux,nio,filechannel,mmap,随机读写

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

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???文章来源地址https://www.toymoban.com/news/detail-796473.html

$ echo xxx > FileLockDemo.txt

到了这里,关于【linux】NIO中的FileChannel与mmap的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【操作系统】一篇文章带你理清Linux中的权限!

    🎬 乀艨ic: 个人主页 ⛺️说是高产但是还是过了快半个月才更新() ⭐️来看看这次的博客吧~ 上次注意到发Linux相关的点击量比其他的多很多,那就最近多更几篇Linux相关的吧() 注:Linux的不同发行版本的指令可能有所不同,本次是按照CentOS7的标准来进行追述的。 在谈

    2024年04月11日
    浏览(39)
  • 基于Linux操作系统中的MySQL数据库SQL语句(三十一)

    MySQL数据库SQL语句 目录 一、SQL语句类型 1、DDL 2、DML 3、DCL 4、DQL 二、数据库操作 1、查看 2、创建 2.1、默认字符集 2.2、指定字符集 3、进入  4、删除 5、更改 6、练习 三、数据表操作 (一)数据类型 1、数值类型 1.1、TINYINT 1.2、SMALLINT 1.3、INT 1.4、BIGINT 1.5、FLOAT(M,D) 2、时间

    2024年02月15日
    浏览(50)
  • Java NIO FileChannel:BIO与NIO区别、核心组成部分和常用方方法

    深入探讨Java NIO中的FileChannel,包括BIO与NIO的区别、NIO的核心组成部分(Channels、Buffers、Selectors)、FileChannel的常用方法以及示例代码。了解如何使用FileChannel进行文件数据读写操作。

    2024年01月25日
    浏览(39)
  • 《Linux操作系统编程》 第六章 Linux中的进程监控: fork函数的使用,以及父子进程间的关系,掌握exec系列函数

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月11日
    浏览(27)
  • 【Linux操作系统】探秘Linux奥秘:Linux 操作系统的解密与实战

    🌈个人主页: Sarapines Programmer 🔥 系列专栏: 《操作系统实验室》 🔖诗赋清音:柳垂轻絮拂人衣,心随风舞梦飞。 山川湖海皆可涉,勇者征途逐星辉。 目录 🪐1 初识Linux OS 🪐2. Linux 操作系统的解密与实战 🌍1. 实验目的 🌍2. 实验准备 🌍3. 实验内容 🌍4. 实验心得 📝总

    2024年02月03日
    浏览(42)
  • 【Linux操作系统】探秘Linux奥秘:操作系统的入门与实战

    🌈个人主页: Sarapines Programmer 🔥 系列专栏: 《操作系统实验室》 🔖诗赋清音:柳垂轻絮拂人衣,心随风舞梦飞。 山川湖海皆可涉,勇者征途逐星辉。 目录 🪐1 初识Linux OS 🪐2 操作系统的入门与实战 🌍1. 实验目的 🌍2. 实验准备 🌍3. 实验内容 🌍4. 实验心得 📝总结

    2024年02月04日
    浏览(38)
  • 操作系统中的调度算法

    处理机调度层次: 1.高级调度( 作业 调度/) 2.中级调度( 内存 调度/) 3.低级调度( 进程 调度/) 一、作业调度算法 1.先来先服务算法(FCFS) 2.短作业优先算法(SJF) 3.优先级调度算法(PR) 4.高响应比调度算法(PR特例) 5.时间片轮转算法(RR) 6.多级队列调度算法 7.基

    2024年02月10日
    浏览(31)
  • 【网络安全】-Linux操作系统—操作系统发展历史与Linux

    操作系统(Operating System,简称OS)是管理计算机硬件与软件资源的系统软件,它是计算机系统的核心与基石。操作系统的发展历史可以追溯到20世纪50年代。 初期的操作系统 在计算机技术的早期,操作系统并不存在。那时,计算机使用一种叫做批处理系统的方式来运行程序。

    2024年02月03日
    浏览(50)
  • linux操作系统与Windows操作系统

    【1】Windows 操作系统和Linux操作系统有什么不同? 1、首先是他们的开放性不同 所谓的开放性就是linux操作系统是开放源du码系统,可以对其程序进行编辑修改。而微软的daoWindows系统是受微软版权保护,只能微软内部进行开发及修改。 2、其次是它们的价格不同 linux系统是免费

    2024年02月04日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包