我正在尝试从有故障的外部 SSD 读取数据以创建用于数据恢复的映像。该驱动器是 Apacer Panther SSD,通过 Ubuntu 上的 ICY BOX SATA 转 USB 连接器连接到 USB 端口。
执行下面的MWE,
read
挂在某个地址。地址在连续运行之间基本上是稳定的,但它可能会有所不同(例如在不同的日子)。当块大小为 1 时,read
挂在某个扇区的第一个字节上。结果是程序冻结并且没有信号中断读取,ctrl-c 只是将“^C”打印到终端,但不会终止程序,并且永远不会调用警报的处理程序。
关闭终端并在新终端上重新运行程序,没有完成
read
(它在第一次迭代时挂起)。只有断开并重新连接SSD才能再次从磁盘读取数据。但是,如果我在 read
被阻止时断开驱动器,程序将继续。
修改并运行以stdin为文件描述符的程序,SIGINT和SIGALRM都会中断
read
。
所以问题是: a) 为什么
read
会无限期地阻塞,因为根据 man page 它会被信号中断?
b) 有什么办法可以解决这个问题吗?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
void sig_handler(int signum){
printf("Alarm handler\n");
}
int main(int argc, char *argv[]) {
// Register ALARM signal handler to prevent read() from blocking indefinitely
struct sigaction alarm_int_handler = {.sa_handler=sig_handler};
sigaction(SIGALRM, &alarm_int_handler, 0);
char* disk_name = "/dev/sdb";
const int block_size = 512;
int offset = 0;
char block[block_size];
// Open disk to read as binary file via file descriptor
int fd = open(disk_name, O_RDONLY | O_NONBLOCK);
if (fd == -1){
perror(disk_name);
exit(0);
}
int i;
int position = offset;
for (i=0; i<100000; i++){
// Reset alarm to 1 sec (to interrupt blocked read)
alarm(1);
// Seek to current position
int seek_pos = lseek(fd, position, SEEK_SET);
if (seek_pos == -1){
perror("Seek");
}
printf("Reading... ");
fflush(stdout);
int len = read(fd, block, block_size);
printf("Read %d chars at %d\n", len, position);
if (len == -1){
if (errno != EINTR){
perror("Read");
}
else {
printf("Read aborted due to interrupt\n");
// TODO: handle it
}
}
position += len;
}
close(fd);
printf("Position %d (%d)\n", position, i * block_size);
printf("Done\n");
return 0;
}
终端上的输出如下所示
.
.
.
Reading... Read 1 chars at 29642749
Reading... Read 1 chars at 29642750
Reading... Read 1 chars at 29642751
Reading...
这可能是内核驱动程序错误。
您尝试过非阻塞读取吗?常规文件无法轮询,但描述符仍然可以设为非阻塞
听起来您的 SSD 可能有缺陷(无法响应请求,例如,在尝试从闪存中的损坏数据中恢复时其固件挂起)或内核驱动程序有错误。
至于进程为什么不响应信号:有一种进程状态叫做“不间断睡眠”(在
D
和top
中缩写为状态htop
)。当进程的控制流位于内核代码内部时,例如等待来自磁盘或网络的数据(NFS 挂载在网络中断期间因此而臭名昭著),进程就会进入此状态。如果您的 SSD 没有回复数据请求,则该进程将无限期地等待数据,因为内核不会第二次询问 SSD。