Low Memory Killer in Android

目录

低内存管理(Linux vs Android)

Linux内存回收

shrink_slab原理

shrink_zone原理

oom killer

oom killer设计原则

OOM killer具体实现

android的lmk(Low Memory Killer)

Android系统特点

oom killer在android中的不足

​​​​​​​LMK概述

LMK提供的接口

接口说明

​​​​​​​minfree与adj初始化

LMK实现

​​​​​​​LMK驱动实现

​​​​​​​android进程管理

​​​​​​​Android组件

​​​​​​​Android进程生命周期

进程adj调整

adj值

​​​​​​​adj调整原则

​​​​​​​adj调整时机

​​​​​​​adj调整算法

如何降低被kill概率


低内存管理(Linux vs Android)

Linux内存回收

低内存情况下,Linux内存回收有二种模式:一种是直接内存回收,通过伙伴系统分配一大块内存或者需要创建一个很大的缓冲区时,如果没有足够多的free pages,那么系统会尽快进行页面回收;另一种是定期内存回收,定期唤醒kswapd内核线程,当系统空闲内存小于阈值时则进行页面回收。具体的系统调用过程,请参考图1所示。

由图1可知,不管是直接页面回收,还是周期页面回收,最终都调用shrink_slab()和shrink_zone()。直接页面回收,在一定的约束下,如果最终没能释放所需page,则调用OOM(out of memory) killer。

shrink_slab原理

先向操作系统内核注册shrinker函数,会在内存较少时主动释放一些内存。shrink_slab()会遍历shrinker链表,回调所有注册了shrinker函数的处理内存的操作。当前,linux操作系统中主要的shrinker函数有:

  1. shrink_dcache_memory():负责dentry缓存。
  2. shrink_icache_memory():负责inode缓存。
  3. mb_cache_shrink_fn():负责用于文件系统元数据的缓存。

shrink_zone原理

shrink_zone()的主要工作可以分为三步:

  1. 通过shrink_active_list()将页面从active移到inactive list;
  2. 通过shrink_inactive_list()将inactive list的页放入临时链表;
  3. 最终调用shrink_page_list()回收页。回收算法如图2所示。

           图1 内存回收系统调用

            图2 回收页算法

oom killer

Oom killer是kernel在内存耗尽时的最后手段,使用oom_badness(),为每个进程计算得分,移除得分高的进程。

系统调用如下:

out_of_memory()->select_bad_process->oom_badness()

OOM killer计算进程得分的策略是:损失最少的工作,释放最大的内存同时不伤及无辜,并且杀掉的进程数尽量少。

oom killer设计原则

OOM killer简单粗暴地作风并不符合linux的风格,并且也不符合linux机制与策略分离的原则。然而当系统内存不足时,已经是将死之态,也无需讲究,暴力往往是最有效的解决方案。虽然粗暴,但是手段上也需要尽可能地“精致”,oom killer从提出到现在,也已经进行了几次优化,目前的设计遵循下面几方面:

  • 以进程所占物理内存作为判断依据

  进程实际所需的是物理内存,而早期以进程的虚拟地址空间大小为基准,显然不准确。

  • 可配置的用户建议策略

有些进程占用物理内存很大,但是也在做很重要的事情,如KDE桌面主进程。因此必须将一部分控制权交出给用户层,用户可以根据具体情况,对进程重要度加权。

  • 简单并且合理的默认策略

  将特权进程,免于oom killer机制;如果按现有选择策略,无法选择出需要被结束的进程,那么就直接panic,因为已经无能为力;另外,杀子不杀父,因为如果kill父进程,还需要为子进程托孤,需要做很多事情,而这会加重内存不足的情况,况且父进程往往是关键服务,而子进程往往是工作都线程,kill父进程对用户的影响更大。

OOM killer具体实现

  • 提供给用户层的接口:  /proc/<pid>/oom_adj & /proc/<pid>/oom_score_adj

    oom_adj是以前的接口,为了兼容低版本,现在还保留此接口,但是最好不要再使用了。

    oom_score_adj的值会影响各个进程的最终得分,范围是-1000(OOM_SCORE_ADJ_MIN)~1000(OOM_SCORE_ADJ_MAX)。用户空间可以调整进程的oom_score_adj值,来影响oom killer的行为,值越大,进程越容易被kill,-1000可以关闭oom killer对此进程的作用。

  • 算法

oom_badness是oom killer选择“bad”进程的核心算法,流程图如图3所示。

         图3 oom_badness流程图

