问题

为什么 Windows 文件设计成占用无法删除?

回答
Windows 文件系统中,有些文件确实存在“无法删除”的情况,但这并非设计初衷的“故意为之”,而是为了保证操作系统正常运行的必要措施。更准确地说,是系统为了维护自身稳定性和安全性,对特定文件施加了访问限制。

核心原因:系统依赖与文件锁定

想象一下,Windows 就像一个复杂的城市,各种部门(进程、服务)各司其职,彼此协作。而系统文件,就像是城市里的关键基础设施,比如供水管网、电力系统、交通信号灯等等。如果这些基础设施突然被拆除,整个城市就会陷入瘫痪。

同样,Windows 操作系统也依赖大量的系统文件来启动、运行和执行各种任务。如果用户能够随意删除这些文件,就会出现:

系统崩溃: 丢失了关键的系统文件,Windows 无法找到启动所需的组件,直接导致启动失败,蓝屏(BSOD)是常态。
功能异常: 即使能够勉强启动,许多应用程序和服务也可能因为找不到所需的库文件、配置文件而无法正常工作,甚至引发各种奇怪的错误。
安全漏洞: 一些安全相关的系统文件,如防火墙组件、杀毒软件的引擎文件,如果被删除,将使系统门户大开,容易受到病毒和恶意软件的攻击。

为了防止这种灾难性的后果,Windows 系统设计了文件锁定机制和权限控制。

具体表现形式:

1. 进程占用(文件锁定):
这是最常见也是最根本的原因。当一个文件正在被系统中的某个进程(例如,一个正在运行的程序、一个后台服务、系统启动时加载的驱动程序)使用时,该文件会被“锁定”。
Windows 的文件系统(如 NTFS)支持文件锁定功能。当一个进程请求访问某个文件时,它可以请求独占访问权限,或者共享访问权限。如果它请求独占访问,其他进程就无法修改或删除该文件,直到该进程释放文件锁。
你可以把这想象成一把锁。正在使用文件的程序就像是拿着这把锁的钥匙,其他人想要打开(访问/修改/删除)这个文件,就必须等到这把钥匙被归还(程序关闭或释放文件)。
常见例子: 正在播放的音乐文件(媒体播放器占用)、正在编辑的 Word 文档(Word 占用)、系统启动时加载的驱动程序(如显卡驱动、声卡驱动)、正在运行的杀毒软件扫描的系统文件等等。

2. 所有权和权限设置:
Windows 系统使用一套复杂的权限模型(ACL Access Control List)来管理文件和文件夹的访问。这些权限决定了哪些用户或用户组可以读取、写入、执行或删除特定的文件。
关键的系统文件通常被设置为只有Administrator(管理员)用户组或SYSTEM(系统账户)才有完全的控制权,而普通用户则没有删除权限。
即使你以管理员身份登录,某些文件可能仍然属于 SYSTEM 账户,并且 SYSTEM 账户对它们拥有更高级别的访问权,导致即使管理员也无法直接删除。这是一种额外的保护层。
例子: 位于 `C:Windows` 目录下的核心系统文件(如 `ntoskrnl.exe`,即 Windows 内核),或者 `C:WindowsSystem32` 目录下的许多动态链接库(DLL 文件)和可执行文件。

3. 只读属性和系统属性:
虽然不直接等于“无法删除”,但只读属性和系统属性会增加删除的难度。
只读属性意味着文件内容不能被修改,但通常可以删除(需要权限)。
系统属性则是一个标记,表明该文件是系统文件,Windows 在显示文件列表时默认情况下会隐藏它们,并且更倾向于保护它们。有些第三方工具在删除带有系统属性的文件时可能会需要额外确认。

