深入理解 Linux 内核中的 FTL 算法 (linux 内核 ftl)

随着存储技术的不断发展,大家使用 Flash 存储设备的机会也愈加频繁。但是,很多人并不熟悉 Flash 存储设备的内部实现,其中一个重要的组成部分——FTL 算法却往往被忽略。在本文中,我们将深入探讨 Linux 内核中的 FTL 算法,为读者展示它的核心思想和实现细节。

一、什么是 FTL 算法?

FTL(Flash Translation Layer)是一种通过将逻辑地址映射到物理地址的算法,使得操作系统和应用程序能够使用 Flash 存储设备。由于 Flash 存储设备是由多个闪存芯片组成的,所以 FTL 算法需要发挥出其独特性,以实现对闪存芯片的管理和使用。

一般而言,FTL 算法在 SSD(Solid State Drive)或者 eMMC(embedded MultiMediaCard)中使用。FTL 算法会将闪存芯片递归地划分为块、页和子页等结构,并通过页表进行逻辑地址到物理地址的映射。FTL 的功能不仅仅是逻辑地址到物理地址的映射,还需要管理闪存芯片的空间分配以及垃圾回收的工作。

因此,FTL 算法不仅简化了应用程序、操作系统和闪存芯片之间的交互,还可以提高数据访问速度和闪存设备的寿命。

二、FTL 算法的核心思想

1. 预处理

FTL 算法的之一步是对原始数据进行处理,比如 ECC(Error Correction Code)、RLC(Run-Length Coding)和 Huffman 编码等。这些预处理步骤有助于提高数据的可靠性或者压缩效率。

2. 管理页表

FTL 算法需要建立页表,以将逻辑地址映射到物理地址。页表通常会分为三个级别:块、页和子页。该表需要记录每个物理页面的状态信息,包括“空闲”、“已用”、“坏块”等等。

3. 空间管理

FTL 算法需要管理闪存芯片的空间分配,以确保每个用户请求都可被满足。闪存芯片通常会被均匀地划分为多个“块”,每个块通常会被划分为数百至数千个“页”。FTL 算法会根据需要从这些块和页中分配空间,并在必要时执行垃圾回收操作。

4. 垃圾回收

FTL 算法需要管理闪存芯片上的垃圾数据,特别是闪存芯片中被删除的文件可能会变成垃圾数据。常见的垃圾回收策略包括标记-清除、标记-压缩和复杂垃圾回收等等。

三、FTL 算法的实现细节

Linux 内核中,最常见的 FTL 算法是 MTD(Memory Technology Device)层实现的“ftl-core”模块。这个模块是一个独立的 C 文件,需要将其编译为内核模块才能使用。ftl-core 实现了 Flash 存储设备的分区、寻址、读写和垃圾回收等功能。

ftl-core 核心代码比较简单,其实现主要分为三个模块:块管理、页管理和垃圾回收。从概念上来说,块是一组连续的页面,每个页面可以存储一定数量的数据。在 FTL 算法中,块是管理每个芯片上的逻辑空间并追踪哪些块已满或已损坏的基本单位。

页,则是构成块的基本单元。页用于存储用户数据,也包括 FTL 核心代码中的元数据(元数据是指用于管理闪存的信息,如页状态、块状态、页表等信息)。FTL 算法通过页来实现逻辑地址到物理地址的映射。

垃圾回收是 FTL 算法的必要部分,用于清除不再使用的页面以释放未用空间。在垃圾回收过程中,FTL 算法扫描每个块的页面,将有效页面复制到新块,丢弃旧块中的错误页面等。

四、

FTL 算法是 Flash 存储设备的重要组成部分,它通过将逻辑地址映射到物理地址的算法,实现存储设备的管理和使用。该算法被广泛应用于 SSD、eMMC 等闪存芯片中,同时也作为 Linux 内核中的一个模块进行实现。在 FTL 算法的实现中,空间管理和垃圾回收等功能是至关重要的。因此,在使用 Flash 存储设备时,深入理解 FTL 算法的核心思想和实现细节,对于提高系统性能和延长设备寿命都非常重要。

相关问题拓展阅读:

如何在flashsim上运行自己的ftl程序

首先进行准备工作:(基于disksim的FlashSim运行环境为linux,要求低版本的gcc编译器,且需要bison和flex脚本的支持。)

