linux 强制删除文件句柄,在linux系统中如果误删除了某个文件,但是文件句柄还被某些进程占用着,这种情况下该怎么恢复被删除的文件呢?...

在linux系统中如果误删除了某个文件,但是文件句柄还被某些进程占用着,这种情况下文件还能恢复吗?如果可以,那该怎么恢复被删除的文件呢?

首先我们先来理解下,【文件被删除了,但是文件句柄还被某些进程占用着】是什么意思?

先上个图

7f4f1329a399

文件名与inode的关系

当我们创建一个文件后,linux文件系统会将文件的数据存储到磁盘的数据区(data area),文件根据其大小可能会占用多个磁盘扇区;

每个文件都会对应一个inode,inode存在于磁盘的索引表部分(inode table),inode中存储着文件的元数据信息,文件系统通过inode里存储的元数据读写磁盘;

每个inode都会分配一个唯一的inode编号,即索引节点编号;

文件系统会将文件名与inode编号建立映射关系,存储在内存中,便于快速访问。

上面说了这么一些,那实际上文件系统访问文件的流程是怎样的呢?

1)我们平常查看或修改文件都是通过文件名来操作的,上边说到了文件名与inode之间存在映射关系,而这个映射关系存储在内存中,通过文件名可以找到与之关联的inode编号;

2)拿到了inode编号后,就可以查找到该编号对应的inode,进而从磁盘里读取到inode中存储的文件元数据;

3)通过文件元数据,再访问。

大概流程就是这样。

那这个与我们所要理解的【文件被删除了,但是文件句柄还被某些进程占用着】有什么关系呢?其实是有关系的。

我们常说文件名、文件名,都知道它是一个文件的名称,但实际上文件名只是一个指向文件inode的硬链接(可以理解为文件的一个别名),可以真正恒久不变地标识一个文件的是inode编号;

从一个文件被创建后,它就会对应着一个inode,在它的生命周期内(即一个文件被删除之前),文件的inode都是不会发生改变的;而文件名是可以随意改变的,文件名发生了改变,文件内容是不会受影响的;

就像一个人的微信,它的微信号是唯一的,但是微信昵称却是可以变来变去的。

当一个文件正被某个进程读写时,我们执行rm操作将其删除,此时我们执行cat filename,查看文件文件内容的话,肯定会报【No such file or directory】的错误。

衔接上面说到的文件名只是一个指向文件inode的硬链接(它与inode之间建立的联系),文件系统访问文件是先通过文件名在内存中存储的映射关系中找到inode编号,然后再找到inode,接着读取出inode中文件的元数据,然后文件系统再通过元数据访问磁盘进行IO操作。

我们把文件名删除后,相当于在内存中删除了文件名与inode之间建立的映射关系,这样文件系统就找不到inode了;

但是如果此时还有进程在占用着文件,那么文件不会彻底删除,因为此时该文件的句柄还没释放,文件属于标记删除状态;

而如果文件没有被进程占用且文件也没有其他的硬链接指向它的inode了(即文件只有一个别名),那么此时该文件会被彻底删除(包括文件名、inode以及磁盘中存储的文件数据);

如果此时还有其他的硬链接指向文件的inode(文件有多个别名,类似于一个人可以拥有多个花名),那么仅是删除这个这个硬链接(即此次rm操作只是删除这个文件名,对文件数据没影响)。

所以,【文件被删除了,但是文件句柄还被某些进程占用着】的意思是:虽然你在linux系统上访问文件时报【No such file or directory】错误,但是别慌,文件还没真正删除的,还可以找回来。那怎么找回来呢?这就需要从进程以及其占用的文件句柄(文件句柄其实是一个文件描述符,即符号链接)入手了。

linux系统上跑着的每个进程都在/proc目录下存在着一个记录,记录格式为:/proc/pid,即每个进程在/proc目录下都会对应一个目录,目录名为进程的id号。

例如,进程tail -f ldapbak.ldif的进程id为15603

7f4f1329a399

查看进程的进程id

我们到/proc目录下查看下是否真的有这样的一个目录,切换到/proc,执行ls 15603

7f4f1329a399

进程id为15603的进程在/proc对应的目录为15603

可以看到这个目录下还是有挺多文件的,其中有一个目录为fd。

fd这个目录的作用是存储着该进程占用着的文件句柄,例如:

7f4f1329a399

查看进程15603占用着的文件句柄

其中,0、1、2、3、4这些就是文件描述符,它们其实本质就是符号链接(即软链接),指向被该进程打开的文件。

所以进程打开的文件描述符的存储路径是:/proc/pid/fd/文件描述符

如果一个进程占用的文件描述符没有被释放,那么执行rm操作删除文件时,文件尚未真正被删除。

那么当我们执行rm操作误删了文件,我们怎么判断这个文件是否正被某些进程占用着以判断文件能否恢复呢?

如果你知道文件名,那么可以直接执行lsof | grep filename,如果能够查到记录,那么说明还有进程在占用着这个文件;

否则没有进程占用了,文件可能很难恢复了。

如果不知道确切的文件名,那么可以执行lsof | grep deleted,来查找记录。

此时ldapbak.ldif文件正被tail进程占用着,我们对其执行rm -f操作,然后再通过lsof | grep filename或者lsof | grep deleted来查找,由于这里我明确知道文件名,所以我就直接使用lsof | grep ldapbak.ldif来查找了,如下图:

7f4f1329a399

lsof查看进程打开的文件

可以看到,如果一个被进程占用着,这种情况下对其执行rm操作,lsof得到的记录中会有(deleted)标签,所以我们才可以通过lsof | grep deleted来查找。