4. 特殊文件夹和文件保护:
某些特定的系统文件夹(如 `C:System Volume Information`)包含系统还原点、索引服务信息等重要数据,它们本身就受到特殊的保护,不允许用户直接访问和修改。
一些重要的系统进程(如 `svchost.exe`)会加载各种服务,这些服务需要依赖于 `System32` 目录下的特定 DLL 文件。即使进程退出,操作系统的其他部分可能还在等待这些文件的可用性。

如何“绕过”这些限制(不推荐,仅为说明原理):

理解了原因,也就知道了为什么直接删除会失败。要删除这些文件,通常需要:

安全模式: 在安全模式下启动 Windows,只有最核心的系统服务会加载,许多第三方程序和驱动不会运行,从而减少文件占用的情况。
第三方工具: 使用一些特殊的第三方工具,它们可以在系统启动前(如通过引导盘或预启动环境)访问文件系统,或者能够强制解锁文件并删除。
修改文件权限: 在管理员权限下,可以通过文件属性的“安全”选项卡,修改文件的所有权和权限设置,赋予当前用户删除权限。但这通常需要小心操作,因为错误地修改权限可能会导致系统不稳定。
停止相关服务/进程: 找到占用文件的具体进程或服务,先将其停止,然后再尝试删除。这需要了解系统的运行机制,并且可能不是所有进程都能随意停止。

总结:

Windows 文件设计成占用无法删除,不是为了“刁难”用户,而是为了保障操作系统的稳定性、完整性和安全性。这就像你不能随意拆掉家里的电线或水管一样,它们是维持正常生活的基础。系统文件是 Windows 正常运行的基石,一旦被破坏,整个系统就会像一座没有地基的房子一样轰然倒塌。因此,Windows 通过文件锁定、权限控制等机制来保护这些至关重要的文件,确保用户在使用过程中不会因为误操作而导致系统瘫痪。

网友意见

user avatar

简单回答:是的,确实就是一个设计失误。

Linux的文件实体本身,与文件名是解耦的,也就是说,一个文件可以拥有很多个文件名,删除掉其中一个文件名不影响访问到文件实体本身。

在内存中打开一个Linux文件,大致相当于在内存中创建了一个临时的新文件名用于访问这个文件实体,此时,硬盘上所有的文件名当然都可以删除掉。关闭内存中的文件,意味着删除了这个文件实体对应的最后一个文件名,此时引用计数为零,才会被删除。

——用程序员的话来讲,Linux的文件名就是一个共享指针,文件实体的删除其实类似于内存垃圾回收机制。删除其中一个指针不应当受到指针指向的实体所限制,删除掉所有指针才会导致实体释放。

而Windows最初设计为一个文件的实体只能依赖文件名来访问,文件名与文件实体之间强耦合,因此删除掉文件名之后缺乏有效的手段访问到一个文件的实体。想要访问到这个文件就必须提供某种,根据文件ID或者编号之类的来访问文件的方法。而目前Windows并没有提供类似的API。

我相信后来微软也意识到了这个问题,但大概是因为兼容性的原因,一直没有解决掉,也就延续了几十年之久。不知道未来会不会解决它,大概是不会了吧。

user avatar

要回答这个问题,先要分析一下:如何让文件系统允许删除正在打开的文件?

这个问题看上去简单,其实设计起来比较复杂的。比如,在Linux上删除正在打开的文件,那么打开的文件FD是仍然可用的,文件的全部内容都是可见的,直到最后一个FD关闭了为止(引用计数为0)。文件系统只是把对应的文件设成“待删除”的状态,等待引用计数归零以后才真正在后台删除。这期间,“待删除”文件仍然占用着磁盘空间。这需要文件系统和操作系统支持:

1. 在内存中维护一个待删除的队列

为什么不在硬盘上?理论上可以在硬盘上,但会增大文件系统设计的复杂度。这跟FAT的文件名删除标记是不同的,待删除的文件,整个数据结构在磁盘上都是存在的,只有名字不存在。而FAT的文件名删除标记打上以后,FAT链表也会跟着释放掉。

