Android 启动时应用的安装解析过程《一》

应用对于Android系统来说至关重要,系统会有几个时机对APP进行解析,一个是APK安装的时候会进行解析,还有一个就是系统在重启之后会进行解析,这里就简单的记录一下重启的时候APK的解析过程。

一、SystemServer

系统在启动之后从内核层启动第一个用户控件Init进程,再通过Init进程启动系统中第一个java 进程zygote,随之zygote fock出SystemServer,SystemServer再开始启动一些列系统服务,其中就包括PackageMangerService,在startBootstrapServices函数中:

public final void startBootstrapServices(TimingsTraceAndSlog timingsTraceAndSlog) {
.....// 先调用了PackageManagerService的main函数Pair<PackageManagerService, IPackageManager> main = PackageManagerService.main(context, installer, domainVerificationService, z, this.mOnlyCore);// main 函数创建了PackageManagerService 和IPackageManager 的实例this.mPackageManagerService = (PackageManagerService) main.first;IPackageManager iPackageManager = (IPackageManager) main.second;Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");// 该方法将在 BaseDexClassLoader 中安装一个报告器,同时还将强制报告系统服务器已加载的任何 dex 文件SystemServerDexLoadReporter.configureSystemServerDexReporter(iPackageManager);this.mFirstBoot = this.mPackageManagerService.isFirstBoot();this.mPackageManager = this.mSystemContext.getPackageManager();

二、PackageManagerService

在SystemServer中是先调用PackageManagerService的main函数来初始化它自己的,来看看它做了什么:

public static Pair<PackageManagerService, IPackageManager> main(Context context,Installer installer, @NonNull DomainVerificationService domainVerificationService,boolean factoryTest, boolean onlyCore) {PackageManagerServiceCompilerMapping.checkProperties();final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",Trace.TRACE_TAG_PACKAGE_MANAGER);t.traceBegin("create package manager");final PackageManagerTracedLock lock = new PackageManagerTracedLock();final Object installLock = new Object();// 创建自带handler的线程HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);backgroundThread.start();Handler backgroundHandler = new Handler(backgroundThread.getLooper());// 创建PackageManagerServiceInjector 用来持有管理其他一系列重要的类实例PackageManagerServiceInjector injector = new PackageManagerServiceInjector(context, lock, installer, installLock, new PackageAbiHelperImpl(),backgroundHandler,SYSTEM_PARTITIONS,// 解析compnent的(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),// 权限管理服务(i, pm) -> PermissionManagerService.create(context,i.getSystemConfig().getAvailableFeatures()),// 用户管理服务(i, pm) -> new UserManagerService(context, pm,new UserDataPreparer(installer, installLock, context, onlyCore),lock),// 系统数据库,用来访问系统数据库的(i, pm) -> new Settings(Environment.getDataDirectory(),RuntimePermissionsPersistence.createInstance(),i.getPermissionManagerServiceInternal(),domainVerificationService, backgroundHandler, lock),(i, pm) -> AppsFilterImpl.create(i,i.getLocalService(PackageManagerInternal.class)),// compat服务用来管理一些系统特性(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),// 系统配置服务,预置的系统的权限、硬件feature还有其他的一些配置在这里面解析(i, pm) -> SystemConfig.getInstance(),// 在软件包上运行 dexopt 命令的辅助类。(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),i.getContext(), "*dexopt*"),// dex 行为管理类会保存所有包的dex位置(i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(),i.getInstaller(), i.getInstallLock()),(i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(),i.getInstallLock()),(i, pm) -> ApexManager.getInstance(),(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),(i, pm) -> (IncrementalManager)i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),(i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),() -> LocalServices.getService(UserManagerInternal.class)),(i, pm) -> new DisplayMetrics(),// 包解析类(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,i.getDisplayMetrics(), pm.mCacheDir,pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,i.getDisplayMetrics(), null,pm.mPackageParserCallback) /* scanningPackageParserProducer */,(i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,// Prepare a supplier of package parser for the staging manager to parse apex file// during the staging installation.// 包安装服务(i, pm) -> new PackageInstallerService(i.getContext(), pm, i::getScanningPackageParser),(i, pm, cn) -> new InstantAppResolverConnection(i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),(i, pm) -> new ModuleInfoProvider(i.getContext()),(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),(i, pm) -> domainVerificationService,(i, pm) -> {HandlerThread thread = new ServiceThread(TAG,Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);thread.start();return new PackageHandler(thread.getLooper(), pm);},new DefaultSystemWrapper(),LocalServices::getService,context::getSystemService,(i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),(i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)),(i, pm) -> new SharedLibrariesImpl(pm, i));if (Build.VERSION.SDK_INT <= 0) {Slog.w(TAG, "**** ro.build.version.sdk not set!");}// 初始化PackageManagerServicePackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest, PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);t.traceEnd(); // "create package manager"final CompatChange.ChangeListener selinuxChangeListener = packageName -> {synchronized (m.mInstallLock) {final Computer snapshot = m.snapshotComputer();final PackageStateInternal packageState =snapshot.getPackageStateInternal(packageName);if (packageState == null) {Slog.e(TAG, "Failed to find package setting " + packageName);return;}AndroidPackage pkg = packageState.getPkg();SharedUserApi sharedUser = snapshot.getSharedUser(packageState.getSharedUserAppId());String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);if (pkg == null) {Slog.e(TAG, "Failed to find package " + packageName);return;}final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,m.mInjector.getCompatibility());if (!newSeInfo.equals(oldSeInfo)) {Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "+ oldSeInfo + " to: " + newSeInfo);m.commitPackageStateMutation(null, packageName,state -> state.setOverrideSeInfo(newSeInfo));m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);}}};//监听selinux的一些变化injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,selinuxChangeListener);injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,selinuxChangeListener);// ota完或者第一次开机为所有用户安装其被m.installAllowlistedSystemPackages();// 初始化 IPackageManagerImpl IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();ServiceManager.addService("package", iPackageManager);// 初始化PackageManagerNative final PackageManagerNative pmn = new PackageManagerNative(m);ServiceManager.addService("package_native", pmn);LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());return Pair.create(m, iPackageManager);}