1、安装ubuntu操作系统

版本选择ubuntu10.04和ubuntu10.10均可(已试验成功)。

注意:一定不要在线安装,否则ubuntu安装的同时会在线更新,过程将会长达数小时!

安装过程:双击ubuntu-10.04-desktop-i386.iso(或ubuntu-10.10-desktop-i386.iso)压缩包,再双击图1中最下方的wubi.exe,会出肆仔答现解压界面。

解压完后进入ubuntu安装引导界面。

可选择windows下的任意一个驱动盘,但要保证空闲容量10G以上(越大越好,这样系统运行比较快,同时避免后期扩展应用导致容量不足)。右侧语言选择Chinese,用户名和密码自己设置。以上步骤完成后点击确定,即开始安装ubuntu系统,离线情况下此过程5分钟左右可完成。

Windows下安装完成后重启电脑,选择进入ubuntu系统,将自动完成后续安装。

至此,ubuntu系统成功完成安装,你可以进入图形界面或打开终端自由操作了~

2、安装GCC编译器

ubuntu 10.04中默认安装的gcc版本为gcc-4.*,而disksim需要安装低版本的gcc(ubuntu下gcc-2.95编译disksim确认可以通过,笔者在RHEL5下安装gcc-3.3.6,貌似也可准确编译disksim)。因为从源代码安装比较麻烦,所以选择下载.deb包进行安装。

将下列五个文件依次下载:

cpp-2.95_2.95.4-24_i386.deb;

gcc-2.95_2.95.4-24_i386.deb;

g++-2.95_2.95.4-24_i386.deb;

libstdc++2.10-glibc2.2_2.95.4-24_i386.deb ;

libstdc++2.10-dev_2.95.4-24_i386.deb

第4个包libstdc++2.10-glibc2.2_2.95.4-24_i386.deb ubuntu官网上找不到,需要自己去其他地方搜索下载。

下载完按如上顺序依次强制安装,终端命令为:

sudo dpkg –force-depends -i .deb

(注:若安装顺序不准确,终端会给出提示,调整顺序重新安装就ok~)

然后更改软连接,执行命令:

sudo ln -fs /usr/bin/gcc-2.95 /usr/bin/gcc

查看gcc版本:gcc –v ,可以看到版本为gcc-2.95.4 。

这样,当需要编译软件时,默认使用的gcc版本就是gcc-2.95.4 。

3、安ison和flax

可直接在线安装,终端命令分别为:

sudo apt-get install bison

sudo apt-get install flex

装完使用bison -v和flex –v可以看到版本信息为bison-2.4.3和flex-2.5.35(或flex-2.5.4)

接下来就正式进入flashsim的安装了。这个过程要注意,每次重新编译安装所戚迹用到的disksim3.0和flashsim的源码包都要选择最原始的压缩包,然后解压操作。

4、编译disksim-3.0

解压disksim-3.0.tar.gz,假设解压到当前用户主目录下,解压后文件名为disksim-3.0

whm@ubuntu:~$ cd disksim-3.0/#进入disksim-3.0目录

whm@ubuntu:~/disksim-3.0$ make#编译整个工程

如没有提示编译错误裂慧,则可以进入/disksim-3.0/valid目录下,运行runvalid文件。若能准确运行,说明disksim-3.0的编译是正确的。

5、在disksim-3.0基础上编译flashsim

(1)将flashsim-psu中的src.release.tar.gz压缩包解压生成src.release文件夹,将其拷入disksim-3.0目录下,更名为src,替代disksim-3.0中已有的src目录

(2)将flashsim-psu中的flashsim.patch文件拷入/disksim-3.0/src目录下,然后命令行进入src目录,为其中的某些文件打补丁。(为什么打补丁?因为之前的某些代码编写不够规范,可能导致编译通不过,flashsim.patch文件就是为了修正那些不规范的写法。)命令如下:

whm@ubuntu:~/disksim-3.0/src$ patch -p1 -i flashsim.patch

运行时可能提示patch 命令找不到之类的,这是因为ubuntu系统默认没有安装patch,需要先安装一下。命令为:

sudo apt-get install patch

Patch安装成功后再执行如上命令,一般可patch通过。如出现类似如下错误:

patching file fast.c

