Linux oom机制

news2024/9/21 20:36:56

Linux oom机制

  • 前言
  • 1 内存回收
  • 2 OOM基本原理
    • 2.1 虚拟内存OOM
    • 2.2 物理内存OOM
  • 3 oom配置参数
    • 3.1 panic_on_oom
    • 3.2 oom_kill_allocating_task
    • 3.3 oom_dump_tasks
  • 4 安卓LMK简介
  • 5 总结

前言

Linux oom是由于内存泄漏或者内存使用不合理而导致的问题。

在讲OOM之前,我们先来了解一下内核内存回收的总体框架。

1 内存回收

内核空间和用户空间的内存管理方式的差别是非常大的,首先内核是不会缺页也不会换页的,不会缺页是指内核的物理内存在启动时就直接映射好了,使用时直接分配就行了,分配好虚拟内存的同时物理内存也分配好了。不会换页是指,当系统内存不足时内核自身使用的物理内存不会被swap出去。与此相反,用户空间的内存分配是先分配虚拟内存,此时并不会直接分配物理内存,而是延迟到程序运行时访问到哪里的内存,如果这个内存还没有对应的物理内存,MMU就会报缺页异常从而陷入内核,执行内核的缺页异常handler给分配物理内存,并建立页表映射,然后再回到用户空间刚才的那个指令处继续执行。当系统内存不足时,用户空间使用的物理内存会被swap到磁盘,从而回收物理内存。之后如果进程再访问这段内存又会再发生缺页异常从swap处把内存内容加载回来。

内存作为系统最宝贵的资源,总是不够用的,经常需要进行回收。内存回收可分为两种方式,同步回收和异步回收,同步回收是在分配内存时发现内存不足直接调用函数进行回收,异步回收是唤醒专门的回收线程kswapd进行回收。我们先看一下它们的总体架构图,然后再一一说明。

同步回收的话是在alloc_pages时发现内存不足就直接进行回收,首先尝试的是内存规整,也就是内存碎片整理,比如说系统当前有10个不连续的空闲page,但是你要分配两个连续的page,显然是无法分配的,此时就要进行内存规整,通过移动movable page,使空闲page尽量连在一起,这样能有可能分配出多个连续的page了。如果内存规整之后还是无法分配到内存,此时就会进行页帧回收了。用户空间的物理内存可以分为两种类型,文件页和匿名页,文件页是text data段对应的页帧,它们都有文件做后备存储,匿名是栈和堆对应的内存页,它们没有对应的文件,一般用swap分区或者swap文件做它们的后备存储。系统会首先考虑干净的文件页进行回收,因为回收它们只要直接丢弃内容就可以了,需要的时候再直接从文件里读取回来,这样不会有数据丢失。如果没有干净的文件页或者干净的文件页不太多,此时就要从dirty 文件页和匿名页进行回收了,因为它们都要进行IO操作,所以会非常的慢。如果页帧回收也回收不到内存的话,内核只能使出最后一招了,OOM Killer,直接杀进程进行内存回收,虽然这招好像不太文雅,但是也是没有办法,因为不这样做的话,系统没有多余的内存就没法继续运行,系统就会卡死,用户就会重启系统,结果更糟,所以杀进程也是最后的无奈之举。一般能走到这一步都是因为进程有长期或者严重的内存泄漏导致的。

异步回收线程kswapd是被周期性的唤醒来执行回收任务的,当然同步回收的时候也会顺便唤醒它来一起回收内存。有一点需要注意的是kswapd线程不是per CPU的,而是per node的,是一个NUMA节点一个线程,这是因为内存的分配是per node不是 per CPU的,大部分内存分配都是优先从本node分配或者只能从本node分配,因此哪个node的内存不足了就唤醒哪个node的kswapd线程就行内存回收工作。对于家庭电脑和手机来说都是一个node,所以一般就只有一个kswapd线程。Kswapd完成回收工作之后,它会唤醒kcompactd线程进行内存规整,对的,内存规整也可以异步执行。

2 OOM基本原理

在讲内核的OOM Killer之前,我们先来说一下OOM基本概念。OOM,out of memory,就是内存用完了耗尽了的意思。OOM分为虚拟内存OOM和物理内存OOM,两者是不一样的。虚拟内存OOM发生在用户空间,因为用户空间分配的就是虚拟内存,不能分配物理内存,程序在运行的时候触发缺页异常从而需要分配物理内存,内核自身在运行的时候也需要分配物理内存,如果此时物理内存不足了,就会发生物理内存OOM。用户空间虚拟内存OOM表现为malloc、mmap等内存分配接口返回失败,错误码为ENOMEM。大家也许会想,虚拟内存会OOM吗,虚拟内存那么大,对于32位进程来说就有3G,对于64位进程来说至少也得有上百G,应有尽有,而且很多教科书上都说的是虚拟内存可以随意分配,不受物理内存的限制,事实上真的是这样吗,让我们来看一看。