android的lmk(Low Memory Killer)

 Linux 已经有oom killer了,那android为何又要引入LMK呢?

Android系统特点

Android是嵌入式系统,通常运行在内存很有限的设备上(如手机,平板)。这类设备,有一个特点就是“屏幕独占性”,即要调出一个任务,意味着必须退出或隐藏当前的任务。另外,此类设备同时运行的任务不会太多,并且影响用户使用体验的任务比较好识别。除此之外,“高交互”的系统特征,决定必须提高系统响应速度,因为这直接影响用户的使用体验。而在嵌入式系统中,资源相当有限,如何在低配置设备中,提高响应速度,是android需要重点考虑的。

Android使用的特点,决定设计的方案:

  1. Android进程并不主动退出,而是作为“空进程”保留在内存中,以便用户再次进入该应用时,可以提高响应速度;
  2. 因为进程不主动退出,必须有一套机制能实时按一定的策略回收内存;
  3. Android进程的重要度,随着用户操作的改变而改变,即实时改变;

oom killer在android中的不足

  • 启动时机不合适

OOM killer是在内存被耗尽时,启动的极端内存回收机制。Android中用户对系统反应快慢非常敏感。别说在内存被耗尽时,就是在内存不足时,系统出现,反应延迟,也会严重影响用户体验。再加上android进程的“不主动退出”机制,所以需要周期检查,进行内存管理。

  • 选择进程策略不合适

OOM killer根据进程所占物理内存为主要判断依据,加上oom_score_adj和其它一些因素调整。在android中,物理内存占用多少,不能成为其主要判断依据,而应该根据进程的重要程度,在相同重要度的情况下,才考虑内存因素。进程的重要度是相对于用户而言的。

  • 选择进程范围不合适

OOM killer候选的进程是除init进程,内核线程以及一些特权进程外的所有进程。而android只需要管理zygote启动之后的进程,之前的native进程都不需要考虑。

  • 用户层控制权的不合适

OOM killer提供了oom_score_adj的接口,程序可以自己设置值,降低OOM时的得分。一般而言,这个值不会频繁去变更。这对于android系统特点3)并不适用。在android系统中,用户层需要大部分的控制权限,并且能够根据用户操作实时变更进程的重要度。

​​​​​​​LMK概述

由于android有自己的应用场景,而OOM killer并不能满足其要求,因此引入了LMK机制。主要解决android中“进程不主动退出”机制所引起的内存回收问题。

LMK复用了linux低内存管理中的shrink_slab机制,将lmk注册到shrinker键表中,那么linux中无论是直接回收还是定期回收内存,都可以调到lmk的处理。

LMK还复用了用户层控制接口(/proc/<pid>/oom_score_adj),并将其作为选择进程的主要依据。FW层的AMS作为类似于“任务管理器”,根据用户行为管理进程。

LMK的整体结构如图4所示。

              图4 LMK整体结构

LMK提供的接口

接口说明

LMK将剩余内存分为几个等级,最多支持6个等级,分级的策略则交与用户层制定,其值写入以下文件,作为LMK驱动的参数。minfree值以页为单位(一页为4K)。

LMK按重要度(adj)为进程分组,最多支持6组,组数一般都设成与minfree对应。与minfree一样,分组的策略也由用户层制定,并将值写入以下文件,作为LMK驱动的参数。

这里需要注意,前面OOM killer中已经介绍过,adj是低版本的接口,现在已经换成oom_score_adj。但是由于android上层一直使用旧接口,修改上层太麻烦,因此android用户层仍然继续使用adj接口,只是在kernel层将adj自动转换成oom_score_adj。

上面二个接口,就是用户层制定的,即在什么的内存情况下,LMK开始工作,并且以什么标准工作。表1配置,当内存剩余6656*4K=26M时,LMK将kill所有 adj为9及以上的进程。

Minfree

3072

4096

4608

6656

8704

10752

adj

0

1

3

9

11

15

          表1 mk配置​​​​​​​

​​​​​​​minfree与adj初始化

Android原生对minfree和adj的初始化,放在ProcessList:updateOomLevels()中。系统默认adj配置如下所示:

  系统给minfree给了二个标准配置,mOomMinFreeLow与mOomMinFreeHigh​​​​​​​

然后根据内存屏幕大小,决定用lowminfree还是highminfree。内存在300~700M之间用lowminfree;内存在700M及以上,用highminfree;屏幕在480*800~1280*800之间用lowminfree;屏幕1280*800及以下,用highminfree。内存与屏幕,只要有一个需要用highminfree ,最终就用highminfree。

除此之外,如果是64bit设备,minfree*1.5。还可以根据设备配置config:

对minfree微调,adj与minfree的值,最终调用lmkd写入module/lowmemorykiller/parameters/,

作为参数传入lowmemorykiller驱动。

LMK实现

    LMK整套机制,可以分为二部分,一个是LMK驱动,一个是AM对进程的管理。

​​​​​​​LMK驱动实现

前面已经提到,LMK是通过shrinker机制,将自己整合进kernel。在内存不足或者定期检查内存时,都会通过shrink_slab,回调lmk操作。Lowmem_shrink()就是lmk驱动的核心。具体操作流程参考图5。

          图5 lowmem_shrink流程图

​​​​​​​android进程管理

Lmk驱动只是机制,实现非常简单。Android运行时的进程管理才是LMK的复杂点,因为需要在运行时实时为每个进程打分(设置adj)。

​​​​​​​Android组件

Android中,进程是由组件组成,每个组件都扮演不同的角色。组件的不同状态决定进程的adj。四大组件分别是activity,services,Broadcast,Content Providers。

1.Activity

   Activity是屏幕上单独的虚拟UI。通常,activity是应用与用户交互的主要组件。在activity的生命周期中,有create,running,pause,stop,destroy之些状态,当在running状态时,表示用户正在操作这个activity。

          图6 Activity生命周期

2.Services

Services通常是在后台运行的组件,但是也可以在前台启动。应用一旦在后台启动service,即使用户切换到其它应用,service还会在后台运行。服务没有用户交互界面。

         图7 service生命周期

3.Content Providers

Content Providers为不同的应用提供内容(数据),支持在文件或数据库中存储结构化数据。其它应用可以通过content resolver访问数据。

4.Broadcast Receiver

可以接收系统范围的广播。应用可以发起广播信息给另外一个应用,如文件下载已经完成等。它没有任何用户界面,但是会在status bar上形成注意信息。

​​​​​​​Android进程生命周期

Android系统试图尽可能地保持应用进程,但是最终需要为新的或者更重要的进程回收内存,而移除老的进程。为了决定kill哪个进程,系统根据进程中运行的组件和这些组件的状态,给每个进程一个重要度。系统总是先kill最不重要的进程,来回收系统资源。

重要度分为5个level:(the first is most important and is killed last)

1.Foreground process(前台进程)

用户正在操作的进程。下面任何一个条件成立,都可以认为是前台进程。

  1. 包含用户正在交互的Activity(Activity的onResume()已经被执行);
  2. 包含Service,并且这个Service绑定于用户正在交互的activity;
  3. 包含Service,并且这个Service运行在前台-Service已经执行startForeground();
  4. 包含Service,并且这个Service正在执行其生命周期的callbacks(onCreate(),onStart(),or onDestroy());
  5. 包启BroadcastReceiver,并且其正在执行onReceive()方法。

在给定的任一时间,通常只有小部分Foreground process(前台进程)。

2.Visible process(可见进程)

没有任何前台组件,但是用户在屏幕上仍然可以看到。下面任何一个条件成立,都可以认为是可见进程。

  1. 包含一个不在前台Activity,但是用户仍可以看到(其onPause()方法已经被执行);这可能发生,例如,前台activity起了一个dialog,此时后台的activity不在前台,但是用户仍能看到。
  2. 包含一个Service,并且此Service绑定到visible/foreground activity。

3.Service process(服务进程)

运行着Service但不属于前台和可见的进程,且此service已经执行过startService()。尽管服务进程与用户可见的无关,但是却正在做用户关心的事(如正在后台播放音乐或者从网上下载数据),因此系统会尽可能地保持他们运行。

4.Background process(后台进程)

包含一个activity,当前对用户不可见(activity的onStop()已经被执行)。这些进程不会直接影响用户体验,系统会为前台/可见/服务进程回收内存,随时会kill他们。通常会有很多后台进程在运行,他们被保存在LRU(least recently used)列表中,确保用户最后使用的最后被kill。如果一个activity正确地实现了其生命周期方法,并且保存了其正确地状态,那么它被kill,就不会影响用户体验,因为当用户回到这个activity时,由于其已经保存了其状态,用户看到地还是原来的。

5.Empty process(空进程)

不包含任何存活应用组件的进程。保存这些进程,纯粹就是为了缓存,方便下次组件运行在此进程时,加速启动时间。系统为了在进程缓存和kernel缓存间平衡系统资源,总是会kill这些进程。

进程adj调整

adj值

  adj的有效范围从-17~15,各值所代表的意思,如表2所示:

adj

意思

UNKNOWN_ADJ

16

实现需要,一般不会给进程设置,只做为临时值过渡

CACHED_APP_MAX_ADJ

15

进程不可见,并且只包启activity组件

CACHED_APP_MIN_ADJ

9

进程不可见,并且只包含activity组件

SERVICE_B_ADJ

8

包含service组件的进程分成二组,比起A,B组中的service相对比较陈旧,对用户影响小。

PREVIOUS_APP_ADJ

7

用户使用的前一个应用的进程。提高此进程优先级,是因为按back键返回到前一个应用的操作非常普通。

HOME_APP_ADJ

6

Home进程

SERVICE_ADJ

5

含serivce组件的进程

HEAVY_WEIGHT_APP_ADJ

4

重量级应用的进程

BACKUP_APP_ADJ

3

正在备份操作的进程

PERCEPTIBLE_APP_ADJ

2

进程含有用户可感知的组件,如后台音乐播放器

VISIBLE_APP_ADJ

1

进程含有用户可见的activity

FOREGROUND_APP_ADJ

0

前台进程

PERSISTENT_SERVICE_ADJ

-11

系统进程或长驻进程绑定的进程

PERSISTENT_PROC_ADJ

-12

系统长驻进程,如电话。

SYSTEM_ADJ

-16

系统进程,默认设为-16

NATIVE_ADJ

-17

系统不管的native进程,统一设为-17

​​​​​​​adj调整原则

Android基于进程中存活着组件的重要度,尽可能地提高进程的重要水平。例如,一个进程既有一个service,又有一个visible activity,那么进程会被标为可见进程,而非服务进程。

另外,如果其它进程依赖一个进程,那么这个进程的重要度也可能提高-提供服务的进程,其重要度不能低于其服务的进程。例如:进程A为进程B提供content provider,或者进程A的Service绑定于进程B的组件,那么进程A的重要度至少等同于进程B。

因为运行service的进程,其重要度比运行后台activity的进程高,所以如果activity需要长时间的操作(尤其这个操作比activity存活时间长),那么启动service比简单的创建工作线程,效果好。例如,网页上上传图片的activity,应该启动service来执行上传操作,这样即使用户leave这个activity,上传操作仍旧会在后台继续。使用service,其操作的优先级至少是“服务进程”,而不需要关心activity。同理,broadcast receivers也推荐使用service,而非将耗时操作放在线程里。

​​​​​​​adj调整时机

  只要组件状态发生变化,就会进行adj调整,具体看下面列出的各个函数。

  • Serivce状态变化

        bindServiceLocked

        unbindServiceLocked

        realStartServiceLocked

        sendServiceArgsLocked

        bringDownServiceLocked

        removeConnectionLocked

        serviceDoneExecutingLocked

  • Contentprovider处理

        getContentProviderImpl

        removeContentProvider

        removeContentProviderExternalUnchecked

        publishContentProviders

  • Broadcast处理

        processCurBroadcastLocked

        deliverToRegisteredReceiverLocked

        processNextBroadcast

  • Activity状态变更

        realStartActivityLocked

        resumeTopActivityInnerLocked

        finishSubActivityLocked

        finishVoiceTask

        finishCurrentActivityLocked

        destroyActivityLocked

  • Application调整

        addAppLocked

        attachApplicationLocked

        appDiedLocked

        setSystemProcess

        setProcessForeground

        updateProcessForegroundLocked

        killAllBackgroundProcesses

        killPackageProcessesLocked

        foregroundTokenDied

        trimApplications

        bindBackupAgent

        unbindBackupAgent

​​​​​​​adj调整算法

Adj值调整,主要在AMS:computeOomAdjLocked()方法中实现。算法实现如图8所示。

                     图8 调adj算法

如果进程中有service和contentprovider组件,那么进程的adj还需要随client进程adj而调整。

以bindService启动service时,会有bind flag,某些flag值会影响client进程与service宿主进程。如表2所示:

Bind flag

影响

BIND_WAIVE_PRIORITY

此次binding服务,不影响进程(包含service的进程)调度与内存管理优先级。

BIND_ALLOW_OOM_MANAGEMENT

允许进程(包含service的进程)通过其正常的内存管理。

BIND_NOT_FOREGROUND