Hunk #1 FAILED at 58.

Hunk #2 FAILED at 266.

Hunk #3 FAILED at 294.

……

6 out of 6 hunks FAILED — saving rejects to file fast.c.rej

则表明从flashsim-psu中拷过来的src.release不是原始的版本。笔者刚开始因为不了解patch文件的真正含义,所以无意识中就犯了这个粗心的错误。因为之前阅读src.release中的代码时加了一些注释,导致行号和原来的不对应(行数有增加),而flashsim.patch文件的执行是严格按照行号来修订原始版本中的不规范代码,所以patch总是通不过。意识到问题症结所在后,使用重新解压得到的src.release,再执行patch操作,问题很快解决。

(3)patch通过之后,退回到disksim-3.0目录下,再次编译。

whm@ubuntu:~/disksim-3.0$ make

编译正确,至此flashsim的配置大功告成,可以运行其自带的一些FTL算法了~

如何交叉编译mkfs.jffs2等工具链mtd-utils

首先说明一下:

在YAFFS2源文件的帆银灶态扮utils目录下,执行make就可以生成 mkyaffs2image工具,执行

.(要制作yaffs2的目录) (目标镜像)/mkyaffs2image

acl_2.2.47.orig.tar.gz

lzo-2.03.tar.gz

mtd-utils_.orig.tar.gz

zlib-1.2.3.tar.gz

mkfs.jffs2.for.arm-linux-gcc.3.4.1平台.tar.bz2

mkfs.jffs2.for.arm-linux-gcc.4.3.2平台.tar.bz2

mkfs.jffs2.for.pc平台.tar.bz2

如果只需要mkfs.jffs2工具,那么ubuntu 8.10下直接安装jffnms软件包即可,

luther@gliethttp:~$ sudo apt-get install jffnms

如果需要将jffs2移植到arm开发板上,那么就需要下载源码进行交叉编译了,这就是本文的内容.

1.下载工具软件源码包

luther@gliethttp:~$ wget

luther@gliethttp:~$ wget

luther@gliethttp:~$ wget

luther@gliethttp:~$ mkdir libs 用来存放下面生成的lib库.

2.编译zlib库

luther@gliethttp:~/zlib-1.2.3$ ./configure –prefix=~/libs –shared

对于交叉编译输入如下指令

luther@gliethttp:~/zlib-1.2.3$ CC=arm-linux-gcc ./configure –prefix=~/libs –shared

luther@gliethttp:~/zlib-1.2.3$ make -j4

luther@gliethttp:~/zlib-1.2.3$ make install

luther@gliethttp:~$ tree ~/libs

/home/ubuntu/libs

|– include

| |– zconf.h

| `– zlib.h

|– lib

| |– libz.so -> libz.so.1.2.3

| |– libz.so.1 -> libz.so.1.2.3

| `– libz.so.1.2.3

`– share

`– man

`– man3

`– zlib.3

5 directories, 6 files

这就表示完成了.

3.编译lzo库

luther@gliethttp:~/lzo-2.03$ ./configure –prefix=/home/ubuntu/libs –enable-shared

对于交叉编译输入如下指令搏禅

luther@gliethttp:~/lzo-2.03$ CC=arm-linux-gcc ./configure –host=arm-linux –prefix=/home/ubuntu/libs –enable-shared –disable-static

这个还必须要绝对路径才行.

luther@gliethttp:~/lzo-2.03$ make

luther@gliethttp:~/lzo-2.03$ make install

luther@gliethttp:~$ tree ~/libs

.

|– include

| |– lzo

| | |– lzo1.h

| | |– lzo1a.h

| | |– lzo1b.h

| | |– lzo1c.h

| | |– lzo1f.h

| | |– lzo1x.h

| | |– lzo1y.h

| | |– lzo1z.h

| | |– lzo2a.h

| | |– lzo_a.h

| | |– lzoconf.h

| | |– lzodefs.h

| | `– lzoutil.h

| |– zconf.h

| `– zlib.h

|– lib

| |– liblzo2.a

| |– liblzo2.la

| |– liblzo2.so -> liblzo2.so.2.0.0

| |– liblzo2.so.2 -> liblzo2.so.2.0.0

| |– liblzo2.so.2.0.0

| `– libz.a

`– share

`– man

`– man3