2.1 虚拟内存OOM

虚拟内存我们是不是可以随意分配,虚拟空间有多大我们就能分配多少?事实不是这样的。UNIX世界有个著名的哲学原理,提供机制而不是策略,对于这个问题,Linux也提供了机制,我们可以通过 /proc/sys/vm/overcommit_memory 文件来选择策略。我们有三种选择,我们可以往这个文件里面写入0、1、2来选择不同的策略,这三个值对应的宏是:

#define OVERCOMMIT_GUESS   0
#define OVERCOMMIT_ALWAYS  1
#define OVERCOMMIT_NEVER   2

通过宏名我们也可以大概猜出来是啥意思,下面我们一一解析一下,先从最简单的开始,OVERCOMMIT_ALWAYS,从名字就可以看出来,只要虚拟内存空间还有富余,你malloc多少内存就给你多少虚拟内存,不管它物理内存到底还够不够用。OVERCOMMIT_GUESS,名为GUESS,实在不好guess的,通过看代码发现,这个模式允许你最多分配的虚拟内存不能超过系统总的物理内存(这里说的总物理内存是物理内存加swap的总和,因为swap在一定意义上也相当于是增加了物理内存),也就是说一个进程分配的总虚拟内存可以和系统的总物理内存相同,还是够可以的。OVERCOMMIT_NEVER,这个就比较苛刻了,它像一位勤俭持家的妈妈,总是只给你勉强够用的零花钱,从来不多给一分。我们来看一下它的计算过程,它先计算一个基准值,默认等于50%的物理内存加上swap大小,然后再减去系统管理保留的内存,再减去用户管理保留的内存,如果系统所有已分配的虚拟内存大于这个值,就返回分配失败。具体情况大家可以去看代码:

linux-src/mm/util.c:__vm_enough_memory

我们再来看一个这个三个宏的公共部分OVERCOMMIT,过度承诺,这个词想表达什么含义呢,过程承诺 always never guess,我们可以看出来,过程承诺指的是,系统允许分配给你的虚拟内存是对你的承诺,后面当你具体用访问内存的时候,是要给你分配物理内存来实现对你的承诺的,那么这个承诺到底能不能实现呢,如果不能实现会怎么样呢?

2.2 物理内存OOM

出来混迟早是要还的,分配出去的虚拟内存迟早是要兑现物理内存的。内核运行时会分配物理内存,程序运行时也会通过缺页异常去分配物理。如果此时没有足够的物理内存,内核会通过各种手段来收集物理内存,比如内存规整、回收缓存、swap等,如果这些手段都用尽了,还是没有收集到足够的物理内存,那么就只能使出最后一招了,OOM Killer,通过杀死进程来回收内存。代码实现在linux-src/mm/oom_kill.c:out_of_memory,触发点在linux-src/mm/page_alloc.c:__alloc_pages_may_oom,当使用各种方法都回收不到不到内存时会调用out_of_memory函数。

out_of_memory函数的实现还是有点复杂,我们把各种检测代码和辅助代码都去除之后,高度简化之后的函数如下:


bool out_of_memory(struct oom_control *oc)
{
    select_bad_process(oc);
    oom_kill_process(oc, "Out of memory");
}

这样就看逻辑就很简单了,

  • 1先选择一个要杀死的进程
  • 2杀死它,就是这么简单。
    oom_kill_process函数的目的很简单,但是实现过程也有点复杂,这里就不展开分析了,大家可以自行去看一下代码。我们重点分析一下select_bad_process函数的逻辑,select_bad_process主要是依靠oom_score来进行进程选择的。我们先来看一下和每一个进程相关联的三个文件。

/proc//oom_score系统计算出来的oom_score值,只读文件,取值范围0 –- 1000,0代表never kill,1000代表aways kill,值越大,进程被选中的概率越大。

/proc//oom_score_adj
让用户空间调节oom_score之值的接口,root可读写,取值范围 -1000 — 1000,默认为0,若为 -1000,则oom_score加上此值一定小于等于0,从而变成never kill进程。OS可以把一些关键的系统进程的oom_score_adj设为-1000,从而避免被oom kill。

