尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

ubuntu26.04坏块分区隔离急速版

ubuntu26.04坏块分区隔离急速版 总共3个步骤扫描 使用 sudo python3 s1.pyimport os import sys import time import struct import fcntl import subprocess # --- 配置参数 --- DEVICE_PATH /dev/sdb # 目标设备路径请根据实际情况修改 OUTPUT_FILE found_bad_blocks.txt # 坏块记录文件 BLOCK_SIZE 4096 # 块大小 (字节) SKIP_SIZE_MB 500 # 发现坏块后跳过的距离 (MB) ALL_current_block 83876080 #自定义当前块 # 计算跳跃的块数 SKIP_BLOCKS (SKIP_SIZE_MB * 1024 * 1024) // BLOCK_SIZE def get_block_device_size(device_path): 获取块设备大小的稳健方法 优先使用 os.path.getsize失败则尝试 ioctl最后尝试 blockdev 命令 # 方法 1: 使用 os.path.getsize (现代 Linux 内核通常支持) try: size os.path.getsize(device_path) if size 0: return size except Exception as e: print(fos.path.getsize 尝试失败: {e}) # 方法 2: 使用 ioctl BLKGETSIZE64 try: # O_NONBLOCK 防止在某些特殊设备上挂起 fd os.open(device_path, os.O_RDONLY | os.O_NONBLOCK) try: # BLKGETSIZE64 请求码: 0x80081272 # 缓冲区必须是 8 字节 buf b\x00 * 8 ret_buf fcntl.ioctl(fd, 0x80081272, buf) # Q 表示小端序 unsigned long long (8 bytes) size struct.unpack(Q, ret_buf) if size 0: return size except Exception as e: print(fioctl 尝试失败: {e}) finally: os.close(fd) except Exception as e: print(f打开设备文件失败: {e}) # 方法 3: 使用 blockdev 命令作为最后的手段 try: result subprocess.run( [blockdev, --getsize64, device_path], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue, timeout5 ) if result.returncode 0: return int(result.stdout.strip()) except Exception as e: print(fblockdev 命令执行失败: {e}) return 0 def check_block_readability(device_path, block_index): 尝试读取指定索引的块。 如果成功返回 True如果失败IOError/OSError返回 False。 try: with open(device_path, rb) as f: f.seek(block_index * BLOCK_SIZE) data f.read(BLOCK_SIZE) if len(data) BLOCK_SIZE: return False return True except OSError: return False except Exception: return False def scan_device(): bad_blocks [] # 获取设备大小 print(f正在检测设备 {DEVICE_PATH} ...) device_size get_block_device_size(DEVICE_PATH) if device_size 0: print(f错误: 无法获取设备 {DEVICE_PATH} 的大小。) print(请执行以下检查:) print(f1. 运行 lsblk {DEVICE_PATH} 确认设备存在且显示大小。) print(f2. 运行 sudo blockdev --getsize64 {DEVICE_PATH} 确认内核能识别大小。) print(3. 确保设备未处于休眠状态且连接正常。) return total_blocks device_size // BLOCK_SIZE print(f设备: {DEVICE_PATH}) print(f总大小: {device_size / (1024**3):.2f} GB) print(f总块数 (4K): {total_blocks}) print(f策略: 遇到坏块跳过 {SKIP_SIZE_MB} MB ({SKIP_BLOCKS} blocks)) print(- * 30) #更改当前进度 current_block ALL_current_block #6790000 start_time time.time() try: # 以只读、二进制模式打开 with open(DEVICE_PATH, rb) as f: while current_block total_blocks: # 进度显示 if current_block % 10000 0: progress (current_block / total_blocks) * 100 elapsed time.time() - start_time speed current_block / elapsed if elapsed 0 else 0 sys.stdout.write(f\r扫描进度: {progress:.2f}% | 当前块: {current_block} | 速度: {speed:.0f} blocks/s) sys.stdout.flush() # 检测当前块 if not check_block_readability(DEVICE_PATH, current_block): print(f\n[!] 发现坏块 at Block Index: {current_block} (Offset: {current_block * BLOCK_SIZE})) bad_blocks.append(current_block) # 记录到文件 with open(OUTPUT_FILE, a) as out_f: out_f.write(f{current_block}\n) print(f[-] 跳过 {SKIP_SIZE_MB} MB...) current_block SKIP_BLOCKS else: current_block 1 print(\n扫描完成。) print(f共发现 {len(bad_blocks)} 个坏块区域。) # 记录到文件 with open(OUTPUT_FILE, a) as out_f: out_f.write(f{total_blocks}\n) print(f结果已保存至: {OUTPUT_FILE}) except PermissionError: print(错误: 权限不足。请使用 sudo 运行此脚本。) except FileNotFoundError: print(f错误: 设备 {DEVICE_PATH} 不存在。) except Exception as e: print(f发生未知错误: {e}) if __name__ __main__: if os.geteuid() ! 0: print(警告: 建议以 root 权限运行以访问原始设备。) # 不清空旧结果 #if os.path.exists(OUTPUT_FILE): #os.remove(OUTPUT_FILE) scan_device()2 生成分区文件 使用 sudo python3 s2.py#!/usr/bin/env python3 import os import sys # 配置常量 INPUT_FILE /root/found_bad_blocks.txt OUTPUT_FILE /root/large_gap_bad_blocks.txt # 导出的新文件路径 BLOCK_SIZE 4096 # 块大小4096 字节 THRESHOLD_MB 512 * 3 100 # 间隔阈值1 MB 自定义 def export_large_gap_blocks(input_path, output_path, block_size, threshold_mb): 读取排序后的坏块文件找出间隔超过阈值的坏块对并导出到新文件。 if not os.path.exists(input_path): print(f错误: 输入文件 {input_path} 不存在) return # 计算阈值对应的字节数 threshold_bytes threshold_mb * 1024 * 1024 prev_block None line_count 0 exported_count 0 print(f正在处理文件: {input_path}) print(f阈值设置: 间隔 {threshold_mb} MB) try: # 以写入模式打开输出文件 with open(input_path, r) as infile, open(output_path, w) as outfile: for line in infile: line line.strip() if not line: continue try: current_block int(line) except ValueError: continue line_count 1 # 如果不是第一行计算间隔 if prev_block is not None: gap_bytes (current_block - prev_block) * block_size # 如果间隔大于阈值将这两个坏块号写入新文件 if gap_bytes threshold_bytes: outfile.write(f{prev_block}\n) outfile.write(f{current_block}\n) exported_count 2 # 每次写入两个坏块号 # 更新上一个坏块 prev_block current_block print(f处理完成。) print(f总读取坏块数: {line_count}) print(f导出的坏块数: {exported_count}) print(f结果已保存至: {output_path}) except Exception as e: print(f发生错误: {e}) sys.exit(1) if __name__ __main__: export_large_gap_blocks(INPUT_FILE, OUTPUT_FILE, BLOCK_SIZE, THRESHOLD_MB)3 分区 使用 sudo python3 s3.py#!/usr/bin/env python3 import os import sys import struct import subprocess import time # --- 配置参数 --- DEVICE /dev/sdb # 目标磁盘设备请根据实际情况修改 INPUT_FILE /root/large_gap_bad_blocks.txt # 坏块/分区定义文件 BLOCK_SIZE 4096 # 逻辑块大小 (字节) SECTOR_SIZE 512 # 物理扇区大小 (字节) SGDISK_CMD sgdisk # GPT分区工具 # 1M 256 blk # 262,144 1G对应的blk # THRESHOLD_MB 512 * 3 100 # 间隔阈值1 MB ##1G有效空间两边空余50M ALL_start_blk_add_blk256 * (512 50) ALL_end_blk_xor_blk256 * 50 def check_root(): if os.geteuid() ! 0: print(错误: 此脚本需要 root 权限 (sudo) 才能操作原始磁盘设备。) sys.exit(1) def check_dependencies(): if not subprocess.run([which, SGDISK_CMD], stdoutsubprocess.PIPE, stderrsubprocess.PIPE).returncode 0: print(f错误: 未找到命令 {SGDISK_CMD}。) print(请安装 gdisk 包: sudo apt-get install gdisk) sys.exit(1) def read_partition_definitions(filepath): 读取文件每两行为一组起始块, 结束块 返回: [(start_block, end_block), ...] if not os.path.exists(filepath): print(f错误: 文件 {filepath} 不存在。) sys.exit(1) pairs [] with open(filepath, r) as f: lines [line.strip() for line in f if line.strip()] if len(lines) % 2 ! 0: print(错误: 输入文件格式错误。行数必须为偶数每两行定义一个分区起始块, 结束块。) sys.exit(1) for i in range(0, len(lines), 2): try: start_blk int(lines[i]) ALL_start_blk_add_blk end_blk int(lines[i1]) - ALL_end_blk_xor_blk if start_blk 0 or end_blk 0: raise ValueError(块索引不能为负数) if start_blk end_blk: print(f警告: 跳过无效区间 (Start {start_blk} End {end_blk})) continue pairs.append((start_blk, end_blk)) except ValueError as e: print(f错误: 无法解析行 {lines[i]} 或 {lines[i1]}。{e}) sys.exit(1) if not pairs: print(错误: 文件中没有有效的分区定义。) sys.exit(1) return pairs def wipe_disk(device): 清除磁盘现有的分区表 (MBR/GPT) print(f[步骤 1/3] 正在清除 {device} 上的现有分区表...) try: # -Z: zap (destroy) GPT and MBR data structures result subprocess.run( [SGDISK_CMD, -Z, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: # 有时如果磁盘全是0sgdisk可能会报错说没发现有效签名这通常是可以接受的 if No valid GPT or MBR in result.stderr or No partition table found in result.stderr: print( 磁盘似乎已经是空的或无有效分区表。) else: print(f 清除失败: {result.stderr}) return False else: print( 分区表已清除。) # 强制内核重新读取分区表 subprocess.run([partprobe, device], stdoutsubprocess.DEVNULL, stderrsubprocess.DEVNULL) time.sleep(1) return True except Exception as e: print(f 执行清除命令时出错: {e}) return False def create_partitions(device, partitions): 使用 sgdisk 创建分区 注意: sgdisk 使用 512 字节扇区作为单位。 转换: 扇区索引 块索引 * (BLOCK_SIZE / SECTOR_SIZE) multiplier BLOCK_SIZE // SECTOR_SIZE # 通常为 8 print(f[步骤 2/3] 开始在 {device} 上创建 {len(partitions)} 个分区...) for idx, (start_blk, end_blk) in enumerate(partitions, 1): start_sec start_blk * multiplier end_sec end_blk * multiplier # sgdisk 分区号从 1 开始 part_num idx print(f - 分区 {part_num}: 块 [{start_blk}-{end_blk}] 扇区 [{start_sec}-{end_sec}]) try: # -n: new partition # 格式: -n part_num:start_sector:end_sector cmd [SGDISK_CMD, -n, f{part_num}:{start_sec}:{end_sec}, device] result subprocess.run( cmd, stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: print(f [失败] {result.stderr.strip()}) return False else: print(f [成功]) except Exception as e: print(f [错误] {e}) return False return True def verify_partitions(device): 验证并打印最终分区表 print(f[步骤 3/3] 验证分区表...) try: result subprocess.run( [SGDISK_CMD, -p, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode 0: print(\n--- 当前磁盘分区结构 ---) print(result.stdout) print(------------------------) return True else: print(f验证失败: {result.stderr}) return False except Exception as e: print(f验证时出错: {e}) return False def main(): print(*50) print(GPT 自动分区工具) print(*50) check_root() check_dependencies() print(f目标设备: {DEVICE}) print(f定义文件: {INPUT_FILE}) print(f块大小: {BLOCK_SIZE} Bytes) print(-*50) # 1. 读取定义 partitions read_partition_definitions(INPUT_FILE) print(f已加载 {len(partitions)} 个分区定义。) # 确认操作 print(\n警告: 此操作将清除磁盘所有数据并重建分区表) confirm input(请输入 yes 继续: ) if confirm.lower() ! yes: print(操作已取消。) sys.exit(0) # 2. 清除磁盘 if not wipe_disk(DEVICE): print( aborting due to wipe failure.) sys.exit(1) # 3. 创建分区 if not create_partitions(DEVICE, partitions): print( aborting due to partition creation failure.) sys.exit(1) # 4. 验证 verify_partitions(DEVICE) print(\n所有操作完成。) if __name__ __main__: main()然后查看分区 lsblksdb 8:16 0 320G 0 disk├─sdb1 8:17 0 1.8G 0 part├─sdb2 8:18 0 8.4G 0 part├─sdb3 8:19 0 15.2G 0 part├─sdb4 8:20 0 10.2G 0 part├─sdb5 8:21 0 27.7G 0 part└─sdb6 8:22 0 29.2G 0 part然后使用pvcreate /dev/sdb1 ...组成vg 就可以正常使用了或者直接raid0组在一起也可以我这个硬盘最终能用的320-33297GTotal free space is 71011036 sectors (33.9 GiB)只损失了34G还行挂上去临时下载还是可以的rootpi2b4:~# sudo python3 3-gpt-part.pyGPT 自动分区工具目标设备: /dev/sdb定义文件: /root/large_gap_bad_blocks.txt块大小: 4096 Bytes--------------------------------------------------已加载 17 个分区定义。警告: 此操作将清除磁盘所有数据并重建分区表请输入 yes 继续: yes[步骤 1/3] 正在清除 /dev/sdb 上的现有分区表...分区表已清除。[步骤 2/3] 开始在 /dev/sdb 上创建 17 个分区...- 分区 1: 块 [484119-806934] 扇区 [3872952-6455472][成功]- 分区 2: 块 [1192898-3225565] 扇区 [9543184-25804520][成功]- 分区 3: 块 [4128213-7950113] 扇区 [33025704-63600904][成功]- 分区 4: 块 [8106785-10620977] 扇区 [64854280-84967816][成功]- 分区 5: 块 [11842308-18940744] 扇区 [94738464-151525952][成功]- 分区 6: 块 [19765257-27259688] 扇区 [158122056-218077504][成功]- 分区 7: 块 [27416360-28943204] 扇区 [219330880-231545632][成功]- 分区 8: 块 [29099876-31163871] 扇区 [232799008-249310968][成功]- 分区 9: 块 [31320543-33129883] 扇区 [250564344-265039064][成功]- 分区 10: 块 [35010872-36724873] 扇区 [280086976-293798984][成功]- 分区 11: 块 [37023802-59326067] 扇区 [296190416-474608536][成功]- 分区 12: 块 [59482739-61660948] 扇区 [475861912-493287584][成功]- 分区 13: 块 [62547259-62821821] 扇区 [500378072-502574568][成功]- 分区 14: 块 [62978493-64274660] 扇区 [503827944-514197280][成功]- 分区 15: 块 [65001743-65763271] 扇区 [520013944-526106168][成功]- 分区 16: 块 [65919943-66484623] 扇区 [527359544-531876984][成功]- 分区 17: 块 [66641295-78130006] 扇区 [533130360-625040048][成功][步骤 3/3] 验证分区表...--- 当前磁盘分区结构 ---Disk /dev/sdb: 625142448 sectors, 298.1 GiBModel: BIAZESector size (logical/physical): 512/512 bytesDisk identifier (GUID): 2A60937A-192D-4705-91B1-B67487E3B60CPartition table holds up to 128 entriesMain partition table begins at sector 2 and ends at sector 33First usable sector is 34, last usable sector is 625142414Partitions will be aligned on 2048-sector boundariesTotal free space is 71011036 sectors (33.9 GiB)Number Start (sector) End (sector) Size Code Name1 3872768 6455472 1.2 GiB 83002 9541632 25804520 7.8 GiB 83003 33024000 63600904 14.6 GiB 83004 64854016 84967816 9.6 GiB 83005 94738432 151525952 27.1 GiB 83006 158121984 218077504 28.6 GiB 83007 219330560 231545632 5.8 GiB 83008 232798208 249310968 7.9 GiB 83009 250562560 265039064 6.9 GiB 830010 280086528 293798984 6.5 GiB 830011 296189952 474608536 85.1 GiB 830012 475860992 493287584 8.3 GiB 830013 500377600 502574568 1.0 GiB 830014 503826432 514197280 4.9 GiB 830015 520013824 526106168 2.9 GiB 830016 527357952 531876984 2.2 GiB 830017 533129216 625040048 43.8 GiB 8300------------------------所有操作完成。rootpi2b4:~#
返回列表