PackageManagerService

五个阶段:

(1)BOOT_PROGRESS_PMS_START

(2)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

(3)BOOT_PROGRESS_PMS_DATA_SCAN_START

(4)BOOT_PROGRESS_PMS_SCAN_END

(5)BOOT_PROGRESS_PMS_READY

1)BOOT_PROGRESS_PMS_START

工作内容:创建所需的服务,本地服务,多用户管理,加载installer。然后将各种系统标志的uid写入settings中。注册权限改变监听器。

if (mSdkVersion <= 0) {Slog.w(TAG, "**** ro.build.version.sdk not set!");}Slog.w(TAG, " run in PackageManagerService");mContext = context;mFactoryTest = factoryTest;//工厂模式mOnlyCore = onlyCore;//加密模式mMetrics = new DisplayMetrics();//像素mInstaller = installer;//installer服务// Create sub-components that provide services / data. Order here is important.synchronized (mInstallLock) {synchronized (mPackages) {// Expose private service for system components to use.LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());//本地服务sUserManager = new UserManagerService(context, this,new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);//多用户管理mComponentResolver = new ComponentResolver(sUserManager,LocalServices.getService(PackageManagerInternal.class),mPackages);mPermissionManager = PermissionManagerService.create(context,mPackages /*externalLock*/);mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();//权限mSettings = new Settings(Environment.getDataDirectory(),mPermissionManager.getPermissionSettings(), mPackages);//settings环境}}mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);......mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);//将各种UID写入。。。String separateProcesses = SystemProperties.get("debug.separate_processes");if (separateProcesses != null && separateProcesses.length() > 0) {if ("*".equals(separateProcesses)) {mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;mSeparateProcesses = null;Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");} else {mDefParseFlags = 0;mSeparateProcesses = separateProcesses.split(",");Slog.w(TAG, "Running with debug.separate_processes: "+ separateProcesses);}} else {mDefParseFlags = 0;//走这mSeparateProcesses = null;}mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,"*dexopt*");mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());//权限监听getDefaultDisplayMetrics(context, mMetrics);//获取默认的显示

packages-backup.xml是否存在。存在,检查packages.xml是否存在,删除packages.xml,然后清除xml的list,检查packages-backup.xml的句柄是否存在,存在删除packages.xml,然后创建,将packages-backup.xml 复制到packages.xml,解析xml文件的内容

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");SystemConfig systemConfig = SystemConfig.getInstance();mAvailableFeatures = systemConfig.getAvailableFeatures();//。。。。Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);mProtectedPackages = new ProtectedPackages(mContext);mApexManager = new ApexManager(context);synchronized (mInstallLock) {// writersynchronized (mPackages) {mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());mProcessLoggingHandler = new ProcessLoggingHandler();Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);mInstantAppRegistry = new InstantAppRegistry(this);ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig= systemConfig.getSharedLibraries();//加载共享lib文件final int builtInLibCount = libConfig.size();for (int i = 0; i < builtInLibCount; i++) {String name = libConfig.keyAt(i);SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);addBuiltInSharedLibraryLocked(entry.filename, name);}// Now that we have added all the libraries, iterate again to add dependency// information IFF their dependencies are added.long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;for (int i = 0; i < builtInLibCount; i++) {String name = libConfig.keyAt(i);SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);final int dependencyCount = entry.dependencies.length;for (int j = 0; j < dependencyCount; j++) {final SharedLibraryInfo dependency =getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);if (dependency != null) {getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);}}}SELinuxMMAC.readInstallPolicy();//读取安装权限位于vendor system product *permissions.xmlTrace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");FallbackCategoryProvider.loadFallbacks();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));mSettings.readmPreInstalledAPKStatus();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);// Clean up orphaned packages for which the code path doesn't exist// and they are an update to a system app - caused by bug/32321269final int packageSettingCount = mSettings.mPackages.size();for (int i = packageSettingCount - 1; i >= 0; i--) {PackageSetting ps = mSettings.mPackages.valueAt(i);if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {mSettings.mPackages.removeAt(i);mSettings.enableSystemPackageLPw(ps.name);}}if (!mOnlyCore && mFirstBoot) {requestCopyPreoptedFiles();}String customResolverActivityName = Resources.getSystem().getString(R.string.config_customResolverActivity);if (!TextUtils.isEmpty(customResolverActivityName)) {mCustomResolverComponentName = ComponentName.unflattenFromString(customResolverActivityName);}

2)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

工作内容:检查各种系统相关属性,环境变量,目录,sdk版本,准备解析器,然后扫描各个目录下apk,对androidManifest.xml进行解析。如果未进行加密,删除不存在的系统软件包,对disable列表的app进行处理。删除临时文件。

           EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,startTime);final String bootClassPath = System.getenv("BOOTCLASSPATH");final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");if (bootClassPath == null) {Slog.w(TAG, "No BOOTCLASSPATH found!");}if (systemServerClassPath == null) {Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");}File frameworkDir = new File(Environment.getRootDirectory(), "framework");final VersionInfo ver = mSettings.getInternalVersion();mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);if (mIsUpgrade) {logCriticalInfo(Log.INFO,"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);}// when upgrading from pre-M, promote system app permissions from install to runtimemPromoteSystemApps =mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;// When upgrading from pre-N, we need to handle package extraction like first boot,// as there is no profiling data available.mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;appList_Pre = mContext.getResources().getStringArray(com.android.internal.R.array.PreInstalled_Apks);int preUpgradeSdkVersion = ver.sdkVersion;// save off the names of pre-existing system packages prior to scanning; we don't// want to automatically grant runtime permissions for new system appsif (mPromoteSystemApps) {Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();while (pkgSettingIter.hasNext()) {PackageSetting ps = pkgSettingIter.next();if (isSystemApp(ps)) {mExistingSystemPackages.add(ps.name);}}}mCacheDir = preparePackageParserCache();// Set flag to monitor and not change apk file paths when// scanning install directories.int scanFlags = SCAN_BOOTING | SCAN_INITIAL;if (mIsUpgrade || mFirstBoot) {scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;}// Collect vendor/product/product_services overlay packages. (Do this before scanning// any apps.)// For security and version matching reason, only consider overlay packages if they// reside in the right directory.scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),mDefParseFlags| PackageParser.PARSE_IS_SYSTEM_DIR,scanFlags| SCAN_AS_SYSTEM| SCAN_AS_VENDOR,0);......scanDirTracedLI(productServicesAppDir,mDefParseFlags| PackageParser.PARSE_IS_SYSTEM_DIR,scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES,0);// Prune any system packages that no longer exist.final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();// Stub packages must either be replaced with full versions in the /data// partition or be disabled.final List<String> stubSystemApps = new ArrayList<>();if (!mOnlyCore) {// do this first before mucking with mPackages for the "expecting better" casefinal Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();while (pkgIterator.hasNext()) {final PackageParser.Package pkg = pkgIterator.next();if (pkg.isStub) {stubSystemApps.add(pkg.packageName);}}final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();while (psit.hasNext()) {PackageSetting ps = psit.next();/** If this is not a system app, it can't be a* disable system app.*/if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}/** If the package is scanned, it's not erased.*/final PackageParser.Package scannedPkg = mPackages.get(ps.name);if (scannedPkg != null) {/** If the system app is both scanned and in the* disabled packages list, then it must have been* added via OTA. Remove it from the currently* scanned package so the previously user-installed* application can be scanned.*/if (mSettings.isDisabledSystemPackageLPr(ps.name)) {logCriticalInfo(Log.WARN,"Expecting better updated system app for " + ps.name+ "; removing system app.  Last known"+ " codePath=" + ps.codePathString+ ", versionCode=" + ps.versionCode+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());removePackageLI(scannedPkg, true);mExpectingBetter.put(ps.name, ps.codePath);}continue;}if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {psit.remove();logCriticalInfo(Log.WARN, "System package " + ps.name+ " no longer exists; it's data will be wiped");// Actual deletion of code and data will be handled by later// reconciliation step} else {// we still have a disabled system package, but, it still might have// been removed. check the code path still exists and check there's// still a package. the latter can happen if an OTA keeps the same// code path, but, changes the package name.final PackageSetting disabledPs =mSettings.getDisabledSystemPkgLPr(ps.name);if (disabledPs.codePath == null || !disabledPs.codePath.exists()|| disabledPs.pkg == null) {possiblyDeletedUpdatedSystemApps.add(ps.name);} else {// We're expecting that the system app should remain disabled, but add// it to expecting better to recover in case the data version cannot// be scanned.mExpectingBetter.put(disabledPs.name, disabledPs.codePath);}}}}//delete tmp filesdeleteTempPackageFiles();final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();// Remove any shared userIDs that have no associated packagesmSettings.pruneSharedUsersLPw();final long systemScanTime = SystemClock.uptimeMillis() - startTime;final int systemPackagesCount = mPackages.size();Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime+ " ms, packageCount: " + systemPackagesCount+ " , timePerPackage: "+ (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)+ " , cached: " + cachedSystemApps);if (mIsUpgrade && systemPackagesCount > 0) {MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",((int) systemScanTime) / systemPackagesCount);}

3)BOOT_PROGRESS_PMS_DATA_SCAN_START

工作内容:当处于普通模式时,对data目录进行处理,

           if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);// Remove disable package settings for updated system apps that were// removed via an OTA. If the update is no longer present, remove the// app completely. Otherwise, revoke their system privileges.//删除禁止列表apk的数据for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {final String packageName = possiblyDeletedUpdatedSystemApps.get(i);final PackageParser.Package pkg = mPackages.get(packageName);final String msg;// remove from the disabled system list; do this first so any future// scans of this package are performed without this statemSettings.removeDisabledSystemPackageLPw(packageName);if (pkg == null) {// should have found an update, but, we didn't; remove everythingmsg = "Updated system package " + packageName+ " no longer exists; removing its data";// Actual deletion of code and data will be handled by later// reconciliation step} else {// found an update; revoke system privilegesmsg = "Updated system package " + packageName+ " no longer exists; rescanning package on data";// NOTE: We don't do anything special if a stub is removed from the// system image. But, if we were [like removing the uncompressed// version from the /data partition], this is where it'd be done.// remove the package from the system and re-scan it without any// special privilegesremovePackageLI(pkg, true);try {final File codePath = new File(pkg.applicationInfo.getCodePath());scanPackageTracedLI(codePath, 0, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse updated, ex-system package: "+ e.getMessage());}}// one final check. if we still have a package setting [ie. it was// previously scanned and known to the system], but, we don't have// a package [ie. there was an error scanning it from the /data// partition], completely remove the package data.final PackageSetting ps = mSettings.mPackages.get(packageName);if (ps != null && mPackages.get(packageName) == null) {removePackageDataLIF(ps, null, null, 0, false);}logCriticalInfo(Log.WARN, msg);}/** Make sure all system apps that we expected to appear on* the userdata partition actually showed up. If they never* appeared, crawl back and revive the system version.*/for (int i = 0; i < mExpectingBetter.size(); i++) {final String packageName = mExpectingBetter.keyAt(i);if (!mPackages.containsKey(packageName)) {final File scanFile = mExpectingBetter.valueAt(i);logCriticalInfo(Log.WARN, "Expected better " + packageName+ " but never showed up; reverting to system");final @ParseFlags int reparseFlags;final @ScanFlags int rescanFlags;if (FileUtils.contains(privilegedAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(systemAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM;} else if (FileUtils.contains(privilegedVendorAppDir, scanFile)|| FileUtils.contains(privilegedOdmAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_VENDOR| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(vendorAppDir, scanFile)|| FileUtils.contains(odmAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags//| SCAN_AS_SYSTEM| SCAN_AS_VENDOR;} else if (FileUtils.contains(oemAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_OEM;} else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(productAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT;} else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES| SCAN_AS_PRIVILEGED;} else if (FileUtils.contains(productServicesAppDir, scanFile)) {reparseFlags =mDefParseFlags |PackageParser.PARSE_IS_SYSTEM_DIR;rescanFlags =scanFlags| SCAN_AS_SYSTEM| SCAN_AS_PRODUCT_SERVICES;} else {Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);continue;}mSettings.enableSystemPackageLPw(packageName);try {scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}// Uncompress and install any stubbed system applications.// This must be done last to ensure all stubs are replaced or disabled.installSystemStubPackages(stubSystemApps, scanFlags);final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()- cachedSystemApps;final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;final int dataPackagesCount = mPackages.size() - systemPackagesCount;Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime+ " ms, packageCount: " + dataPackagesCount+ " , timePerPackage: "+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)+ " , cached: " + cachedNonSystemApps);if (mIsUpgrade && dataPackagesCount > 0) {MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",((int) dataScanTime) / dataPackagesCount);}}mExpectingBetter.clear();

4)BOOT_PROGRESS_PMS_SCAN_END

PackageManagerService相关推荐

  1. PackageManagerService详解

    2019独角兽企业重金招聘Python工程师标准>>> 本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在 apk 安装时也会被调用.包管理服务启动时主要做的工作大致有 ...

  2. 安卓高手之路之PackageManagerservice

    源码位置:frameworks/base/core/java/android/content/pm/PackageParser.java 源文件路径:android\frameworks\base\s ...

  3. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  4. Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

    摘要:PackageManagerService在systemReady()后,进行了/system/etc/permissions中的各种xml进行扫描,进行相应的权限存储,供以后使用 阅读本文大约 ...

  5. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  6. android7.1 动态申请权限改为默认授权,修改PackageManagerService.java下的grantPermissions为true

    系统默认情况下,如果需要访问external storage.audio record权限,需要动态申请,对话框举例如下: 如果不需要弹出此对话框来手动授予权限,而是默认授权,可修改framework ...

  7. Android system server之PackageManagerService详细分析

    概要 本篇主要分析了系统启动阶段包管理服务的启动流程,其中的几个接口在 apk 安装时也会被调用.包管理服务启动时主要做的工作大致有如下几方面: 1. 建立 java 层的 installer 与 c ...

  8. PackageManagerService启动过程

    1.PackageManagerService服务启动过程:[Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中] public fi ...

  9. Android PackageManagerService分析三:卸载APK

    为什么80%的码农都做不了架构师?>>>    这一章我们介绍APK的卸载过程,从前一章分析安装APK的过程,我们应该大致了解这里的卸载的过程如下: 1.从PMS的内部结构上删除ac ...

  10. PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析

    文章目录 前言 1. PMS 2. 源码和关键方法 SystemServer PackageManagerService ParallelPackageParser PackageParser 3. ...

