数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 4024|回复: 25

[Other] 关于固态存储设备TRIM功能的一些研究

[复制链接]
发表于 2024-1-7 21:32:22 来自手机浏览器 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
好久没发帖了,上号发现M币都给我扣成负数,还是发个贴赚点币吧
这里简单说说我对TRIM机制的研究,大伙随便看看,如果有哪里不对也请指出来。
下面这段是复制我博客的:

Trim是固态硬盘中的一种垃圾回收机制。百度上的废话这里就不复制粘贴了,说一说笔者的理解:
操作系统向固态硬盘发起Trim命令,将文件系统中无用块的信息发送给固态硬盘主控,主控收到命令后会在空闲时对无用块进行擦除。由于SSD的特性,如果一块空间中有垃圾数据,想要写新的数据进去就必须将垃圾数据先擦除后写入,经常进行Trim操作理论上可以改善磁盘的读写性能。
目前已知Windows和Linux都支持固态硬盘的Trim操作。

然后,由于Windows不开源嘛,就只能基于开源的Linux研究了
通过查看内核源码(6.1)中的文件系统相关驱动,以下文件系统支持Trim:
ext4、btrfs、ecryptfs、f2fs、fat、gfs2、hpfs、jfs、nilfs2、ntfs3、exfat、ocfs2、xfs

Linux下TRIM很简单,使用util-linux包中的fstrim工具即可:
  1. sudo fstrim -av
  2. /var/log: 137.7 MiB (144424960 bytes) trimmed on /dev/zram1
  3. /boot: 912.2 MiB (956493824 bytes) trimmed on /dev/mmcblk0p1
  4. /: 1.6 GiB (1746763776 bytes) trimmed on /dev/mmcblk0p2
复制代码

然后,楼下简单追一下Trim的实现:

打赏

参与人数 2家元 +96 收起 理由
perter + 6 原創內容
家睦 + 90

查看全部打赏

 楼主| 发表于 2024-1-7 21:36:04 | 显示全部楼层
先看fstrim源码:
  1. if (ioctl(fd, FITRIM, &range)) {
  2. switch (errno) {
  3. case EBADF:
  4. case ENOTTY:
  5. case EOPNOTSUPP:
  6. case ENOSYS:
  7. rc = 1;
  8. break;
  9. default:
  10. rc = -errno;
  11. }
  12. if (rc < 0)
  13. warn(_("%s: FITRIM ioctl failed"), path);
  14. goto done;
  15. }
复制代码


这里ioctl发送的是FITRIM命令,参数是一个结构体range:
  1. // /usr/include/linux/fs.h
  2. struct fstrim_range {
  3. __u64 start;
  4. __u64 len;
  5. __u64 minlen;
  6. };
复制代码

而在结构体传入之前,main函数已经将len设置为一个很大的值:
  1. struct fstrim_control ctl = {
  2. .range = { .len = ULLONG_MAX } // 真的很大
  3. };
复制代码


ioctl完成后,打印已经trim的字节数量的代码:
  1. if (ctl->verbose) {
  2. char *str = size_to_human_string(
  3. SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
  4. (uint64_t) range.len);
  5. if (devname)
  6. /* TRANSLATORS: The standard value here is a very large number. */
  7. printf(_("%s: %s (%" PRIu64 " bytes) trimmed on %s\n"),
  8. path, str, (uint64_t) range.len, devname);
  9. else
  10. /* TRANSLATORS: The standard value here is a very large number. */
  11. printf(_("%s: %s (%" PRIu64 " bytes) trimmed\n"),
  12. path, str, (uint64_t) range.len);

  13. free(str);
  14. }
复制代码


熟悉Linux的朋友知道,ioctl是和内核交互了,所以要追溯到内核层了。这里简单追一下ext4的实现:
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-8 08:15:39 | 显示全部楼层