2. 文件系统内部有一个清理任务(线程?)去完成真正的删除动作

这个比较容易理解,就是有个任务专门负责清理待删除文件。

3. 操作系统的cache管理能针对一个删除的名字(甚至是重名)文件做管理,并正确区分。

比如,现在有1.txt文件正在被使用,然后被删掉了,又创建了1.txt文件,又被打开了,那么此时操作系统内部,以文件名作为索引的话,是有两个1.txt文件的,一个是被删除的状态,但文件内容,以及对应的cache块都是存在的。这个看上去比较简单的事情,才是最麻烦的,要操作系统的cache管理层负责维护两个重名文件,而cache管理又往往是操作系统最核心的部分。


现在来看Windows的实现,虽然Windows提供了FILE_SHARE_DELETE,但是如果看一下WRK的代码就知道了,这个玩意仅仅只是打了个标记,根本不涉及任何内核上限制性的内容,所以这个flag不能解释任何原因。

有兴趣的可以看一下IoCheckShareAccess的实现:

那么Windows不能这么做的原因,只能到文件系统驱动里去找,微软开源了FAT的驱动源码(FASTFAT)通过对比WRK源码可以发现,Windows内核标记一个文件对象的,是一个叫FILE_OBJECT的数据结构:

       typedef struct _FILE_OBJECT {     CSHORT Type;     CSHORT Size;     PDEVICE_OBJECT DeviceObject;     PVPB Vpb;     PVOID FsContext;     PVOID FsContext2;     PSECTION_OBJECT_POINTERS SectionObjectPointer;     PVOID PrivateCacheMap;     NTSTATUS FinalStatus;     struct _FILE_OBJECT *RelatedFileObject;     BOOLEAN LockOperation;     BOOLEAN DeletePending;     BOOLEAN ReadAccess;     BOOLEAN WriteAccess;     BOOLEAN DeleteAccess;     BOOLEAN SharedRead;     BOOLEAN SharedWrite;     BOOLEAN SharedDelete;     ULONG Flags;     UNICODE_STRING FileName;     LARGE_INTEGER CurrentByteOffset;     ULONG Waiters;     ULONG Busy;     PVOID LastLock;     KEVENT Lock;     KEVENT Event;     PIO_COMPLETION_CONTEXT CompletionContext; } FILE_OBJECT;     

