您的位置:首页 > 移动开发 > Android开发

emmc FITRIM/discard命令在android系统中提升write performance的运用

2015-08-11 13:37 323 查看
在学习android文件系统时接触到discard和FITRIM这两个概念,发现android L上没有用到文件系统这两个功能,而android kk上有网络文章提到这两个功能的实现,下面就是我转载的一篇关于这两个概念的文章。

转摘于 http://blog.csdn.net/fybon/article/details/30232681
SSD/emmc 存储设备discard命令的使用意义及使用方法,请参考xfs官网上的这篇文章,描述详细。

http://xfs.org/index.php/FITRIM/discard

FITRIM/discard我的理解是:discard命令,只是将page标识为dirty状态,并未做真正的block erase操作。

而当一个block大部份page都标识为dirty时,为重新利用该block,就得将数据有效page(none dirty page)迁移到别的block,同时整个block擦除掉。

这个过程我们称之为:垃圾页回收。============》FITRIM就是做此工作的。

为什么它能提升emmc write performance 呢?

因为nand读、写规则是: 以page为读、写单元,而以block作为擦除单元。 写一个已经写过的page之前,必须将该页所在的block先擦除。

那么如果能在系统空闲时将一些垃圾页提前回收,并将block预先擦除作为写的准备。当然可以提升write performance,同时也可以提高page利用率。

下面我重点介绍disdcard在android系统中的实时使用和批量使用方式:


Realtime discard

init.rc 可以看到mount时,就带了disdcard flag ,这样kernel emmc /block 就会做很多不同的处理。
注:kernel需3.0以后版本。

mount ext4 /emmc@usrdata /data noatime nosuid nodev wait noauto_da_alloc,discard


Batch Mode

android4.4 在系统满足如下条件的情况,通过FSTRIM ioctl的方式对device进行垃圾回收的处理,条件如下:

1)设备已经闲置了至少一小时;

2)在过去24小时内没有进行清理回收工作;

3)电池电量大于 30%(充电中)或者80%(未进行充电)。

具体代码分析如下;

IdleMaintenanceService.java

private void updateIdleMaintenanceState(boolean noisy) {

if (mIdleMaintenanceStarted) {

// Idle maintenance can be interrupted by user activity, or duration

// time out, or low battery.

if (!lastUserActivityPermitsIdleMaintenanceRunning()

|| !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {

unscheduleUpdateIdleMaintenanceState();

mIdleMaintenanceStarted = false;

EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),

mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),

isBatteryCharging() ? 1 : 0);

sendIdleMaintenanceEndIntent();

// We stopped since we don't have enough battery or timed out but the

// user is not using the device, so we should be able to run maintenance

// in the next maintenance window since the battery may be charged

// without interaction and the min interval between maintenances passed.

if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {

scheduleUpdateIdleMaintenanceState(

getNextIdleMaintenanceIntervalStartFromNow());

}

}

} else if (deviceStatePermitsIdleMaintenanceStart(noisy)         //  FITRIM 执行条件判断          

&& lastUserActivityPermitsIdleMaintenanceStart(noisy)

&& lastRunPermitsIdleMaintenanceStart(noisy)) {

// Now that we started idle maintenance, we should schedule another

// update for the moment when the idle maintenance times out.

scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);

mIdleMaintenanceStarted = true;

EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),

mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),

isBatteryCharging() ? 1 : 0);

mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();

sendIdleMaintenanceStartIntent();                  // 条件满足 发送广播给mountservice.java   

} else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {

if (lastRunPermitsIdleMaintenanceStart(noisy)) {

// The user does not use the device and we did not run maintenance in more

// than the min interval between runs, so schedule an update - maybe the

// battery will be charged latter.

scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);

} else {

// The user does not use the device but we have run maintenance in the min

// interval between runs, so schedule an update after the min interval ends.

scheduleUpdateIdleMaintenanceState(

getNextIdleMaintenanceIntervalStartFromNow());

}

}

}

MountService.java

private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

waitForReady();

String action = intent.getAction();

// Since fstrim will be run on a daily basis we do not expect

// fstrim to be too long, so it is not interruptible. We will

// implement interruption only in case we see issues.

if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {

try {

// This method runs on the handler thread,

// so it is safe to directly call into vold.

mConnector.execute("fstrim", "dotrim");                   //  发送fstrim命令

EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());

} catch (NativeDaemonConnectorException ndce) {

Slog.e(TAG, "Failed to run fstrim!");

}

}

}

};

CommandListener.cpp

CommandListener::FstrimCmd::FstrimCmd() :

VoldCommand("fstrim") {

}

int CommandListener::FstrimCmd::runCommand(SocketClient *cli,

int argc, char **argv) {

。。。。。。。

if (!strcmp(argv[1], "dotrim")) {

dumpArgs(argc, argv, -1);

rc = fstrim_filesystems(); //接收到fstrim命令后,调用fstrim.c 中的fstrim_filesystems()

} else {

dumpArgs(argc, argv, -1);

cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown fstrim cmd", false);

}

fstrim.c

static void *do_fstrim_filesystems(void *ignored)

{

int i;

int fd;

int ret = 0;

struct fstrim_range range = { 0 };

struct stat sb;

extern struct fstab *fstab;

SLOGI("Starting fstrim work...\n");

/* Log the start time in the event log */

LOG_EVENT_LONG(LOG_FSTRIM_START, get_boot_time_ms());

for (i = 0; i < fstab->num_entries; i++) {

 。。。。。。

fd = open(fstab->recs[i].mount_point, O_RDONLY);

if (fd < 0) {

SLOGE("Cannot open %s for FITRIM\n", fstab->recs[i].mount_point);

ret = -1;

continue;

}

memset(&range, 0, sizeof(range));

range.len = ULLONG_MAX;         // 指定fitrim的范围是整个emmc

SLOGI("Invoking FITRIM ioctl on %s", fstab->recs[i].mount_point);

if (ioctl(fd, FITRIM, &range)) {      // 调用特定文件系统中的FITRIM ioctl,执行真正的垃圾回收处理。

  。。。。。。

close(fd);

}

 。。。。。。。。。。。。。

}

好的,看到这里,垃圾页回收整个过程基本清楚了。

我们可以看到fstrim是需要文件系统支持的,比如,kernel 3.4版本只有如下几种文件系统支持fstrim。!

ioctl.c (\android\kernel\fs\btrfs): case FITRIM:

ioctl.c (\android\kernel\fs\ext3): case FITRIM: {

ioctl.c (\android\kernel\fs\ext4): case FITRIM:

ioctl.c (\android\kernel\fs\ext4): "FITRIM not supported with bigalloc");

ioctl.c (\android\kernel\fs\ext4): case FITRIM:

ioctl.c (\android\kernel\fs\ocfs2): case FITRIM:

ioctl.c (\android\kernel\fs\ocfs2): case FITRIM:

xfs_ioctl.c (\android\kernel\fs\xfs): case FITRIM:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: