浅谈Android4.4和Android L中的trim技术

来源:互联网 发布:电脑时间校对软件 编辑:程序博客网 时间:2024/05/17 01:55
1、trim是什么?

        一条ATA指令,由操作系统发送给SSD主控制器,告诉它哪些数据占有的地址是“无效的”。 其实就是操作系统将一部分的控制权交给了SSD主控制器,让操作系统与SSD主控制器有一个互动。因为闪存需要先擦除才能再次写入数据,要得到空闲的闪存空间,SSD必须复制所有“有效”页到新的“空白页”里,并且擦除旧块,即垃圾回收;避免了在操作系统要重新写入数据时, SSD才会知道哪些地址是无效的情况。这样就可以在适当的时机做最好的优化。 简而言之,这是一种能够让闪存长期工作后,也能维持高速度的技术。不少人长期使用安卓机后表示,安卓怎么就越用越卡,无论怎么刷系统清数据也没法回到刚买时的流畅度。这往往是由于长期使用后,闪存的垃圾回收效率大大降低,使得安卓机的I/O性能大跌造成的。安卓4.3支持Trim技术,能够大幅提升闪存垃圾回收效率,让闪存始终保持接近原始状态的高速度。  这样做既提高了“硬盘速度”, 同时也延长了SSD的使用寿命,垃圾回收!!!

2、调用trim整理NAND上碎片的两种方法

    1、挂载ext4分区时加上discard选项, 可以在fstab.qcom文件中查看相关定义。
        eg:fstab.qcom中有如下定义,
            /dev/block/bootdevice/by-name/system         /system      ext4    ro,barrier=1,discard                                wait
        其中的discard选项表示使用trim整理功能。
    2、使用fstrim命令,当设备各传感器满足一定条件下就会从MountService.java中调用语句 — — > mConnector.execute("fstrim", "dotrim")启动trim整理功能,该语句将直接调用到vold层执行fstrim 命令。
3、fstrim命令实现流程简单介绍

   3.1 Java Framework层
        Framework层启动fstrim整理磁盘anroid L 和android4.4版本有所不同,将分别介绍两种方案。
        
   3.1.1 android 4.4   

        可以分为两种方式,一是系统自我检测触发功能,二是手动输入命令直接触发功能。手动触发功能是USB连上手机后输入adb shell am idle-maintenance直接启动,frame

works/base/cmds/am/src/com/android/commands/am/Am.java中将注册com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE广播,在IdleMain

tenanceService.java下onReceive(...)将监听到广播,从而实现手动触发。下面重点介绍系统自我检测触发功能的相关流程,主流程如下:

        SystemServer.java -----> IdleMaintenanceService.java ----> MountService.java ----> NativeDaemonConnector.java
        1)SystemService.java中启动注册IdleMaintenaceServi
           
         try {             Slog.i(TAG, "IdleMaintenanceService");                     new IdleMaintenanceService(context, battery);         } catch (Throwable e) {             reportWtf("starting IdleMaintenanceService", e);         }

    
        2)IdleMaintenanceService.java中注册各种广播和触发条件,register(Handler handler)中注册各种广播,如电量变化,屏幕开关等。在onReceive中监听广播,当收到Intent.ACTION_BATTERY_CHANGED,Intent.ACTION_SCREEN_ON,Intent.ACTION_DREAMING_STOPPED等广播时调用updateIdleMaintenanceState函数判断是否满足trim整理条件,android4.4在系统满足如下条件的情况,通过FSTRIM ioctl的方式对device进行垃圾回收的处理,条件如下:   
            *设备已经闲置了至少一小时
            *在过去24小时内没有进行清理回收工作    
            *电池电量大于 30%(充电中)或者80%(未进行充电)
            代码如下:        
    private void updateIdleMaintenanceState(boolean noisy) {        ...        if (mIdleMaintenanceStarted) {            ...        } else if (deviceStatePermitsIdleMaintenanceStart(noisy)                       && 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        }     }

        3)在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");//接收到广播,发送trim命令                  EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());              } catch (NativeDaemonConnectorException ndce) {                  Slog.e(TAG, "Failed to run fstrim!");              }          }       }    }    

       4)通过NativeDameConnector.java中的Socket发送fstrim命令到vold层
 
    public NativeDaemonEvent execute(String cmd, Object... args)           throws NativeDaemonConnectorException {        final NativeDaemonEvent[] events = executeForList(cmd, args);//异步发送fstrim指令,“mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8))”        if (events.length != 1) {            throw new NativeDaemonConnectorException(            "Expected exactly one response, but received " + events.length);        }        return events[0];    }
       
   3.1.2 android L
        SystemServer.java -----> PackageManagerService.java ----> MountService.java ----> NativeDaemonConnector.java
        1)SystemServer.java run()调用startOtherServices(),startOtherServices()调用PackageManagerService.java中的mPackageManagerService.performBootDexOpt()方法。
        2)PackageManagerService.java performBootDexOpt()中判断是否满足trim整理的条件(在过去的三天没有进行垃圾回收工作),若满足则调用MountService中的runMaintenance()方法
    public void performBootDexOpt() {        ...        // Before everything else, see whether we need to fstrim.        try {            IMountService ms = PackageHelper.getMountService();            if (ms != null) {                final boolean isUpgrade = isUpgrade();                boolean doTrim = isUpgrade;                    if (doTrim) {                        Slog.w(TAG, "Running disk maintenance immediately due to system update");                    } else {                        final long interval = android.provider.Settings.Global.getLong(                        mContext.getContentResolver(),                        android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,                        DEFAULT_MANDATORY_FSTRIM_INTERVAL);                        if (interval > 0) {                            final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance();                            if (timeSinceLast > interval) {                                doTrim = true;                                Slog.w(TAG, "No disk maintenance in " + timeSinceLast  + "; running immediately");                            }                        }                    }                    if (doTrim) {                        if (!isFirstBoot()) {                            ...                        }                        ms.runMaintenance(); ---- > MountService                    }                } else {                    Slog.e(TAG, "Mount service unavailable!");                }            } catch (RemoteException e) {                // Can't happen; MountService is local            }        }    }

        3)MountService,通过mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback))发送消息,并在Handler中处理H_FSTRIM

    public void handleMessage(Message msg) {        switch (msg.what) {            ...        case H_FSTRIM:            waitForReady();            Slog.i(TAG, "Running fstrim idle maintenance");                                // Remember when we kicked it off            try {                mLastMaintenance = System.currentTimeMillis();                mLastMaintenanceFile.setLastModified(mLastMaintenance);             } catch (Exception e) {                Slog.e(TAG, "Unable to record last fstrim!");             }                                 try {                 // This method must be run on the main (handler) thread,                 // so it is safe to directly call into vold.                 mConnector.execute("fstrim", "dotrim");//发送trim命令                 EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());             } catch (NativeDaemonConnectorException ndce) {                 Slog.e(TAG, "Failed to run fstrim!");             }             ...             break;        }    }

            另外在MountService中也可以触发trim功能,通过mBootIPOReceiver和MountServiceHandler:case H_SYSTEM_READY里的handleSystemReady()触发该功能,handleSystemReady()中的MountServiceIdler.scheduleIdlePass(mContext)可触发,再看MountServiceIdle的代码,
   
    public static void scheduleIdlePass(Context context) {        JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);        Calendar calendar = tomorrowMidnight();//calendar.set(Calendar.HOUR_OF_DAY, 3)每天的三点        ...        tm.schedule(builder.build());//执行JobScheduler    }

            MountServiceIdle继承JobService,调用MountServiceIdle时会执行MountServiceIdle中的onStartJob()方法,在onStartJob()方法中通过ms.runIdleMaintenance(mFinishCallback) 触发trim功能。其实就是每次开机或者每天的3点空闲时间也将触发trim整理功能。
        4)可参照android4.4,两者一样。
        
   3.2 vold层
        CommandListener.cpp ----> fstrim.c
        1)CommandListener.cpp中int CommandListener::FstrimCmd::runCommand(SocketClient *cli, int argc, char **argv)函数中接收到fstrim命令后做处理
 
    int CommandListener::FstrimCmd::runCommand(SocketClient *cli, int argc, char **argv) {        ......        if (!strcmp(argv[1], "dotrim")) {            if (argc != 2) {                cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: fstrim dotrim", false);                return 0;            }            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);        }    }

        2)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);            }        }    }


1 0
原创粉丝点击