最新文章

  1. AI产业智能化白皮书 | 清华x百度:全面解读AI产业化的现在和未来(附下载)...
  2. SQLServer2000同步复制技术实现步骤
  3. 为什么老编辑器Vim这么难用,却很受欢迎?
  4. c++ 多重背包状态转移方程_串讲:控制理论:全状态反馈控制(FSFB)
  5. 【Unity3D与23种设计模式】模板方法模式(Template Method)
  6. web.xml文件报红,怎么解决???
  7. mysql cluster双机_GitHub - sophys/mysqlha: 博客“Mysql-cluster数据库集群双机HA研究”测试代码...
  8. 【杭州云栖】边缘计算ENS:拓展云的边界
  9. 近100个Spring/SpringBoot常用注解汇总!
  10. bootstrap-fileinput 简单使用
  11. 从SAP客户主数据里直接创建商机(Opportunity) 1
  12. vue.js多页面开发 webpack.config.js 配置方式
  13. java反编译 mac_java反编译工具Mac版-jd-gui for Mac下载 V1.6.6-PC6苹果网
  14. 命令提示符死亡之ping教程
  15. big code: Toward Deep Learning Software Repositories [MSR 2015]
  16. win10照片查看器_解决“Windows照片查看器无法显示此图片,因为计算机上的可用内存可能不足……”...
  17. meld的使用 Ubuntu入门之六
  18. 【寻找佳慧】“第一关”——无所不能的矩阵(java实现代码)
  19. 假设 A 类有如下定义,设 a 是 A 类的一个实例,下列语句调用哪个是错误的?()
  20. 电子表整点报时怎么取消_歪果仁怎么说“我被放鸽子了”?这可跟鸽子没关系哦...

热门文章

  1. Python序列 数据类型 创建方式 Tuple元组 Str字符串 List列表 dict字典 Set集合 range,zip,map,enumerate
  2. 深聊性能测试,从入门到放弃之:如何对IO进行性能调优
  3. 论文中的定理(Theorem)、引理(Lemma)、推论(Corollary)
  4. DB2 PC服务器本地磁盘集群部署模式简介
  5. 柠檬班的课程怎么样,来自一个金融行业转行到软件测试行业的故事
  6. MOS管热设计及发热分析详解
  7. 阿里云镜像服务 vpc地址 无法 pull
  8. Python 基于modbus tcp 协议 实现与plc通信
  9. OC的单例模式的实现
  10. EasyPoi Excel简单导出导入