文件系统如何实现扇区锁定?

3
虽然这是一个普遍的问题,但对于任何Windows、Linux或Mac支持的文件系统的具体答案都可以接受。
文件系统如何防止2个或更多进程并发读/写本地驱动器的扇区?
我知道显然需要使用某种类型的扇区锁定,但是...
  1. 这个操作系统级别的扇区锁的“类型”会是什么:

    a)命名互斥体/命名信号量/文件映射?(在Windows上)

    b)共享内存?(在Linux上)

  2. 这个扇区锁存储在哪里:内存中?磁盘上?无论在哪种情况下,无论这个锁存储在哪里,我都知道...

    a)锁的存储必须是逻辑上临时的存储,因为如果其所有者进程崩溃,锁不应永久地挂起或无限期地挂起。因此,如果这些锁存在于内存中,则有所帮助。

    b)锁的位置必须通过名称或ID公开地由所有进程发现,该名称或ID是找到扇区号函数的一部分。因此,如果进程A需要锁定扇区S,A应该知道如何首先访问扇区S的锁L(S),如果它不存在,则创建L(S),尝试锁定L(S),并原子地返回或阻止 - 因为另一个进程B也可能与A一起竞争,试图执行相同的步骤序列。

  3. 对于大型存储设备,扇区的数量也会很大。因此,每个扇区的锁显然不能预先分配在内存或磁盘上。

我不知道或者不理解的是,Linux上的ext*文件系统、Windows上的FAT*和NTFS以及Mac等操作系统如何处理这个问题。

我的问题的实际背景:最终,我需要将以上答案应用于我正在使用Java 7 NIO2 FileSystemProvider编写的基于Java的自定义文件系统,其中2个或多个独立的Java进程可以访问所附加驱动器的一堆扇区进行读/写。在Java 7中,我所知道的唯一非JNI方式来实现进程间共享内存的方法是通过MappedByteBuffer。但问题是,这个字节缓冲区将是“原始”内存 - 而我需要的是一种全局的、互斥型机制,并且可能还需要一个每个扇区S的互斥体。看起来,除非我也调用MappedByteBuffer.force,否则我的更改不会在整个操作系统范围内原子地执行。

非常感谢所有答案和评论。


2
你为什么认为文件系统实现了扇区锁定?你所说的“扇区锁定”具体指什么? - Dwayne Towell
如果sector是一个共享资源,可以同时由2个或更多进程进行读写操作,那么这个资源就像其他资源一样,在并发编程中需要进行序列化访问。这就是我所说的“扇区锁定”。但是,即使是你简短的反问(你措辞的方式)也足以引起我的思考,所以没有冒犯之意,只有感谢! - Harry
1
Harry,文件系统操作在驱动程序内核模式下进行。驱动程序使用同步原语来同步其线程,但不需要进行扇区锁定。 - Eugene Mayevski 'Callback
谢谢Eugene,但是对于Java程序来说,什么是内核驱动程序的等效物呢?我在想,为什么Java的设计者没有考虑提供一个明确的“VM驱动程序”功能,让所有JVM实例都可以运行共同的代码。 - Harry
在某个时刻,所有I/O都变成串行操作。你似乎担心两次写入同一扇区发生在同一时间。这是不可能的,因为硬件一次只能执行一次操作。所以问题要么是:我们如何防止多个进程同时访问同一硬件?或者,它与某种“更高级别”的碰撞有关。并非所有文件系统都提供这种更高级别的预防措施。文件系统不需要防止较低级别的冲突,那是驱动程序的问题。 - Dwayne Towell
Dwayne,我理解“在某个时候所有的I/O都变成串行”的说法,这绝对是在硬件层面上。我很失望Java没有“驱动程序”的概念,即对象可以存在于单例中,并且可以在多个JVM之间声明共享。我希望Java的设计者们意识到(或者意识到)这个问题并采取措施。Java被吹捧为“平台”、“虚拟机”,并且提供像NIO2的DirectByteBuffer、JNI等低级别的东西。但是,可悲的是,没有VM驱动程序。在我看来,程序员应该能够决定代码是否可以跨VM共享。 - Harry
1个回答

2
我认为您对现代文件系统的实现方式有些困惑,而旧系统通常不支持多用户;在这个前提下,让我们来看看所谓的日志。现在有一些替代方案 - 即
  1. 软更新
  2. 日志结构文件系统
  3. 写时复制
然而,最常见的可能是日志文件系统。它将数据写入空块,完成写操作后简单地移动指针(同时原子性地写入“日志”),这允许在发生停电(或其他写入中断)的情况下进行快速恢复。
至于在系统级别上锁定您的“日志”,我可能会像这样使用文件锁 -
FileLock lock = null;
FileChannel channel = null;
try {
  // Get a file channel for the file
  File file = new File("journal");
  channel = new RandomAccessFile(file,
      "rw").getChannel();

  // Try acquiring the lock without blocking.
  try {
    lock = channel.tryLock();
    if (lock != null) {
      // GOT THE LOCK.... DO WORK...
      return true; // write success.
    }
  } catch (OverlappingFileLockException e) {
    // File is already locked in this thread or virtual
    // machine
  } finally {
    if (lock != null) {
      lock.release(); // Release the lock
    }
    if (channel != null) {
      channel.close(); // Close the file.
    }
  }
  return false; // Write must be retried?

我确实完全不了解文件系统的实现甚至其内部设计,你和Dwayne给了我一些很好的指引。非常感谢。(在将您的答案标记为最终答案之前,我会等待几天。) - Harry
Elliot,我更倾向于实现一个日志。但是,即使有了日志,我仍需要防止2个或更多进程尝试同时追加到它。在Java领域中,我如何获得一个跨JVM的操作系统级互斥锁来维护日志本身的完整性? - Harry
你可以使用 FileLock;这里有一个 教程 - Elliott Frisch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接