要想恢复文件,首先得知道占用着这个文件的进程id以及其创建的文件描述符,为什么?因为我们需要根据/proc/pid/fd/文件描述符,找到被进程占用的文件句柄;

如上图所示,第二列是tail进程的pid:34095(这里因为我kill了进程,重新打开了一个,所以pid不是之前的15603了),第四列是文件句柄(文件描述符)3。

好了,现在找到了pid,也找到了文件描述符,我们可以切换到/proc/34095/fd目录,找到文件描述符3,然后通过cp命令即可恢复被删除的文件;

例如我删除的文件是ldapbak.ldif,那么就可以执行cp 3 /home/test/ldapbak.ldif就可以恢复文件ldapbak.ldif到指定目录了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux上,使用DBus传递文件需要以下步骤: 1. 定义DBus消息的格式,包含传递文件的FD(File Descriptor)类型。 2. 在发送方进程打开文件,并将文件通过DBus消息发送给接收方进程。 3. 在接收方进程接收DBus消息,并从消息获取文件。 4. 使用文件进行文件操作,并在使用完成后关闭文件。 以下是一个简单的C++代码示例,演示了如何使用DBus传递文件: ```cpp #include <iostream> #include <dbus/dbus.h> #include <fcntl.h> #include <unistd.h> using namespace std; // 定义DBus消息的格式 static const char* DBUS_MSG_PATH = "/com/example/Test"; static const char* DBUS_MSG_INTERFACE = "com.example.TestInterface"; static const char* DBUS_MSG_NAME = "TestMessage"; static const char* DBUS_MSG_CONTENT = "This is a test message."; // 定义文件路径 static const char* FILE_PATH = "/tmp/test.txt"; int main() { // 初始化DBus连接 DBusError error; dbus_error_init(&error); DBusConnection* dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { cerr << "DBus Connection Error: " << error.message << endl; dbus_error_free(&error); return 1; } // 创建DBus消息 DBusMessage* dbus_msg = dbus_message_new_signal(DBUS_MSG_PATH, DBUS_MSG_INTERFACE, DBUS_MSG_NAME); if (!dbus_msg) { cerr << "DBus Message Error: Failed to create dbus message." << endl; return 1; } // 打开文件并获取文件 int fd = open(FILE_PATH, O_RDONLY); if (fd < 0) { cerr << "File Error: Failed to open file." << endl; return 1; } // 将文件添加到DBus消息 dbus_bool_t dbus_ret; dbus_ret = dbus_message_append_args(dbus_msg, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); if (!dbus_ret) { cerr << "DBus Message Error: Failed to append file descriptor to dbus message." << endl; return 1; } // 发送DBus消息 dbus_ret = dbus_connection_send(dbus_conn, dbus_msg, NULL); if (!dbus_ret) { cerr << "DBus Connection Error: Failed to send dbus message." << endl; return 1; } // 等待DBus消息发送完成 dbus_connection_flush(dbus_conn); // 关闭文件 close(fd); // 释放DBus消息和DBus连接 dbus_message_unref(dbus_msg); dbus_connection_unref(dbus_conn); return 0; } ``` 在接收方进程,可以通过DBus回调函数接收DBus消息,并从消息获取文件。以下是一个简单的DBus回调函数示例: ```cpp #include <iostream> #include <dbus/dbus.h> #include <fcntl.h> #include <unistd.h> using namespace std; // 定义DBus消息的格式 static const char* DBUS_MSG_PATH = "/com/example/Test"; static const char* DBUS_MSG_INTERFACE = "com.example.TestInterface"; static const char* DBUS_MSG_NAME = "TestMessage"; static const char* DBUS_MSG_CONTENT = "This is a test message."; // 定义文件路径 static const char* FILE_PATH = "/tmp/test.txt"; // 定义DBus回调函数 DBusHandlerResult dbus_callback(DBusConnection* dbus_conn, DBusMessage* dbus_msg, void* user_data) { if (!dbus_msg) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } // 获取DBus消息的类型和名称 const char* dbus_msg_type = dbus_message_type_to_string(dbus_message_get_type(dbus_msg)); const char* dbus_msg_name = dbus_message_get_member(dbus_msg); if (strcmp(dbus_msg_type, "signal") == 0 && strcmp(dbus_msg_name, DBUS_MSG_NAME) == 0) { // 从DBus消息获取文件 int fd; if (!dbus_message_get_args(dbus_msg, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID)) { cerr << "DBus Message Error: Failed to get arguments from dbus message." << endl; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } // 读取文件内容 char buffer[1024]; int nread = read(fd, buffer, sizeof(buffer)); if (nread < 0) { cerr << "File Error: Failed to read file." << endl; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } // 输出文件内容 cout << "File Content: " << buffer << endl; // 关闭文件 close(fd); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } int main() { // 初始化DBus连接 DBusError error; dbus_error_init(&error); DBusConnection* dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { cerr << "DBus Connection Error: " << error.message << endl; dbus_error_free(&error); return 1; } // 注册DBus回调函数 dbus_bus_add_match(dbus_conn, "type='signal',interface='" DBUS_MSG_INTERFACE "',member='" DBUS_MSG_NAME "'", &error); dbus_connection_add_filter(dbus_conn, dbus_callback, NULL, NULL); // 进入DBus消息循环 while (dbus_connection_read_write_dispatch(dbus_conn, -1)) {} // 释放DBus连接 dbus_connection_unref(dbus_conn); return 0; } ``` 需要注意的是,DBus传递文件需要在DBus连接启用FD_PASSING选项,并且DBus库的版本需要在1.9.8以上。在C语言,可以使用dbus_connection_set_max_message_size()函数设置DBus消息的最大长度,以避免DBus消息过长导致传递文件失败。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值