简单的介绍了一下main,下面重头戏在PackageManagerService的初始化过程,它总共有两个构造函数,注意一个只做了赋值,我们这里调用的不是这个构造函数而是另一个

public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {mIsEngBuild = isEngBuild;......// 这里部分主要是给PackageMangerService的一些属性赋值......// CHECKSTYLE:ON IndentationCheckt.traceEnd();t.traceBegin("addSharedUsers");// 注册各种UIDmSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.log", LOG_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.se", SE_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);t.traceEnd();......// 又是一堆初始化赋值......// 从systemconfig获取对于机器来说可用的硬件feature,可以在这里之前对feature进行修改t.traceBegin("get system config");SystemConfig systemConfig = injector.getSystemConfig();mAvailableFeatures = systemConfig.getAvailableFeatures();t.traceEnd();......// 这里是读取在mac_permissions.xml下面配置的一些se策略可以有效的限制一些应用的权限SELinuxMMAC.readInstallPolicy();......final VersionInfo ver = mSettings.getInternalVersion();// 指纹信息是否有更新mIsUpgrade =!buildFingerprint.equals(ver.fingerprint);if (mIsUpgrade) {PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from "+ ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);}mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,mInjector.getSystemPartitions());// when upgrading from pre-M, promote system app permissions from install to runtime// 是否从M升上来mPromoteSystemApps =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.// 是否从N 升上来mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;// 是否从n mr升上来mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;// 是否从Q升上来mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;......// 根据不同的版本类型创建缓存目录,如果已存在则不创建mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);final int[] userIds = mUserManager.getUserIds();PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();// 初始化安装系统级APPmOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);// 初始化安装非系统级APPmInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);packageParser.close();......
}