不允许此次连接提高service进程的优先级到“foreground调度优先级”,但是service进程的“内存优先级”还是会被提高到与client进程一样(即client没被kill之前,service绝不会被kill),但是为了cpu调度的目的,service进程会被留在后台。这个flag只会影响这种情况:client是前台进程,但是service是后台进程。

BIND_ADJUST_WITH_ACTIVITY

如果从activity发起binding服务,进程的优先级会随着activity的优先级调整而调整。

BIND_ABOVE_CLIENT

认为连接的service比client进程重要,如果oom,会先kill client进程。

BIND_IMPORTANT

对于client来说,这个服务非常重要,如果client是前台进程,那么服务所在进程也应该是前台进程。通常情况下,即使client是前台的,进程也只能提高到visibility的优先级。

BIND_NOT_VISIBLE

即使client是visible,也不考虑进程为visible

           表2 serivce绑定flag

如何降低被kill概率

    进程要做到完全不被kill,基本也不可能。除非进程是系统进程,由init启动,那么就可以继承init的adj(-17),这样即使system_server进程被kill了,也不会被kill,不过可以做到尽可能不被lmk选中。

  • 提供进程优先级

后台操作尽可能用service来实现,而不用线程实现,因为包含service的进程优先级比普通进程高。

重载系统back按键事件,使activity在后台运行,而不是被destory。

依赖于其他优先级高的进程。

  • 修改进程属性

    如phone进程,设置persistent属性。

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

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

相关文章

基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用

背景 随着互联网服务的广泛普及与技术应用的深入发展&#xff0c;日志数据作为记录系统活动、用户行为和业务操作的宝贵资源&#xff0c;其价值愈发凸显。然而&#xff0c;当前海量日志数据的产生速度已经远远超出了传统数据分析工具的处理能力&#xff0c;这不仅要求我们具备…

【机器学习】机器学习与AI大数据的融合:开启智能新时代

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 机器学习与AI大数据的融合 &#x1f4d2;1. 引言&#x1f4d5;2. 机器学习与大数据&#x1f3a9;机器学习与大数据的特征&#x1f388;大数据如…

驱动开发之platform总线

1.前言 在前面的实验以及提出的各种问题中&#xff0c;为了提高移植性&#xff0c;降低模块耦合度&#xff0c;提让模块高内聚&#xff0c;分离device与driver是一个必然的趋势了。为了解决这个问题&#xff0c;心心念念的platform总线出来。 linux从2.6起就加入了一套新的驱…

python中利用cartopy库绘制SST图像

1. Cartopy简介 Cartopy 是一个开源的 Python 库&#xff0c;用于绘制地图和地理数据分析。它结合了 matplotlib 的绘图功能和 shapely、pyproj 等库的地理空间数据处理能力&#xff0c;为用户提供了在地图上可视化数据的强大工具。 以下是 Cartopy 的一些主要特点和功能&#…

探索不同类型的“低”之魅力

随着数字化转型的浪潮不断高涨&#xff0c;低代码平台&#xff08;LCDP&#xff09;已逐渐成为企业快速构建和迭代应用程序的重要工具。低代码平台以其高效、灵活和易于集成的特点&#xff0c;受到越来越多企业的青睐。然而&#xff0c;低代码平台并非一蹴而就的单一产品&#…

CST电磁仿真软件表面等离子极化激元SPP --- 一维光栅耦合 - 衍射模式, 效率, Floquet端口

这两期我们看一下衍射光栅的高阶衍射、衍射效率、反射率。具体到仿真设置&#xff0c;就是Floquet端口的模式分析&#xff0c;S参数与衍射效率和反射率的关系。那么研究这些衍射和表面等离子极化激元SPP有什么关系呢&#xff1f;关系可大了&#xff0c;光栅是一种能够用来激励出…

告别鼠标,安卓模拟鼠标,绘图板,手写板操作电脑PC端,卡卡罗特也说好,儿童节快乐

家人们&#xff0c;上链接了&#xff1a;https://download.csdn.net/download/jasonhongcn/89387887 横屏模式&#xff1a; 竖屏模式&#xff1a; 操作说明&#xff1a; 1. 手势滑动模拟鼠标移动 2. 界面如果有滚动条&#xff0c;右手指按紧&#xff0c;通过左手指移动实现…

力扣:101. 对称二叉树

101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提示&#xff1a; …

mysql DDL——增删改

简略版&#xff1a; 文字化&#xff1a; 1.对全部字段添加数据&#xff1a;insert into 表名 values (值1&#xff0c;值2&#xff0c;值3...); 2.对指定字段添加数据&#xff1a;insert into 表名 (字段名1&#xff0c;字段名2...) values &#xff08;值1&#xff0c;值2..…