/proc//oom_adj
旧的接口文件,为兼容而保留,root可读写,取值范围 -16 — 15,会被线性映射到oom_score_adj,特殊值 -17代表 OOM_DISABLE,大家尽量不用再用此接口。

下面我们来分析一下select_bad_process函数的实现:


static void select_bad_process(struct oom_control *oc)
{
  oc->chosen_points = LONG_MIN;
  struct task_struct *p;

  rcu_read_lock();
  for_each_process(p)
    if (oom_evaluate_task(p, oc))
      break;
  rcu_read_unlock();
}

函数首先把chosen_points初始化为最小的Long值,这个值是用来比较所有的oom_score值,最后谁的值最大就选中哪个进程。然后函数已经遍历所有进程,计算其oom_score,并更新chosen_points和被选中的task,有点类似于选择排序。我们继续看oom_evaluate_task函数是如何评估每个进程的函数。

static int oom_evaluate_task(struct task_struct *task, void *arg)
{
  struct oom_control *oc = arg;
  long points;
  if (oom_unkillable_task(task))
    goto next;
  /* p may not have freeable memory in nodemask */
  if (!is_memcg_oom(oc) && !oom_cpuset_eligible(task, oc))
    goto next;
  if (oom_task_origin(task)) {
    points = LONG_MAX;
    goto select;
  }
  points = oom_badness(task, oc->totalpages);
  if (points == LONG_MIN || points < oc->chosen_points)
    goto next;
select:
  if (oc->chosen)
    put_task_struct(oc->chosen);
  get_task_struct(task);
  oc->chosen = task;
  oc->chosen_points = points;
next:
  return 0;
abort:
  if (oc->chosen)
    put_task_struct(oc->chosen);
  oc->chosen = (void *)-1UL;
  return 1;
}

此函数首先会跳轨所有不适合kill的进程,如init进程、内核线程、OOM_DISABLE进程等。然后通过select_bad_process算出此进程的得分points 也就是oom_score,并和上一次的胜出进程进行比较,如果小的会话就会goto next 返回,如果大的话就会更新oc->chosen 的task 和 chosen_points 也就是目前最高的oom_score。那么 oom_badness是如何计算的呢?
在这里插入图片描述
(1)对某一个task进行打分(oom_score)主要有两部分组成,一部分是系统打分,主要是根据该task的内存使用情况。另外一部分是用户打分,也就是oom_score_adj了,该task的实际得分需要综合考虑两方面的打分。如果用户将该task的 oom_score_adj设定成OOM_SCORE_ADJ_MIN(-1000)的话,那么实际上就是禁止了OOM killer杀死该进程。这里看好像和前面的检测冗余了,但是实际上这个函数还被/proc//oom_score的show函数调用用来显示数值,所以还是有必要的,这里也说明了一点,oom_score的值是不保留的,每次都是即时计算。

(2)这里返回了0也就是告知OOM killer,该进程是“good process”,不要干掉它。后面我们可以看到,实际计算分数的时候最低分是1分。

(3)前面说过了,系统打分就是看物理内存消耗量,主要是三部分,RSS部分,swap file或者swap device上占用的内存情况以及页表占用的内存情况。

(4)root进程有3%的内存使用特权,因此这里要减去那些内存使用量。

(5)用户可以调整oom_score,具体如何操作呢?oom_score_adj的取值范围是-1000~1000,0表示用户不调整oom_score,负值表示要在实际打分值上减去一个折扣,正值表示要惩罚该task,也就是增加该进程的oom_score。在实际操作中,需要根据本次内存分配时候可分配内存来计算(如果没有内存分配约束,那么就是系统中的所有可用内存,如果系统支持cpuset,那么这里的可分配内存就是该cpuset的实际额度值)。oom_badness函数有一个传入参数totalpages,该参数就是当时的可分配的内存上限值。实际的分数值(points)要根据oom_score_adj进行调整,例如如果oom_score_adj设定-500,那么表示实际分数要打五折(基数是totalpages),也就是说该任务实际使用的内存要减去可分配的内存上限值的一半。

了解了oom_score_adj和oom_score之后,应该是尘埃落定了,oom_adj是一个旧的接口参数,其功能类似oom_score_adj,为了兼容,目前仍然保留这个参数,当操作这个参数的时候,kernel实际上是会换算成oom_score_adj,有兴趣的同学可以自行了解,这里不再细述了。

3 oom配置参数

3.1 panic_on_oom

当kernel遇到OOM的时候,可以有两种选择:

(1)产生kernel panic(就是死给你看)。

(2)积极面对人生,选择一个或者几个最“适合”的进程,启动OOM killer,干掉那些选中的进程,释放内存,让系统勇敢的活下去。

panic_on_oom这个参数就是控制遇到OOM的时候,系统如何反应的。当该参数等于0的时候,表示选择积极面对人生,启动OOM killer。当该参数等于2的时候,表示无论是哪一种情况,都强制进入kernel panic。panic_on_oom等于其他值的时候,表示要区分具体的情况,对于某些情况可以panic,有些情况启动OOM killer。kernel的代码中,enum oom_constraint 就是一个进一步描述OOM状态的参数。系统遇到OOM总是有各种各样的情况的,kernel中定义如下:
在这里插入图片描述
对于UMA而言, oom_constraint永远都是CONSTRAINT_NONE,表示系统并没有什么约束就出现了OOM,不要想太多了,就是内存不足了。在NUMA的情况下,有可能附加了其他的约束导致了系统遇到OOM状态,实际上,系统中还有充足的内存。这些约束包括:

(1)CONSTRAINT_CPUSET。cpusets是kernel中的一种机制,通过该机制可以把一组cpu和memory node资源分配给特定的一组进程。这时候,如果出现OOM,仅仅说明该进程能分配memory的那个node出现状况了,整个系统有很多的memory node,其他的node可能有充足的memory资源。

(2)CONSTRAINT_MEMORY_POLICY。memory policy是NUMA系统中如何控制分配各个memory node资源的策略模块。用户空间程序(NUMA-aware的程序)可以通过memory policy的API,针对整个系统、针对一个特定的进程,针对一个特定进程的特定的VMA来制定策略。产生了OOM也有可能是因为附加了memory policy的约束导致的,在这种情况下,如果导致整个系统panic似乎有点不太合适吧。

(3)CONSTRAINT_MEMCG。MEMCG就是memory control group,Cgroup这东西太复杂,这里不适合多说,Cgroup中的memory子系统就是控制系统memory资源分配的控制器,通俗的将就是把一组进程的内存使用限定在一个范围内。当这一组的内存使用超过上限就会OOM,在这种情况下的OOM就是CONSTRAINT_MEMCG类型的OOM。

OK,了解基础知识后,我们来看看内核代码。内核中sysctl_panic_on_oom变量是和/proc/sys/vm/panic_on_oom对应的,主要的判断逻辑如下:
在这里插入图片描述

3.2 oom_kill_allocating_task

当系统选择了启动OOM killer,试图杀死某些进程的时候,又会遇到这样的问题:干掉哪个,哪一个才是“合适”的哪那个进程?系统可以有下面的选择:

(1)谁触发了OOM就干掉谁

(2)谁最“坏”就干掉谁

oom_kill_allocating_task这个参数就是控制这个选择路径的,当该参数等于0的时候选择(2),否则选择(1)。

3.3 oom_dump_tasks

当系统的内存出现OOM状况,无论是panic还是启动OOM killer,做为系统管理员,你都是想保留下线索,找到OOM的root cause,例如dump系统中所有的用户空间进程关于内存方面的一些信息,包括:进程标识信息、该进程使用的total virtual memory信息、该进程实际使用物理内存(我们又称之为RSS,Resident Set Size,不仅仅是自己程序使用的物理内存,也包含共享库占用的内存),该进程的页表信息等等。拿到这些信息后,有助于了解现象(出现OOM)之后的真相。

当设定为0的时候,上一段描述的各种进程们的内存信息都不会打印出来。在大型的系统中,有几千个进程,逐一打印每一个task的内存信息有可能会导致性能问题(要知道当时已经是OOM了)。当设定为非0值的时候,在下面三种情况会调用dump_tasks来打印系统中所有task的内存状况:

(1)由于OOM导致kernel panic

(2)没有找到适合的“bad”process

(3)找适合的并将其干掉的时候

4 安卓LMK简介

除了OOM Killer,Android上还开发了low memory killer机制,我们在此也简单介绍一下。LMK是在系统内存较低时就开始杀进程,而不是等到内存不足时再杀。LMK复用了OOM Killer 的 /proc//oom_score_adj 文件接口,但是没有使用/proc//oom_score。LMK仅根据oom_score_adj值的大小选择杀进程,而不会考虑进程本身占用内存的大小。apk进程的oom_score_adj的值由AMS根据apk的生命周期和其他一些因素进行设置,会动态变。apk进程的oom_score_adj都大于等于0,native进程的oom_score_adj的值由rc文件设置或者继承自父进程,一般都是静态的,不会变化,其值一般都小于0。很多重要的系统进程的oom_score_adj值为 -1000,在oom killer的情况下也免杀。LMK默认只管理oom_score_adj大于等于0的进程,所以只能杀死apk进程。

LMK的优点是,1.它在系统内存开始紧张时就开始杀进程,而不是拖到最后一刻一点内存都没有了才去杀进程,2.安卓framework对apk的运行状态很了解,知道哪个进程重要不重要,哪个进程处于什么状态,能更针对性的选择杀哪些进程。LMK和OOM Killer 共同构成了系统内存不足的两道防线,LMK在前,内存有些不足时就杀进程,OOM killer在后,作为最后一道屏障,作最后的努力去回收内存。

5 总结

Linux内存管理是一门庞大的学问,内存回收作为其中的一部分也是十分复杂的,我们今天给大家大概介绍了内核的内存回收概览,并详细的介绍了OOM Killer机制,也算是抛砖引玉让大家对内存回收有个初步的认识。另外如果你在工作中遇到你的进程莫名其妙挂掉了,如果你能在内核log中找到OOM Killer的log的话(搜索 out of memory 关键字并过滤你的进程名),那么你就可以快速的断定你的是因为系统内存不足了,而且你的进程占用物理内存最多,所以被杀了,此时你就有很大的理由怀疑自己的进程内存泄漏了,就可以开始进行内存相关问题的排查了。

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

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

相关文章

数据库,计算机网络、操作系统刷题笔记25

数据库&#xff0c;计算机网络、操作系统刷题笔记25 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

Java实现栈结构

目录 一、栈概述 二、模拟实现栈 1、入栈 2、出栈 3、取栈顶元素 三、栈的应用 1、逆序打印链表 2、括号匹配问题 3、逆波兰表达式求值 4、栈的压入、弹出序列 5、最小栈 一、栈概述 栈&#xff08;Stack&#xff09;也是数据结构的一种&#xff0c;属于线性数…

Usaco Training刷怪旅 第三层 第五题:Wormholes

美国人出的题真的难&#xff08;个人感觉&#xff09;&#xff0c;看起来还行&#xff0c;做起来就是另外一会儿事儿了 Farmer Johns hobby of conducting high-energy physics experiments on weekends has backfired, causing N wormholes (2 < N < 12, N even) to ma…

基于物体基元空间几何特征的移动激光雷达点云街道树提取与分割

paper题目&#xff1a;Street Tree Extraction and Segmentation from Mobile LiDAR Point Clouds Based on Spatial Geometric Features of Object Primitives Abstract 从移动光探测与测距(LiDAR)点云中提取行道树仍然面临挑战&#xff0c;如在复杂的城市环境中提取精度低、…

浅谈哨兵机制的原理

文章目录哨兵机制的基本流程监控:主观下线&#xff1a;客观下线&#xff1a;选主&#xff1a;筛选&#xff1a;打分&#xff1a;通知&#xff1a;哨兵机制的基本流程 哨兵其实就是一个运行在特殊模式下的 Redis 进程&#xff0c;主从库实例运行的同时&#xff0c;它也在运行。…

el-input中放入elbutton

如图&#xff0c;如何在element组建的el-input的后缀放一个可点击的按钮或者标签 <el-input><el-button style"padding-right:10px" slot"suffix" type"text" >选择</el-button></el-input>在el-input的官网介绍中&…

jina实现并发扩展的调研

基于之前对于clip-as-service的调研&#xff0c;我在官方文档中看到横向扩展页面中的副本相关内容&#xff0c;可以解决并发问题&#xff0c;于是动手验证了一番 参考链接 &#xff08;官方文档&#xff09;link 官方文档的描述 首先我整了一个服务端 如果需要开启副本&…

线段相交判断

一、问题描述已知两条线段P1P2和Q1Q2&#xff0c;判断P1P2和Q1Q2是否相交&#xff0c;若相交&#xff0c;求出交点。两条线段的位置关系可以分为三类&#xff1a;[1] 有重合部分;[2] 无重合部分但有交点;[3] 无交点。注意&#xff1a;这里讨论的是两条线段是否相交&#xff0c;…

典型相关分析(附SPSS操作)

典型相关分析&#xff1a;研究两组变量&#xff08;每个变量中都可能有多个指标&#xff09;之间相关关系的一种多元统计方法。他能够揭示出两组变量之间的内在联系。选能较为综合、全面的衡量所在组的内在规律。一组变量最简单的综合形式就是该组变量的线性组合。典型相关分析…

还在用破-解版Navicat?有款纯Web化SQL开发工具,免安装还免费

经常使用SQL工具的开发者对Navicat一定都不陌生。这款软件作为一款全球化的多数据库管理工具&#xff0c;这些年逐步得到全国各地SQLer&#xff08;SQL开发者&#xff09;的关注。 与其他很多外来的软件产品一样&#xff0c;由于价格原因&#xff0c;很多SQLer感觉不太适合适应…

Maven(通用结构,集合了测试、打包、发布功能为一体)

Maven基础&#xff1a; 作用&#xff1a; 1.提供一套标准化的项目结构&#xff08;用于例如idea导入到eclipse或其他软件中&#xff0c;项目结构不会紊乱&#xff09; 2.提供一套标准化构建流程&#xff08;编译、测试、打包、发布...&#xff09;&#xff08;右键Maven-run…

VS中的cmake

新建cmake项目要保证VS安装了SDK&#xff0c;这里是VS2019版本打开创建新项目——查找cmake——设置路径和项目名称新建项目下有三个文件&#xff1a;&#xff08;1&#xff09;与项目同名的cpp文件&#xff08;2&#xff09;与项目同名的h文件&#xff08;3&#xff09;cmake的…

Linux文件/文件夹权限详解

在Linux中&#xff0c;一个文件/文件夹的权限&#xff0c; 从文件/文件夹的归属来看&#xff0c;可以分为三类&#xff0c;一是文件/文件夹所有者权限、二是所有者所在的用户组权限、三是公共&#xff08;不限&#xff09;权限。 从文件本身的操作来看&#xff0c;也可以分为…

SpringBoot+VUE前后端分离项目学习笔记 - 【15 SpringBoot和Vue实现注册和异常处理】

前端代码 Header.vue 获取登录用户的昵称在Header进行显示&#xff0c;加入个人信息的路由 <template><div style"line-height: 60px; display: flex"><div style"flex: 1;"><span :class"collapseBtnClass" style"…

vue 父子组件通讯时执行的生命周期的顺序

Vue的生命周期 Vue的生命周期分为三个阶段&#xff1a;初始化阶段&#xff0c;更新数据阶段&#xff0c;销毁实例阶段 1&#xff0c;初始化阶段&#xff1a; beforeCreate()实例创建前&#xff1a;数据和模板均未获取到created()实例创建后&#xff1a;最早可以访问到data数…

sqlmap之绕过安全狗

sql注入不会绕过WAF&#xff1f;关注我&#xff0c;让我带你由简入难实战各个WAF&#xff0c;今天先来看看web安全渗透必会的安全狗WAF&#xff0c;你会绕吗&#xff1f;看我带你将它拿下 目录 一&#xff1a;环境配置 1.sqli-labs的sql注入靶场环境 2.安全狗waf软件 3.检测…

nrm使用详解

目录1. 什么是 nrm2. 安装 nrm3. 使用 nrm3.1 查看所有源列表3.2 切换源3.3 添加源3.4 删除源3.5 查看所有源的响应速率1. 什么是 nrm npm 的源管理器&#xff0c;切换下载安装 项目依赖 时的源地址。默认包含 npm yarn tencent cnpm taobao npmMirror&#xff0c;支持添加、删…

docker安装kafka

1.下载镜像 docker pull wurstmeister/kafka docker pull zookeeper:latest 2.启动镜像 1&#xff09;启动zookeeper docker run -d -p2181:2181 -v /etc/localtime:/etc/localtime --name zookeeper zookeeper:latest2)启动kafka docker run -d -p9092:9092 \-e KAFKA_ZO…

Twitter开发者账号申请

Twitter开发者账号申请 前期准备 Twitter绑定手机号 申请开发者账号要求绑定手机号&#xff0c;建议境外手机号&#xff0c;国内手机号容易出现收不到验证码的问题。一个可以正常使用的Twitter账号 被封的不可以。 开始注册 注册开发者管理平台账号 登录Twitter开发者管理…

STM32——ADC模数转换器

文章目录一、ADC模数转化器ADC简介逐次逼近型ADCADC框图二、ADC基本结构三、触发转换控制四、输入通道五、规则组的四种转换模式单次转换&#xff0c;非扫描模式连续转换&#xff0c;非扫描模式单次转换&#xff0c;扫描模式连续转换&#xff0c;扫描模式六、数据对齐七、转换时…