关于固态存储设备TRIM功能的一些研究

  1. /**
复制代码
  1. * ext4_trim_fs() -- trim ioctl handle function
  2. * @sb: superblock for filesystem
  3. * @range: fstrim_range structure
  4. *
  5. * start: First Byte to trim
  6. * len: number of Bytes to trim from start
  7. * minlen: minimum extent length in Bytes
  8. * ext4_trim_fs goes through all allocation groups containing Bytes from
  9. * start to start+len. For each such a group ext4_trim_all_free function
  10. * is invoked to trim all free space.
  11. */
  12. int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range);
复制代码
有注释省得看代码了内核将指定(len)文件系统的区域进行了扫描,并且调用ext4_trim_all_free函数:
  1. /**
  2. * ext4_trim_all_free -- function to trim all free space in alloc. group
  3. * @sb: super block for file system
  4. * @group: group to be trimmed
  5. * @start: first group block to examine
  6. * @max: last group block to examine
  7. * @minblocks: minimum extent block count
  8. *
  9. * ext4_trim_all_free walks through group's buddy bitmap searching for free
  10. * extents. When the free block is found, ext4_trim_extent is called to TRIM
  11. * the extent.
  12. *
  13. *
  14. * ext4_trim_all_free walks through group's block bitmap searching for free
  15. * extents. When the free extent is found, mark it as used in group buddy
  16. * bitmap. Then issue a TRIM command on this extent and free the extent in
  17. * the group buddy bitmap. This is done until whole group is scanned.
  18. */
  19. static ext4_grpblk_t
  20. ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
  21. ext4_grpblk_t start, ext4_grpblk_t max,
  22. ext4_grpblk_t minblocks);
复制代码
文件系统将无用块标记,然后向磁盘发送trim命令来清除这些块的数据。楼主是mmc设备,所以追溯到mmc设备的驱动实现:
  1. static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
  2. {
  3. struct mmc_blk_data *md = mq->blkdata;
  4. struct mmc_card *card = md->queue.card;
  5. unsigned int from, nr;
  6. int err = 0, type = MMC_BLK_DISCARD;
  7. blk_status_t status = BLK_STS_OK;

  8. if (!mmc_can_erase(card)) {
  9. status = BLK_STS_NOTSUPP;
  10. goto fail;
  11. }

  12. from = blk_rq_pos(req);
  13. nr = blk_rq_sectors(req);

  14. do {
  15. unsigned int erase_arg = card->erase_arg;

  16. if (mmc_card_broken_sd_discard(card))
  17. erase_arg = SD_ERASE_ARG;

  18. err = 0;
  19. if (card->quirks & MMC_QUIRK_INAND_CMD38) {
  20. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
  21. INAND_CMD38_ARG_EXT_CSD,
  22. card->erase_arg == MMC_TRIM_ARG ?
  23. INAND_CMD38_ARG_TRIM :
  24. INAND_CMD38_ARG_ERASE,
  25. card->ext_csd.generic_cmd6_time);
  26. }
  27. if (!err)
  28. err = mmc_erase(card, from, nr, erase_arg);
  29. } while (err == -EIO && !mmc_blk_reset(md, card->host, type));
  30. if (err)
  31. status = BLK_STS_IOERR;
  32. else
  33. mmc_blk_reset_success(md, type);
  34. fail:
  35. blk_mq_end_request(req, status);
  36. }
复制代码
这里就可以发现是发送了INAND_CMD38_ARG_TRIM或者是INAND_CMD38_ARG_ERASE命令给mmc设备,然后mmc进入内置固件的TRIM处理逻辑。而固件这玩意儿又没有源码,所以最多只能追溯到这里了。

然后简单验证一下TRIM:
先随便搞一个二进制文件:
  1. dd if=/dev/urandom of=test.bin bs=1 count=16
  2. 输入了 16+0 块记录
  3. 输出了 16+0 块记录
  4. 16 字节已复制,0.000342409 s,46.7 kB/s
复制代码