可以从上面PackageManagerService的初始化可以看到,机器开机的时候安装APP的任务是交给InitAppsHelper这个类来做的,两个函数一个initSystemApps一个initNonSystemApps,分别是初始化系统app和非系统app,两个函数都是对系统中的应用进行安装解析,区别就是针对系统app还是非系统app,我们先从系统app来看看。

三、InitAppsHelper

public OverlayConfig initSystemApps(PackageParser2 packageParser,WatchedArrayMap<String, PackageSetting> packageSettings,int[] userIds, long startTime) 

initSystemApps有四个参数,看下其中比较重要的两个参数怎么来的

1、PackageParser2

第一个参数实在PackageManagerService初始化的时候从mInjector获取的,而这里的PackageParser2是在PackaManagerService的main函数初始化的

PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();

2、WatchedArrayMap<String, PackageSetting>

首先这个参数是通过PackageMangerService的成员变量mSettings获取:

final WatchedArrayMap<String, PackageSetting> packageSettings =mSettings.getPackagesLocked();

Settings.getPackagesLocked

 WatchedArrayMap<String, PackageSetting> getPackagesLocked() {return mPackages;}

Settings.addPackageLPw 中添加数据

 PackageSetting addPackageLPw(String name, String realName, File codePath, int uid, int pkgFlags,int pkgPrivateFlags, @NonNull UUID domainSetId) {PackageSetting p = mPackages.get(name);if (p != null) {if (p.getAppId() == uid) {return p;}PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate package, keeping first: " + name);return null;}p = new PackageSetting(name, realName, codePath, pkgFlags, pkgPrivateFlags, domainSetId).setAppId(uid);if (mAppIds.registerExistingAppId(uid, p, name)) {mPackages.put(name, p);return p;}return null;}

Settings.readSettingsLPw,这个函数中对应用的一些配置信息进行解析,这些信息主要是存在/data/system目录下面

 boolean readSettingsLPw(@NonNull Computer computer, @NonNull List<UserInfo> users,ArrayMap<String, Long> originalFirstInstallTimes) {mPendingPackages.clear();mInstallerPackages.clear();originalFirstInstallTimes.clear();ArrayMap<Long, Integer> keySetRefs = new ArrayMap<>();ArrayList<Signature> readSignatures = new ArrayList<>();// 获取一系列配置文件,文件位于/data/system/下面,在setings初始化的时候被赋值/**mSystemDir = new File(dataDir, "system");mSystemDir.mkdirs();mSettingsFilename = new File(mSystemDir, "packages.xml");mSettingsReserveCopyFilename = new File(mSystemDir, "packages.xml.reservecopy");mPreviousSettingsFilename = new File(mSystemDir, "packages-backup.xml");下面就是对这些配置文件进行解析*/try (ResilientAtomicFile atomicFile = getSettingsFile()) {FileInputStream str = null;try {str = atomicFile.openRead();if (str == null) {// Not necessary, but will avoid wtf-s in the "finally" section.findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();return false;}final TypedXmlPullParser parser = Xml.resolvePullParser(str);int type;while ((type = parser.next()) != XmlPullParser.START_TAG&& type != XmlPullParser.END_DOCUMENT) {// nothing}if (type != XmlPullParser.START_TAG) {mReadMessages.append("No start tag found in settings file\n");PackageManagerService.reportSettingsProblem(Log.WARN,"No start tag found in package manager settings");Slog.wtf(PackageManagerService.TAG,"No start tag found in package manager settings");return false;}int outerDepth = parser.getDepth();while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {continue;}String tagName = parser.getName();if (tagName.equals("package")) {//读取包信息readPackageLPw(parser, readSignatures, keySetRefs, users,originalFirstInstallTimes);} else if (tagName.equals("permissions")) {mPermissions.readPermissions(parser);} else if (tagName.equals("permission-trees")) {mPermissions.readPermissionTrees(parser);} else if (tagName.equals("shared-user")) {readSharedUserLPw(parser, readSignatures, users);} else if (tagName.equals("preferred-packages")) {// no longer used.} else if (tagName.equals("preferred-activities")) {// Upgrading from old single-user implementation;// these are the preferred activities for user 0.readPreferredActivitiesLPw(parser, 0);} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {// TODO: check whether this is okay! as it is very// similar to how preferred-activities are treatedreadPersistentPreferredActivitiesLPw(parser, 0);} else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {// TODO: check whether this is okay! as it is very// similar to how preferred-activities are treatedreadCrossProfileIntentFiltersLPw(parser, 0);} else if (tagName.equals(TAG_DEFAULT_BROWSER)) {readDefaultAppsLPw(parser, 0);} else if (tagName.equals("updated-package")) {readDisabledSysPackageLPw(parser, users);} else if (tagName.equals("renamed-package")) {String nname = parser.getAttributeValue(null, "new");String oname = parser.getAttributeValue(null, "old");if (nname != null && oname != null) {mRenamedPackages.put(nname, oname);}} else if (tagName.equals("last-platform-version")) {// Upgrade from older XML schemafinal VersionInfo internal = findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);final VersionInfo external = findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);internal.sdkVersion = parser.getAttributeInt(null, "internal", 0);external.sdkVersion = parser.getAttributeInt(null, "external", 0);internal.buildFingerprint = external.buildFingerprint =XmlUtils.readStringAttribute(parser, "buildFingerprint");internal.fingerprint = external.fingerprint =XmlUtils.readStringAttribute(parser, "fingerprint");} else if (tagName.equals("database-version")) {// Upgrade from older XML schemafinal VersionInfo internal = findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);final VersionInfo external = findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);internal.databaseVersion = parser.getAttributeInt(null, "internal", 0);external.databaseVersion = parser.getAttributeInt(null, "external", 0);} else if (tagName.equals("verifier")) {final String deviceIdentity = parser.getAttributeValue(null, "device");mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {// No longer used.} else if (tagName.equals("keyset-settings")) {mKeySetManagerService.readKeySetsLPw(parser, keySetRefs);} else if (TAG_VERSION.equals(tagName)) {final String volumeUuid = XmlUtils.readStringAttribute(parser,ATTR_VOLUME_UUID);final VersionInfo ver = findOrCreateVersion(volumeUuid);ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION);ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION);ver.buildFingerprint = XmlUtils.readStringAttribute(parser,ATTR_BUILD_FINGERPRINT);ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);} else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) {mDomainVerificationManager.readSettings(computer, parser);} else if (tagName.equals(DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) {mDomainVerificationManager.readLegacySettings(parser);} else {Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "+ parser.getName());XmlUtils.skipCurrentTag(parser);}}str.close();} catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) {// Remove corrupted file and retry.atomicFile.failRead(str, e);// Ignore the result to not mark this as a "first boot".readSettingsLPw(computer, users, originalFirstInstallTimes);}}return true;}

从上面的函数可以看出Settings这个类的主要职责就是存储修改解析应用的配置信息,这里涉及的东西比较多,不再赘述有兴趣可以自行查阅相关源码。

            mFirstBoot = !mSettings.readLPw(computer,mInjector.getUserManagerInternal().getUsers(/* excludePartial= */ true,/* excludeDying= */ false,/* excludePreCreated= */ false));

这里是Settings解析的开始在PackageMangerService的构造函数中,这是第二个参数的由来,接着第三个四个参数就比较简单分别是uid和时间戳。

篇幅有限下一篇展开细说

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/876706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Activiti 本地画流程 http://localhost:8080/activiti-app/#/

http://localhost:8080/activiti-app/#/ 1、本地安装了Tomcat 2、本地安装了Activiti 3、拷贝Activiti中这两个文件到Tomcat中的webapps目录下 4、启动startu.bat 5、http://localhost:8080/activiti-app/#/ 账号&#xff1a;admin 密码&#xff1a;test

乐鑫 Matter 技术体验日回顾|全面 Matter 解决方案驱动智能家居新未来

日前&#xff0c;乐鑫信息科技 (688018.SH) 在深圳成功举办了 Matter 方案技术体验日活动&#xff0c;吸引了众多照明电工、窗帘电机、智能门锁、温控等智能家居领域的客户与合作伙伴。活动现场&#xff0c;乐鑫产研团队的小伙伴们与来宾围绕 Matter 产品研发、测试认证、生产工…

Python学习笔记46:游戏篇之外星人入侵(七)

前言 到目前为止&#xff0c;我们已经完成了游戏窗口的创建&#xff0c;飞船的加载&#xff0c;飞船的移动&#xff0c;发射子弹等功能。很高兴的说一声&#xff0c;基础的游戏功能已经完成一半了&#xff0c;再过几天我们就可以尝试驾驶 飞船击毁外星人了。当然&#xff0c;计…

解析西门子PLC的String和WString

西门子PLC有两种字符串类型&#xff0c;String与WString String 用于存放英文数字标点符号等ASCII字符&#xff0c;每个字符占用一个字节 WString宽字符串用于存放中文、英文、数字等Unicode字符&#xff0c;每个字符占用两个字节 之前我搞过一篇解析String的 关于使用TCP-…

nginx基础使用

文章目录 nginx下载和编译configtest1test2config 原理 nginx 功能: 做为web server 使用在局域网内&#xff0c;提供对外的ip和端口 下载和编译 源码内容&#xff1a; nginx openssl pcrc zlib 编译&#xff1a; 1 cmake 方式&#xff1a; mkdir build cd build cmake 2 ma…

Unity Shader动画:用代码绘制动态视觉效果

在Unity中&#xff0c;Shader是运行在GPU上的小程序&#xff0c;用于控制顶点和像素的渲染过程。通过编写自定义Shader&#xff0c;开发者可以创造出各种令人惊叹的动画效果&#xff0c;从简单的颜色变化到复杂的流体模拟。本文将探讨如何使用Unity Shader来实现动画效果。 Sh…

算法入门篇(五)之 树的应用

目录 1.树和二叉树 1.1树&#xff08;Tree&#xff09; 1.1.1 特点 1.1.2 使用场景 1.1.3 示例 1.2二叉树&#xff08;Binary Tree&#xff09; 1.2.1 特点 1.2.2 使用场景 1.2.3 示例 2.二叉树遍历 2.1 先序遍历、中序遍历、后序遍历、层次遍历 2.1.1 先序遍历&…

git命令实现github与gitee同步

使用 git remote -v查看远程库连接了啥 git remote set-url --add origin 你的git仓库ssh (意思就是在 远端库origin下面加一个)然后就是git push&#xff08;这里可能会碰到问题&#xff0c;远程仓库的分支比本地分支更新&#xff09;注意github&#xff08;main&#xff09;与…

Vue3 Pinia的创建与使用代替Vuex 全局数据共享 同步异步

介绍 提供跨组件和页面的共享状态能力&#xff0c;作为Vuex的替代品&#xff0c;专为Vue3设计的状态管理库。 Vuex&#xff1a;在Vuex中&#xff0c;更改状态必须通过Mutation或Action完成&#xff0c;手动触发更新。Pinia&#xff1a;Pinia的状态是响应式的&#xff0c;当状…

Linux内核 mmap内存映射的实现原理

在Linux内核以及Linux系统编程的时候&#xff0c;经常会碰到mmap内存映射&#xff0c;mmap函数是实现高性能编程的一个关键点。本文详细介绍一下mmap实现原理。 虚拟地址映射物理地址 虚拟地址映射物理地址采用的是页表机制&#xff0c;64位CPU采用的是4级页表。 64位CPU虚拟…

鸿蒙 HarmonyOS NEXT端云一体化开发-认证服务篇

一、开通认证服务 地址&#xff1a;AppGallery Connect (huawei.com) 步骤&#xff1a; 1 进入到项目设置页面中&#xff0c;并点击左侧菜单中的认证服务 2 选择需要开通的服务并开通二、端侧项目环境配置 添加依赖 entry目录下的oh-package.json5 // 添加&#xff1a;主要前…

《python程序语言设计》第6章14题 估算派值 类似莱布尼茨函数。但是我看不明白

这个题提供的公式我没看明白&#xff0c;后来在网上找到了莱布尼茨函数 c 0 for i in range(1, 902, 100):a (-1) ** (i 1)b 2 * i - 1c a / bprint(i, round(4 / c, 3))结果 #按题里的信息&#xff0c;但是结果不对&#xff0c;莱布尼茨函数到底怎么算呀。

本地部署大模型

模型排行榜&#xff1a;https://www.superclueai.com/ Open WebUI https://docs.openwebui.com/ Open WebUI 是一种可扩展、功能丰富且用户友好的自托管 WebUI&#xff0c;旨在完全离线运行。它支持各种 LLM 运行器&#xff0c;包括 Ollama 和 OpenAI 兼容的 API。 docker安装…

PyTorch深度学习快速入门(上)

PyTorch深度学习快速入门&#xff08;上&#xff09; 一、前言&#xff08;一&#xff09;PyTorch环境配置&#xff08;二&#xff09;Python编译器的选择&#xff08;三&#xff09;Python学习中的两大法宝函数 二、如何加载数据&#xff08;一&#xff09;Dataset与Dataloade…

c++实现一个函数,对一个输入的整数,代表时间秒数,将其转换成时间格式字符串,如“01:09:11“,代表1小时,09分,11秒

c实现一个函数&#xff0c;对一个输入的整数&#xff0c;代表时间秒数&#xff0c;将其转换成时间格式字符串&#xff0c;如“01&#xff1a;09&#xff1a;11“&#xff0c;代表1小时&#xff0c;09分&#xff0c;11秒。 #include <iostream> #include <string> …

轻松学EntityFramework Core--模型创建

一、使用代码优先&#xff08;Code-First&#xff09;创建模型 Code-First 方法是 EF Core 提供的一种用于定义模型的方式&#xff0c;它允许开发人员通过编写 C# 类来定义数据库模式&#xff0c;再通过迁移命令生成数据库表。下面我们来一起看一下代码优先如何使用。 1.1、创…

lua 游戏架构 之 游戏 AI (六)ai_auto_skill

定义一个为ai_auto_skill的类&#xff0c;继承自ai_base类。ai_auto_skill类的目的是在AI自动战斗模式下&#xff0c;根据配置和条件自动选择并使用技能。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读379次。定义了一套接口和属性&#…

【原创】使用keepalived虚拟IP(VIP)实现MySQL的高可用故障转移

1. 背景 A、B服务器均部署有MySQL数据库&#xff0c;且互为主主。此处为A、B服务器部署MySQL数据库实现高可用的部署&#xff0c;当其中一台MySQL宕机后&#xff0c;VIP可自动切换至另一台MySQL提供服务&#xff0c;实现故障的自动迁移&#xff0c;实现高可用的目的。具体流程…

快速安装torch-gpu和Tensorflow-gpu(自用,Ubuntu)

要更详细的教程可以参考Tensorflow PyTorch 安装&#xff08;CPU GPU 版本&#xff09;&#xff0c;这里是有基础之后的快速安装。 一、Pytorch 安装 conda create -n torch_env python3.10.13 conda activate torch_env conda install cudatoolkit11.8 -c nvidia pip ins…