跨区域文件管控过程中 如何保障安全和效率?

跨区域文件管控是指在跨越不同地域或区域的情况下对文件进行管理和控制的过程。这种控制可能涉及多个方面&#xff0c;包括安全性、合规性和管理效率等。 为了有效进行跨区域文件管控&#xff0c;组织通常需要采取一系列策略和措施&#xff0c;例如&#xff1a; 1、加密和安全…

旺店通·企业奇门和金蝶云星空接口打通对接实战多系统间数据

旺店通企业奇门和金蝶云星空接口打通对接实战 来源系统:旺店通企业奇门 慧策&#xff08;原旺店通&#xff09;是一家技术驱动型智能零售服务商&#xff0c;基于云计算PaaS、SaaS模式&#xff0c;以一体化智能零售解决方案&#xff0c;帮助零售企业数字化智能化升级&#xff0c…

二进制文件(.bin等文件)转C语言数组形式hex文件(.c等文件)

使用python脚本把二进制文件&#xff08;.bin等文件&#xff09;转C语言数组形式hex文件&#xff08;.c等文件&#xff09;&#xff0c;具体代码如下&#xff1a; &#xff08;1&#xff09;读取.bin文件数据 &#xff08;2&#xff09;生成C语言数组定义 &#xff08;3&…

深圳雷龙LSYT201B语音控制模组

文章目录 前言一、芯片简介处理器外设音频蓝牙电源封装温度 二、功能简介管脚描述 三、应用场景四、使用说明五、硬件连接六、FAQ总结 前言 今天拿到的语音控制板是LSYT201B模组&#xff0c;它是深圳市雷龙发展有限公司基于YT2228芯片开发的一款面向智能家居控制的离线语音控制…

员工转正申请精选12个模板供参考

随着社会在不断地进步&#xff0c;我们会经常使用各种各样的申请书&#xff0c;转正申请书就是申请书的一种&#xff0c;那么转正申请书是怎么写的呢&#xff1f;下面是我收集整理的员工转正申请书几个版本&#xff0c;仅供参考&#xff0c;希望能够帮助到大家。别忘了一键三连…

贵的SSL证书跟便宜的SSL证书有什么不一样的?

目前SSL证书市场上从几十块钱的到上万元的都是有的&#xff0c;那么这些着证书之间有什么不同&#xff1f;怎么收费差这么多&#xff1f; 一&#xff1a;从证书的认证安全等级来说 比较便宜的SSL证书一般都是DV验证级别的。也就是说只验证域名的所有权&#xff0c;做一个DNS解…

03.0docker安装

docker简介 docker的主要目标是"Build,Ship and Run any App,Angwhere",构建&#xff0c;运输&#xff0c;处处运行 部署服务&#xff0c;环境问题 一次构建&#xff0c;处处运行 docker是一种软件的打包技术 构建&#xff1a;做一个docker镜像 运输&#xff1a;do…

数据分析常用模型合集(一)AARRR模型和漏斗模型

准备把常用的数据分析模型&#xff0c;像什么AARRR&#xff0c;RFM之类的&#xff0c;逐个全部写一下&#xff1b; 最好能带点案例和代码&#xff0c;搞一个小合集。 最终达到完全不懂的人&#xff0c;看完就能知道得差不多&#xff1b; 数据分析常用模型合集&#xff08;二…

大型语言模型简介

大型语言模型简介 大型语言模型 (LLM) 是一种深度学习算法&#xff0c;可以使用非常大的数据集识别、总结、翻译、预测和生成内容。 文章目录 大型语言模型简介什么是大型语言模型&#xff1f;为什么大型语言模型很重要&#xff1f;什么是大型语言模型示例&#xff1f;大型语…

(一)django目录介绍

1、生成django项目&#xff0c;得到的目录如下 manage.py&#xff1a;命令行工具&#xff0c;内置多种方式与项目进行交互。在命令提示符窗口下&#xff0c;将路径切换到项目并输入python manage.py help,可以查看该工具的指令信息。 默认的数据库工具&#xff0c;sqlite 在…

基于django | 创建数据库,实现增、删、查的功能

1、在cmd中&#xff0c;输入指令进入mysql终端&#xff1a; mysql -u 用户名 -p 2、输入mysql的密码 3、输入指令&#xff0c;显示出所有的数据库 show databases; 4、输入指令创建表&#xff1a; create table 表名 DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 5、use …