生成了一个test.bin,用hexdump看一看内容:
  1. hexdump -C test.bin
  2. 00000000 3c 73 d1 0e 6f 23 54 d1 6d 51 68 88 c3 24 26 49 |<s..o#T.mQh..$&I|
  3. 00000010
复制代码
  1. sudo hdparm --fibmap test.bin
  2. test.bin:
  3. filesystem blocksize 4096, begins at LBA 2158592; assuming 512 byte sectors.
  4. byte_offset begin_LBA end_LBA sectors
  5. 0 6815240 6815247 8
复制代码
  1. hexdump -C --skip 3489402880 --length 16 /dev/mmcblk0
  2. cffc1000 3c 73 d1 0e 6f 23 54 d1 6d 51 68 88 c3 24 26 49 |<s..o#T.mQh..$&I|
  3. cffc1010
复制代码

再次使用hexdump读取相应位置的数据:
  1. hexdump -C --skip 3489402880 --length 16 /dev/mmcblk0
  2. cffc1000 3c 73 d1 0e 6f 23 54 d1 6d 51 68 88 c3 24 26 49 |<s..o#T.mQh..$&I|
  3. cffc1010
复制代码

可以看见,数据还在原位。大概是因为mmc设备的原因吧,我用手机测试,两小时就把删掉的文件块给TRIM了。
欢迎大家探讨

回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2024-1-9 20:18:11 来自手机浏览器 | 显示全部楼层
yongjie 发表于 2024-1-9 10:38
此处想艾特一下dongfangw 希望他发表一下意见  想听听他的更高的见解

这哥们咋了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-23 14:09:16 来自手机浏览器 | 显示全部楼层
perter 发表于 2024-1-21 15:00
给楼主点个赞,你这分析的应该是手动trim,现在SSD都支持CG+trim了,你不用管,很多主控做CG的时候就自行tr ...

大部分Linux系统都带一个叫fstrim.timer的服务,启用之后会每周定期执行我上面说的那个trim命令。。。其实我也很好奇这个自动trim,按理说(ext4)删掉文件实际上就是从某个块的inode节点上删掉了文件索引,需要支持自动trim的话那么那套固件里面就得做一个可以解析ext4 inode结构的实现。。。世界上这么多文件系统,岂不是fat fat32 exfat ntfs这些都得做好对应的数据结构解析实现放在固件里面。。。不大可能

我个人认为只靠磁盘里的固件是没办法实现自动trim的,因为固件不知道在nand flash里面文件系统的数据结构,所以没有办法自动清理无用块。然后像Windows那种碎片检查机制,我估计是操作系统闲了没事就把文件系统扫一遍(或者是每次删除的时候实际上在某个区域里面存了这个文件的信息),扫出无用块之后把这些信息发给SSD,SSD接收到之后再找时机进行擦除操作。

以上观点均为猜测哈,毕竟我也没有Windows源码 :lol:
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-24 19:35:54 来自手机浏览器 | 显示全部楼层
渣星海力士 发表于 2024-1-24 11:16
原理原来是这样,我之前测试过Windows10下硬盘盒主控对SSD数据恢复影响。
结论就是不支持trim的,在删除文 ...

格式化应该就是去掉了分区头部(和尾部?)的文件系统信息,不过我没看过源码,瞎猜的。
因为之前搞过一次数据恢复,放在文件夹下的东西拿工具一扫就能扫出来,而放在根目录下(也就是直接放在磁盘下)的文件扫不出来,只能winhex一点点抓文件头这样。。。所以我估计是,根目录下文件夹和文件的信息都存到了分区头部的文件系统信息区域内,然后格式化只是擦除了这个分区,并没有把整个磁盘所有记录文件结构(也就是inode)的块清掉,这样扫盘软件扫到这些还没有被删除的inode的时候可以顺着它还原出目录结构。
总之就是文件丢文件夹里面,哪天误格式化了能恢复出来的概率更大
回复 支持 1 反对 0

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-7-8 17:50 , Processed in 0.218401 second(s), 13 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表