对于Linux来说是一个叫file的数据结构

       struct file {  union {   struct llist_node fu_llist;   struct rcu_head  fu_rcuhead;  } f_u;  struct path  f_path;  struct inode  *f_inode; /* cached value */  const struct file_operations *f_op;   /*   * Protects f_ep, f_flags.   * Must not be taken from IRQ context.   */  spinlock_t  f_lock;  enum rw_hint  f_write_hint;  atomic_long_t  f_count;  unsigned int   f_flags;  fmode_t   f_mode;  struct mutex  f_pos_lock;  loff_t   f_pos;  struct fown_struct f_owner;  const struct cred *f_cred;  struct file_ra_state f_ra;   u64   f_version; #ifdef CONFIG_SECURITY  void   *f_security; #endif  /* needed for tty driver, and maybe others */  void   *private_data;  #ifdef CONFIG_EPOLL  /* Used by fs/eventpoll.c to link all the hooks to this file */  struct hlist_head *f_ep; #endif /* #ifdef CONFIG_EPOLL */  struct address_space *f_mapping;  errseq_t  f_wb_err;  errseq_t  f_sb_err; /* for syncfs */ } __randomize_layout   __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */     

虽然两边差异很大,但可以看到相似的成员有两个:

       FILE_OBJECT->FileName <===> file->f_path //文件路径 FILE_OBJECT->FsContext2 <===> file->private_data //驱动私有信息     

而Linux还多了一个很关键的结构:file->f_inode

inode结构对应的有点类似于Windows里的FCB,但FCB结构是各个文件系统自己定义的,比如FAT的定义:

然后各自的文件系统驱动再通过file --> inode或者FILE_OBJECT --> FCB结构关联各自操作系统内核的cache管理部分,形成一套完整的cache管理机制(Windows通过FILE_OBJECT->SectionObjectPointer找到对应的cache内容)。

对比二者的区别的话,可以发现Linux在文件系统公共层(VFS)上有定义inode结构,而Windows没有,同时Linux的inode结构不是以名字索引的,而是以一个序号i_ino:

       struct inode {         /*.....*/  /* Stat data, not accessed from path walking */  unsigned long  i_ino; };     

问题就出在这里:

Windows的文件系统驱动里只能通过名字来索引FILE_OBJECT结构的,不像Linux那样可以通过序号(i_ino)来找到inode,那么一旦一个文件的文件名处于无效的状态,那么内核就没办法检索出来一个文件名对应的FILE_OBJECT,以及它对应的cache节点。而一旦cache管理出了问题,那么内核是必然要崩溃的。

所以Windows不能这么做的原因是:

受限于FILE_OBJECT数据结构的设计(可能最早是因为FAT的设计问题),导致内核/文件系统驱动中不能正确处理重名FILE_OBJECT问题,重名可能导致内核cache管理部分混乱,所以Windows不允许删除一个正在打开的文件。这是一个内核设计的问题,上层的各种行为只是表面现象。

那么Windows的FILE_SHARE_DELETE是怎么回事呢?其实FILE_SHARE_DELETE并没有把文件真的删除。如果你创建了一个带FILE_SHARE_DELETE标记的文件,然后删除它,再创建一个重名的文件,Windows会提示你创建失败:

所以,Windows仍然是以文件名的方式管理cache的。

因为Linux使用i_ino在这个序号来管理文件cache,不存在重名问题,所以自然可以删除文件不受影响。

顺便补充一下,各种杀毒软件的文件粉碎机,本质上就是干掉FILE_OBJECT->SectionObjectPointer里的数据结构,释放内核中对应的cache节点,这样FILE_OBJECT重名也不会波及到内核的cache管理导致蓝屏了。


其实Windows是支持FILE_OPEN_BY_FILE_ID的动作的,但可能是因为兼容性的问题(FILE_OBJECT改动会影响很多驱动),所以微软只能保持现状,而Linux则不考虑兼容性问题,灵活性自然要好很多。

那么,这个算不算是Windows的设计失误?我个人认为勉强算是,非要说bug是feature也是可以接受的。Windows的这个行为导致了其文件系统与POSIX规范不完全兼容,对于一些开源软件来说,不算友好。当然了,微软为了兼容性做了很大的让步,甚至包括bug的兼容性,比如Win9x里内存free以后仍然可用的bug,跳过Windows9这个版本号等等,所以为了文件系统的兼容性而保持这种行为也是可以理解的。毕竟Windows是要考虑兼容性问题的,每个大版本的改动都会导致一些驱动不能正常工作,这对于用户来说是很难接受的。

Linux的fs.h变化很大,不过Linux里大部分软件都是开源的,所以只要重新编译一下就可以了,不过对于某些闭源软件来说,Linux这种频繁变动的设计也是很不友好的。


补充:

其它回答和评论里提到,我这个只是描述不能删除的原因,没有说为什么这么设计。我个人感觉是已经说的很清楚了,就是兼容性的问题。展开来说就是:

Windows使用文件名作为内核对象(FILE_OBJECT)的唯一标识符 -> 文件名不能重名 -> 文件不能在打开状态下被删除。

如果继续向上追溯,是因为DOS时代的文件系统FAT就没有inode的概念,FAT的文件属性保存在目录里而不是单独的一个区域,Win9x只支持FAT

inode的概念最早是从UNIX-like的文件系统中出现的,属于文件系统的另一个发展方向。Windows也试图在文件系统中引入类似inode的定义,比如NTFS的$MFT里就有类似inode的定义。

技术发展到今天,文件系统的底层数据结构已经不是影响文件系统行为的决定性因素,比如FAT32没有inode,但在Linux上一样可以获得inode号。所以到了今天,Windows不能删除一个打开的文件,本质上不是技术能力问题,也不是文件系统数据结构问题,纯粹是为了保持兼容性而做出的牺牲


还有的答案提到,把FILE_OBJECT的里的filename清空,不影响使用,这个确实不影响,但又不是只有这一个地方保存了文件名。以FAT为例,至少在FCB里,在Name splay tree里都有文件名(FASTFAT: FCB_STATE_NAMES_IN_SPLAY_TREE)。核心的问题是Windows内核中,不具备类似Linux那种的inode结构,inode结构是一个与文件名无关的设计,没有这个前提,要删除打开的文件就变得麻烦了。

断开一切FILE_OBJECT里的引用确实没问题,甚至都不会蓝屏,但是如果是移动设备(U盘),你就会发现这个盘怎么也不能安全弹出了,因为有一个data cache一直释放不掉。因为操作系统内核(NTOSKRNL的Cache Manager,就是WDK里一堆CC开头的API)跟这一整套东西紧紧的绑定在一起。

最后,为什么我用WRK,因为WRK是公开的。兼容性又不是指代码完全不变,API行为才是最大的兼容性考验。况且WIN10 20H2的FILE_OBJECT也就比WRK上新增了三个成员,都是在结尾扩展的,前面的TYPE/SIZE完全可以覆盖这种兼容性的缺陷。猜猜WRK是基于哪个版本的?

user avatar

这就是个历史问题,历史上这么做没什么不对的,因为允许文件在打开的时候被删除,对于很多软件来说是灾难性的事情。

但是既然这么实现了,就有软件依赖于这个Feature(譬如说直接用文件做跨进程锁),结果依赖了几十年到了今天就成了个严重的问题。事实上微软自身也被这个问题搞得焦头烂额,Windows更新过程中必须要重启很大程度上就是因为这个原因。这个事情被客户吐槽了不知道多少次,看看微软做出的努力也能知道这个Feature要移除会带来多大的问题(否则十几年前就移除了)……更有很多恶意软件利用这一点来规避杀毒软件的清除……

但是现代的操作系统都开始弱化文件系统的存在感,我认为这个问题在未来随着操作系统给软件的隔离和限制越来越多,会逐步的淡化……

user avatar

回答 why 的问题能不能不要有那么多长篇大论 what/how 的答案?

Windows 的独占强制文件锁是一个设计失误。它的问题在于:

  1. 对于它要避免的问题,完全有其它解决方法;
  2. 它的目的经常被误解。

首先说第二点,独占强制锁要解决的不是数据丢失问题。这个很好理解,如果没有 process 在访问一个文件,用户还是想怎么删就怎么删。它要解决的也不是 app 私有文件的 integrity 问题。私有文件放到用户看不到的私有 container 里就好。

回到第一点,它要解决的是早期 PC 的一个性能问题。首先上面说了,它面对不是 app 私有文件的问题,所以这里是用户文件,也就是诸如 .docx, .pptx, .psd 这样的问题。而且上面说了,它要解决的并不是这类文件被误删的问题,那是什么问题呢?考虑这种情况:

一个用户打开了三个 .doc 文件。都做了编辑。然后他删掉了其中一个,回到 Word 之后,Word 因为处理不好文件被删除 crash 了。结果另外两个文件的编辑也丢失了。

其实这个情况今天很好解决。一个普遍方案是,app 在打开文件的时候把文件 copy 到私有 container 中,编辑结束再 copy 回原来位置。但是早期 PC 时代这样做还是比较消耗资源,所以 Microsoft 就搞了一个 performance hack。

所有你看起来奇怪的设计,大都是 performance hack。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有