`– zlib.3

6 directories, 22 files

手工将静态库删掉就行了,

如果是arm平台还需要strip优化.

4.编译mtd-utils前的准备工作.

编译之前的代码工作

luther@gliethttp:~$ wget

luther@gliethttp:~$ mkdir libs/include/sys -p

luther@gliethttp:~$ cp acl-2.2.47/include/acl.h libs/include/sys

luther@gliethttp:~/mtd-utils$ export LD_LIBRARY_PATH=~/libs/lib:$LD_LIBRARY_PATH

如果还找不到-llzo2,那么把他拷到/usr/lib下,对于交叉编译器,就是拷贝到

比如

luther@gliethttp:~/libs/lib$ sudo cp -a * /vobs/tools/arm-tools/arm-linux-gcc-3.4.1/arm-linux/lib/

luther@gliethttp:~/mtd-utils$ vim Makefile

修改安装路径

DESTDIR=.

SBINDIR=gliethttp/in

MANDIR=gliethttp/share/man

INCLUDEDIR=gliethttp/include

修改CFLAGS变量

CFLAGS := -I./include -I/home/ubuntu/libs/include $(OPTFLAGS)

如果是arm-linux-gcc定义为

CFLAGS := -I./include -I/home/ubuntu/libs/include -DAI_ADDRCONFIG=0x0020 $(OPTFLAGS)

来自/usr/include/netdb.h

luther@gliethttp:~/mtd-utils$ vim ubi-utils/Makefile

DESTDIR := ~/mtd-utils

SBINDIR=gliethttp/in

MANDIR=gliethttp/share/man

INCLUDEDIR=gliethttp/include

luther@gliethttp:~/mtd-utils$ vim recv_image.c

拷贝/usr/include/netinet/in.h文件中

arm-linux-gcc中不需要拷贝它.

struct ip_mreq

{

struct in_addr imr_multiaddr;

struct in_addr imr_interface;

};

结构体数据到头部,否则在u盘版的ubuntu 8.10上老是提示没有ip_mreq定义,虽然上面明明写了#define _USE_MISC

arm-linux-gcc中还需要创建如3下个目录

luther@gliethttp:~/mtd-utils$ mkdir arm-linux

luther@gliethttp:~/mtd-utils$ cp -r ubi-utils arm-linux/

luther@gliethttp:~/mtd-utils$ cp -r include arm-linux/

luther@gliethttp:~/mtd-utils$ vim ubi-utils/src/libpfiflash.c

将所有EBUF(PFIFLASH_ERRSTR);全部替换为EBUF(“%s”, PFIFLASH_ERRSTR);

vim下替换脚本为

:%s/EBUF(PFIFLASH_ERRSTR\);/EBUF(“\%s”, PFIFLASH_ERRSTR\);/g

luther@gliethttp:~/mtd-utils$ vim ubi-utils/src/ubimirror.c

将第206行的

fprintf(stderr, err_buf);

改为

fprintf(stderr, “%s”, err_buf); // 想法是好的,因为err_buf中含有%d等format信息,这样接口更加统一,但是编译器似乎还并不支持这样的操作.

luther@gliethttp:~/mtd-utils$ vim ubi-utils/src/unubi.c

将第898行

char fname;

改为

char fname;

luther@gliethttp:~/mtd-utils$ cd ubi-utils/new-utils

因为-O2优化的原因,会导致如下log信息

error: ignoring return value of ‘scanf’, declared with attribute warn_unused_result

所有手工先编译.o

luther@gliethttp:~/mtd-utils/ubi-utils/new-utils$ gcc -Iinclude -Isrc -I../../include -Wall -Werror -Wall src/ubiformat.c -c -o ubiformat.o

对于交叉编译执行如下1条语句

luther@gliethttp:~/mtd-utils/ubi-utils/new-utils$ arm-linux-gcc -Iinclude -Isrc -I../../include -Wall -Werror -Wall src/ubiformat.c -c -o ubiformat.o

luther@gliethttp:~/mtd-utils/ubi-utils/new-utils$ cd –

好了,上面的所有修改完成之后,就可以执行make成功编译了.

luther@gliethttp:~/mtd-utils$ make

如果是交叉编译,执行

luther@gliethttp:~/mtd-utils$ make CROSS=arm-linux-

luther@gliethttp:~/mtd-utils$ make install

对于交叉编译,执行

luther@gliethttp:~/mtd-utils$ make CROSS=arm-linux- install

luther@gliethttp:~/mtd-utils$ tree gliethttp/

gliethttp/

|– in

| |– bin2nand

| |– doc_loadbios

| |– docfdisk

| |– flash_erase

| |– flash_eraseall

| |– flash_info

| |– flash_lock

| |– flash_otp_dump

| |– flash_otp_info

| |– flash_unlock

| |– flashcp

| |– ftl_check

| |– ftl_format

| |– jffs2dump

| |– mkbootenv

| |– mkfs.jffs2

| |– mkpfi

| |– mtd_debug

| |– nand2bin

| |– nanddump

| |– nandtest

| |– nandwrite

| |– nftl_format

| |– nftldump

| |– pddcustomize

| |– pfi2bin

| |– pfiflash

| |– recv_image

| |– rfddump

| |– rfdformat

| |– serve_image

| |– sumtool

| |– ubiattach

| |– ubicrc32

| |– ubicrc32.pl

| |– ubidetach

| |– ubigen

| |– ubimirror

| |– ubimkvol

| |– ubinfo

| |– ubinize

| |– ubirmvol

| |– ubiupdatevol

| `– unubi

`– share

`– man

`– man1

`– mkfs.jffs2.1.gz

4 directories, 45 files

ep9312开发板上没有任何文件系统flash数据读取

# ./mtd_debug read /dev/mtdgliethttp.bin

Copied 100 bytes from address 0xin flash to gliethttp.bin

# hexdump gliethttp.bin

ff ea00 350c e59f 001c ee e3a0

e583 4a03 e3ae254 fffd 1aff

e3aef ee594

eeee594

eee3ae583

e3ae254 fffd 1aff 4e1e e3a0

e

#

# ./mtd_debug read /dev/mtdgliethttp.bin;hexdump gliethttp.bin -Cv

Copied 100 bytes from address 0xin flash to gliethttp.bin

f 8bca 14 7d 4ae4 5a 0fe7 |……}J…Z.p..|

f 3fba0 0e 38 cd|y.?I6.1A…8.Wp.|

8 f3 19 3ba de 22 c0 a4 |….;`…`..N”..|

b4 81 cad 0d 59 db 4d dd 60 |…B.m.Y.Ir.+M.`|

4 eaa d2 b2 4b 2e f|r…..!….K..Vv|

b7 34 0b 1d cd b1 1d 6b bdc 95 db d1 |c.4…..k..6….|

b 90 d1 ea|….|

笔记本电脑按住电源键强行关机,对电脑伤害大吗?

1、损坏磁盘

  强制关机最容易造成硬盘损坏,瞬间断电会导致硬盘的磁头无法复位,再次开机的时候需要寻找磁道,比如我们正在写文件,强制关机轻则导致文件丢失,重则硬盘损坏,因为磁头在正常关机时有很轻的”哒”一声,是复位声,而强行关机时这个声音就大很多。这也是笔记本上硬盘读写指示灯存在的作用之一伍此运。建议经常用软件查看硬盘的S.M.A.R.T.数据,了解其健康状况,以便保证数据安全。

  2、对系统产生影响

  强制关机会导致系统出现蓝屏,黑屏,错误代码等问题,强制关机出现上述错误推荐对系统进行还原一下,保证我们电脑正常运行。

  3、导致数据丢失

  强制关机会导致,我们电脑当前正在运行的软件部分数据丢失,比如我们办公文档,以及一些其它数据程序。还可能会导致软件的损坏,无法使用。

  4、烧坏部件

  强制关机会各种数据的冲突,会到机箱内部的部件产生高压电弧,对电脑部件一些造成影响,腔梁导致损坏。

  强制关机不会对没有运行的软件造成伤害,偶尔的强制关机也不会产生多大的影响,但对正在运行的软件可能会带来数据丢失,和系统数据丢失一样扒辩,后果将无法估量。

笔记本电脑按住电源键强行关机,对电脑有伤害吗?

这个问题很老了,有很多答案,但所有的答案都没有触及一个根本的问题。那就是按住电源键强制关机和拔电源关机并不一样,这点对笔记本和台式机都是如此。

按住电源键关机,在硬件上看就是拉住 PWRN# 引脚,保持一段时间。

在电源键按下后,也就是 PWRN# 被致高后,硬件会开启一个时钟 Timer,超过 5 秒还没有抬起,也就是 PWRN# 没有被制低,会触桥判发关机动作。

这个关机动作并不是切断电源了事,而是遵循普通的关机硬件流程。

大家要知道,实际上 CPU 和主板上有很多 power rail,关机并不是简简单单切断电源就好了,有携消弯复杂的关闭各个 power rail 的时序,要按照一定的顺序来,大家可以看看芯片手册了解一下,这里不再详述。

这时进入的 ACPI 模式是 G2,不是拔电源的 G3。

也就是说按住电源键关机,看起来像一下关闭电源了,实际上还是要经历一个完整的关机硬件流程,和普通的正常关机在硬件流程上没有什么不同。

和正常关机有什么不同?

硬件流程上一样,那么和普通关机在什么地方不同了呢?差别就是操作系统被跳过了。

无论我们是鼠标点选关机,还是按了一下电源键关机(详见:按下电源键后发生了什么?电脑是如何关机的?),操作系统也就是 Windows 或者 Linux 都会受到通知,尝试安全关机。

操作系统辩闷和固件在 ACPI 规范下共同工作,不同分工,一步步安全关闭计算机。包括保存文件等等操作。跳过这些动作轻则未保存文件丢失,重则操作系统文件损坏而不能启动。

结论

所以说按住电源键关机对硬件完全没有损伤,硬盘也不会一下掉电而要用到掉电保护的功能。

和正常关机一样。真正需要担忧的是数据的安全问题。

其他

话说回来拔电源或者家里如果停电会伤害硬件吗?

实际上机械硬盘并不需要担心,真正需要担心的是 SSD 和主板。看过我们固态硬盘系列的读者应该都知道 FTL 的存在(杂谈闪存三:FTL)。

FTL 的逻辑块和物理块的转换表必须存储在 NAND Flash 上,否则会造成混乱;高端企业级硬盘上有大量的 DRAM 来加速,其中的内容也必须存储下来。

在正常关电的时候,和机械硬盘类似,SATA 的 STANDBY IMMEDIATE 命令和 SCSI 的 Stop 指令是合适的存储这些信息的机会。SSD 控制器可以从容的将这些信息写在合适的地方。

在意外掉电的情况下,情况就复杂不少。不知道大家注意过没有,现在 M.2 的 SSD 上面有很多很小的电容:

在 Vcc 没电的时候,这些电容的 gate 就会打开,会为主控争取 1ms 左右的时间。主控会立刻放弃现在所有没有存储的数据,而立刻开始存储 FTL 表,保证不会出大问题。

在企业级的 SSD 中,有的 DRAM 大小将近 1GB,而企业 SSD 的数据完整性要求极高,不容有失,不但 FTL 表要存好,DRAM 中没有存储过的,也必须全部存下来。

这就需要很大的电容,来争取更多的时间,如果我们拆开企业 SSD,就会发现很多大黄快:

这个 Micron 的 SSD 有 3 块,而 Intel 的部分 SSD,因为 DRAM 很大,大电容达到 8 块!

有些低端 SSD 厂商,没有电容,或者在长时间使用后电容能力下降。在掉电后,FTL 表并没有存下来。

而是采取在下一次上电的时候重建 FTL 表,这是用户就会发现 SSD 反应很慢,要一会才会恢复正常。SSD 固件写的不好的,SSD 不能用也是可能的。

对主板的伤害主要在于掉电前后的电涌和电流波动,不好的电源 + 劣质主板有可能损坏主板。这样是我推荐电源一定要选个好的原因之一。

强制关机可能导致硬盘损坏,丢失硬盘数据。

没有影响 “长按电源键强制关机对电脑硬件没有影响。

linux 内核 ftl的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux 内核 ftl,深入理解 Linux 内核中的 FTL 算法,如何在flashsim上运行自己的ftl程序,如何交叉编译mkfs.jffs2等工具链mtd-utils,笔记本电脑按住电源键强行关机,对电脑伤害大吗?的信息别忘了在本站进行查找喔。


数据运维技术 » 深入理解 Linux 内核中的 FTL 算法 (linux 内核 ftl)