Linux ARM64架构 动态替换 altinstructions

news2024/11/25 16:49:20

文章目录

  • 简介
  • 一、altinstructions节
    • 1.1 .altinstructions
    • 1.2 .rela.altinstructions
  • 二、内核模块重定位源码分析
  • 参考资料

简介

在内核开发中,有时需要对内核代码进行修补,以解决bug、优化性能或引入新功能。替代指令(altinstructions)提供了一种在不修改原始代码的情况下进行修补的方法。它允许开发者在原始指令的位置插入替代指令,以实现所需的功能变更。

内核版本:4.19.90
处理器架构:aarch64

以vfat.ko模块为例:

# readelf -S vfat.ko
There are 35 section headers, starting at offset 0x6c08:

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  ......
  [10] .altinstructions  PROGBITS         0000000000000000  00003768
       0000000000000078  0000000000000000   A       0     0     1
  [11] .rela.altinstruct RELA             0000000000000000  000037e0
       00000000000001e0  0000000000000018   I      31    10     8
  ......

我目前这台机器arm64 架构的OS的内核模块没有 .altinstr_replacement 节(还是arm64 架构的 Linux 都没有.altinstr_replacement 节,这点有待确定)。

一、altinstructions节

1.1 .altinstructions

.altinstructions 是 Linux 内核中的一个特殊节(section),用于定义指令替换规则。它允许内核在运行时替换特定的汇编指令序列,以提高性能或解决特定的问题。

指令替换的规则包括两个部分:原始指令序列和替换指令序列。内核在执行时会检查原始指令序列是否匹配,如果匹配则使用替换指令序列来取代它。

.altinstructions 节主要用于在 Linux 内核中进行指令替换或修补。它提供了在运行时替换特定指令序列的机制,常用于优化关键代码路径或解决硬件问题。

struct alt_instr {
	s32 orig_offset;	/* offset to original instruction */
	s32 alt_offset;		/* offset to replacement instruction */
	u16 cpufeature;		/* cpufeature bit set for replacement */
	u8  orig_len;		/* size of original instruction(s) */
	u8  alt_len;		/* size of new instruction(s), <= orig_len */
};

(1)orig_offset(s32类型):原始指令的偏移量。指示原始指令在代码段中的位置。
(2)alt_offset(s32类型):替换指令的偏移量。指示替换指令在代码段中的位置。
(3)cpufeature(u16类型):替换指令所需的CPU特性位。这个字段用于在替换指令被应用时检查CPU是否支持相应的特性。
(4)orig_len(u8类型):原始指令的长度。表示原始指令所占用的字节数。
(5)alt_len(u8类型):新指令的长度。表示替换指令所占用的字节数。这个值必须小于等于 orig_len,以确保替换后的指令不会超出原始指令的范围。

.altinstructions 节中保存了 struct alt_instr 结构体数组。数组中的每一个元素代表了一条替换指令记录,给出了原始指令的位置、长度和用于修补原始指令的新指令的位置、长度。

这个结构体用于在 Linux 内核的 .altinstructions 节中定义指令替换规则。每个结构体实例表示一条指令的替换规则,其中包含原始指令和替换指令的相关信息。通过使用这些结构体,内核可以在运行时根据需要进行指令替换,以优化性能或解决特定的硬件问题。

# readelf -x 10 vfat.ko.altinstructions”节的十六进制输出:
 NOTE: This section has relocations against it, but these have NOT been applied to this dump.
  0x00000000 00000000 00000000 05000c0c 00000000 ................
  0x00000010 00000000 05000c0c 00000000 00000000 ................
  0x00000020 05000c0c 00000000 00000000 05000c0c ................
  0x00000030 00000000 00000000 05000c0c 00000000 ................
  0x00000040 00000000 05000c0c 00000000 00000000 ................
  0x00000050 05000c0c 00000000 00000000 05000c0c ................
  0x00000060 00000000 00000000 05000c0c 00000000 ................
  0x00000070 00000000 05000c0c                   ........
struct alt_instr {
	s32 orig_offset;	/* offset to original instruction */
	s32 alt_offset;		/* offset to replacement instruction */
	u16 cpufeature;		/* cpufeature bit set for replacement */
	u8  orig_len;		/* size of original instruction(s) */
	u8  alt_len;		/* size of new instruction(s), <= orig_len */
};
sizeof(struct alt_instr) = 12

“.altinstructions”节 都是存放struct alt_instr结构体数据

因此这个节存放了 10 个struct alt_instr结构体。

1.2 .rela.altinstructions

.rela.altinstructions节是一个重定位节,用于存储.altinstructions节中数据结构的重定位信息。

在Linux ELF(Executable and Linkable Format)文件中,重定位节(Relocation Section)是用于存储链接器在链接过程中需要进行地址修正的信息。重定位节包含了需要修改的符号引用和相关的重定位类型。

重定位节中都是未定义的符号,即不是本模块定义的符号,因此这些符号的地址在内核模块加载时需要进行重新处理。

重定位节的名称通常以 “.rel” 或 “.rela” 开头,后面跟随符号表中相关的节名称。例如,“.rel.text” 表示与代码段(.text)相关的重定位信息。

在链接过程中,链接器会根据符号引用和重定位类型,将重定位节中的每个重定位项应用于对应的位置,修正地址或符号引用。

重定位节的结构和格式可以因不同的体系结构和文件格式而有所不同,但通常包含以下信息:
(1)Offset(偏移量):指定需要修正的位置在节中的偏移量。
(2)Symbol Index(符号索引):指定需要修正的符号引用在符号表中的索引。
(3)Type(类型):指定重定位的类型,如绝对重定位、PC相对重定位等。
(4)Addend(增量):一些重定位类型需要额外的增量值,用于计算最终的修正值。

/* Relocation table entry with addend (in section of type SHT_RELA).  */

typedef struct
{
  Elf64_Addr	r_offset;		/* Address */
  Elf64_Xword	r_info;			/* Relocation type and symbol index */
  Elf64_Sxword	r_addend;		/* Addend */
} Elf64_Rela;
sizeof(Elf64_Rela) = 24
# readelf -x 11 vfat.ko.rela.altinstructions”节的十六进制输出:
  0x00000000 00000000 00000000 05010000 02000000 ................
  0x00000010 e0040000 00000000 04000000 00000000 ................
  0x00000020 05010000 02000000 28210000 00000000 ........(!......
  0x00000030 0c000000 00000000 05010000 02000000 ................
  0x00000040 5c070000 00000000 10000000 00000000 \...............
  0x00000050 05010000 02000000 34210000 00000000 ........4!......
  0x00000060 18000000 00000000 05010000 02000000 ................
  0x00000070 54080000 00000000 1c000000 00000000 T...............
  0x00000080 05010000 02000000 40210000 00000000 ........@!......
  0x00000090 24000000 00000000 05010000 02000000 $...............
  0x000000a0 24090000 00000000 28000000 00000000 $.......(.......
  0x000000b0 05010000 02000000 4c210000 00000000 ........L!......
  0x000000c0 30000000 00000000 05010000 02000000 0...............
  0x000000d0 f41a0000 00000000 34000000 00000000 ........4.......
  0x000000e0 05010000 02000000 58210000 00000000 ........X!......
  0x000000f0 3c000000 00000000 05010000 02000000 <...............
  0x00000100 b41c0000 00000000 40000000 00000000 ........@.......
  0x00000110 05010000 02000000 64210000 00000000 ........d!......
  0x00000120 48000000 00000000 05010000 02000000 H...............
  0x00000130 e01e0000 00000000 4c000000 00000000 ........L.......
  0x00000140 05010000 02000000 70210000 00000000 ........p!......
  0x00000150 54000000 00000000 05010000 02000000 T...............
  0x00000160 341f0000 00000000 58000000 00000000 4.......X.......
  0x00000170 05010000 02000000 7c210000 00000000 ........|!......
  0x00000180 60000000 00000000 05010000 02000000 `...............
  0x00000190 28200000 00000000 64000000 00000000 ( ......d.......
  0x000001a0 05010000 02000000 88210000 00000000 .........!......
  0x000001b0 6c000000 00000000 05010000 02000000 l...............
  0x000001c0 84200000 00000000 70000000 00000000 . ......p.......
  0x000001d0 05010000 02000000 94210000 00000000 .........!......

计算得到该重定位节中有20个Elf64_Rela结构体数据。

# readelf -r vfat.ko | grep -A 25 .rela.altinstructions
重定位节 '.rela.altinstructions' at offset 0x37e0 contains 20 entries:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000000  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 4e0
000000000004  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2128
00000000000c  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 75c
000000000010  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2134
000000000018  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 854
00000000001c  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2140
000000000024  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 924
000000000028  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 214c
000000000030  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 1af4
000000000034  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2158
00000000003c  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 1cb4
000000000040  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2164
000000000048  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 1ee0
00000000004c  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2170
000000000054  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 1f34
000000000058  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 217c
000000000060  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2028
000000000064  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2188
00000000006c  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2084
000000000070  000200000105 R_AARCH64_PREL32  0000000000000000 .text + 2194

在这里插入图片描述

其中.text + 4e0、.text + 75、.text + 854等都是对应的 BL 函数跳转指令:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里的__ll_sc___cmpxchg_case_acq_4函数和__ll_sc___cmpxchg_case_mb_8不属于vfat.ko内核模块中的函数,因此需要重定位来获取对应函数的位置。

# cat /proc/kallsyms | grep '\<__ll_sc___cmpxchg_case_acq_4\>'
ffff3c3a3fa01bf0 T __ll_sc___cmpxchg_case_acq_4
# cat /proc/kallsyms | grep '\<__ll_sc___cmpxchg_case_mb_8\>'
ffff3c3a3fa01d28 T __ll_sc___cmpxchg_case_mb_8

可以看到这两个函数都属于内核镜像中定义的函数。

aarch64一条指令四个字节,bl是函数调用指令,比如一个内核模块调用内核镜像或者其他模块的函数:
vfat.ko内核模块中函数调用内核的函数kmem_cache_alloc_trace:

94000000        bl      0 <kmem_cache_alloc_trace>
941001 0100

在这里插入图片描述

上面提到.altinstructions有10 个struct alt_instr结构体,.rela.altinstructions节中有20个Elf64_Rela结构体数据。

其中.text + 2128、.text + 2134、.text + 2140等对应的指令如下所示:
在这里插入图片描述
在这里插入图片描述
这10个重定位项都是在 .text 代码段的末尾,对应的指令都是:

MOV             X30, X1

对于 x30 寄存器:

在ARM64体系结构中,寄存器 x30 是通用寄存器之一,也称为"General Purpose Register"。ARM64体系结构共有31个通用寄存器,编号从x0到x30。

寄存器 x30 在ARM64体系结构中有一个特殊的角色,它被称为"链接寄存器"(Link Register),也经常以 “lr” 的缩写表示。链接寄存器用于存储函数的返回地址,在函数调用过程中起到重要的作用。

当一个函数被调用时,当前函数的返回地址(即调用该函数的指令的下一条指令的地址)会被保存在链接寄存器 x30 中。函数执行完毕后,通过将链接寄存器中的返回地址装载到程序计数器(PC)中,控制流程可以返回到调用函数的位置。

链接寄存器 x30 还可以在函数中用作通用寄存器,存储临时数据、地址计算和数据传输等。但需要注意的是,一旦在函数中使用链接寄存器存储其他数据,必须在函数返回之前将其恢复为正确的返回地址,以确保函数返回到正确的位置。

因此我们可以知道 .altinstructions 节中有10 个struct alt_instr结构体,也就是10处指令要替换,且都是BL函数调用替换,因此会有相应的 RET 函数返回,因此.rela.altinstructions节中有20个Elf64_Rela结构体数据。每一个 .altinstructions 节中的struct alt_instr结构体对应一个 BL和一个RET。

二、内核模块重定位源码分析

// linux-4.19.90/kernel/module.c

SYSCALL_DEFINE3(init_module......)
	-->load_module()
		-->post_relocation()
			   /* Arch-specific module finalizing. */
			-->module_finalize()

其中module_finalize是一个与体系架构有关的函数,这里我们主要关注 aach64位架构:

typedef struct elf64_shdr {
  Elf64_Word sh_name;		/* Section name, index in string tbl */
  Elf64_Word sh_type;		/* Type of section */
  Elf64_Xword sh_flags;		/* Miscellaneous section attributes */
  Elf64_Addr sh_addr;		/* Section virtual addr at execution */
  Elf64_Off sh_offset;		/* Section file offset */
  Elf64_Xword sh_size;		/* Size of section in bytes */
  Elf64_Word sh_link;		/* Index of another section */
  Elf64_Word sh_info;		/* Additional section information */
  Elf64_Xword sh_addralign;	/* Section alignment */
  Elf64_Xword sh_entsize;	/* Entry size if section holds table */
} Elf64_Shdr;
// linux-4.19.90/arch/arm64/kernel/module.c

int module_finalize(const Elf_Ehdr *hdr,
		    const Elf_Shdr *sechdrs,
		    struct module *me)
{
	const Elf_Shdr *s, *se;
	const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;

	for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
		if (strcmp(".altinstructions", secstrs + s->sh_name) == 0)
			apply_alternatives_module((void *)s->sh_addr, s->sh_size);
	}

	return 0;
}
// linux-4.19.90/arch/arm64/kernel/alternative.c

void apply_alternatives_module(void *start, size_t length)
{
	struct alt_region region = {
		.begin	= start,
		.end	= start + length,
	};

	__apply_alternatives(&region, true);
}
struct alt_instr {
	s32 orig_offset;	/* offset to original instruction */
	s32 alt_offset;		/* offset to replacement instruction */
	u16 cpufeature;		/* cpufeature bit set for replacement */
	u8  orig_len;		/* size of original instruction(s) */
	u8  alt_len;		/* size of new instruction(s), <= orig_len */
};

struct alt_region {
	struct alt_instr *begin;
	struct alt_instr *end;
};

typedef void (*alternative_cb_t)(struct alt_instr *alt,
				 __le32 *origptr, __le32 *updptr, int nr_inst);

static void __apply_alternatives(void *alt_region, bool is_module)
{
	struct alt_instr *alt;
	struct alt_region *region = alt_region;
	__le32 *origptr, *updptr;
	alternative_cb_t alt_cb;

	for (alt = region->begin; alt < region->end; alt++) {
		int nr_inst;

		/* Use ARM64_CB_PATCH as an unconditional patch */
		if (alt->cpufeature < ARM64_CB_PATCH &&
		    !cpus_have_cap(alt->cpufeature))
			continue;

		if (alt->cpufeature == ARM64_CB_PATCH)
			BUG_ON(alt->alt_len != 0);
		else
			BUG_ON(alt->alt_len != alt->orig_len);

		pr_info_once("patching kernel code\n");

		origptr = ALT_ORIG_PTR(alt);
		updptr = is_module ? origptr : lm_alias(origptr);
		nr_inst = alt->orig_len / AARCH64_INSN_SIZE;

		if (alt->cpufeature < ARM64_CB_PATCH)
			alt_cb = patch_alternative;
		else
			alt_cb  = ALT_REPL_PTR(alt);

		alt_cb(alt, origptr, updptr, nr_inst);
	
		......
	}
	
	......

}

函数的参数 alt_region 是一个指向替代指令区域的指针,is_module 是一个布尔值,表示是否为模块代码,这里传入的是 true ,表示是模块代码。

函数通过循环遍历 alt_region (.altinstructions)中的每个替代指令,每个替代指令存储在结构体 alt_instr 中,包含原始指令和替代指令的信息。

函数首先检查替代指令的 cpufeature 字段。如果该字段小于 ARM64_CB_PATCH 并且当前 CPU 不支持该特性,则跳过该替代指令。

如果替代指令的 cpufeature 等于 ARM64_CB_PATCH,则需要确保 alt_len(替代指令长度)为零。否则,需要确保 alt_len 和 orig_len(原始指令长度)相等。如果上述条件不满足,则会触发错误(使用 BUG_ON)。

在确定替代指令有效后,函数使用 pr_info_once 记录一条消息,指示正在对内核代码进行修补。

根据 is_module 的值,函数设置 origptr 和 updptr 变量。如果是模块代码,则 origptr 指向原始指令,否则通过 lm_alias 函数获取一个别名。这里是模块代码,origptr 指向原始指令。

通过将替代指令的长度 alt->orig_len 除以 ARM64 指令的大小 AARCH64_INSN_SIZE,计算出指令的数量 nr_inst。

/* A64 instructions are always 32 bits. */
#define	AARCH64_INSN_SIZE		4

根据替代指令的 cpufeature 值,选择相应的回调函数 alt_cb。如果 cpufeature 小于 ARM64_CB_PATCH,则选择 patch_alternative 函数作为回调函数;否则使用 ALT_REPL_PTR(alt) 来确定回调函数。

调用回调函数 alt_cb,并将 alt、origptr、updptr 和 nr_inst 作为参数,来应用替代指令。

这段代码目的是应用替代指令(alternative instructions),用新的指令替换原始指令,根据替代指令的条件和特性选择性地应用替代。它通过调用相应的回调函数来执行替代指令操作。从而修复或改进内核的行为。

参考资料

Linux 4.19.90

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

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

相关文章

云原生K8S------Yaml文件详解

目录 一&#xff1a;K8S支持的文件格式 1&#xff0c;yaml和json的主要区别 2&#xff0c;YAML语言格式 二&#xff1a;yuml 1、查看 api 资源版本标签 2、写一个yaml文件demo 3、创建service服务对外提供访问并测试 4、详解k8s中的port 三&#xff1a;文件生成 1、kubec…

如果你需要使用重试机制,请使用Spring官方的Spring Retry

Spring Retry 是 Spring Framework 中的一个模块&#xff0c;提供了一种简单的方式来在应用程序中实现重试机制。 在应用程序中&#xff0c;如果遇到了一些不可避免的错误&#xff0c;比如网络连接失败、数据库连接失败等&#xff0c;我们通常需要对这些错误进行重试&#xff…

搭建日志服务器Rsyslog

Rsyslog介绍 Rsyslog的全称是 rocket-fast system for log&#xff0c;它提供了高性能&#xff0c;高安全功能和模块化设计。rsyslog能够接受从各种各样的来源&#xff0c;将其输入&#xff0c;输出的结果到不同的目的地。rsyslog可以提供超过每秒一百万条消息给目标文件。 特…

Qt+C++实现灯带动画运动位置变换移动跑马灯图片轮播

程序示例精选 QtC实现灯带动画运动位置变换移动跑马灯图片轮播 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC实现灯带动画运动位置变换移动跑马灯图片轮播>>编写代码&…

Vue项目npm run dev 启动报错TypeError: Cannot read property ‘upgrade‘ of undefined

vue项目启动报错 TypeError: Cannot read property upgrade of undefined 由于我的vue.config.js文件 里面的代理target为空导致的 修改&#xff1a; 结果就可以正常运行了 参考原文&#xff1a; vue项目运行时报Cannot read property ‘upgrade’ of undefined错误_cannot r…

【Docker】Docker的应用场景,Docker 的优点,Ubuntu Docker 安装,使用 Shell 脚本进行安装

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

【福建事业单位-资料分析】01 速算技巧-基期与现期

【福建事业单位-资料分析】01 速算技巧-基期与现期 一、速算技巧&#xff08;基于选项的速算&#xff09;1.1 计算类别——截位直除练习总结 二、速算技巧-比较类-分数比较2.1 一大一小&#xff08;一大/一小&#xff09;2.2 同大同小①分子分母都变大&#xff0c;保留两位直接…

智橙PDM系统:图文档与物料数据同时管理编辑系统

在当今快速变化的商业环境中&#xff0c;数字化转型已经成为企业不可或缺的一部分。产品数据的高效管理和协作变得尤为重要&#xff0c;而智橙的PDM系统&#xff08;产品数据管理系统&#xff09;为企业提供了无限协作的全新维度。 无限协作&#xff0c;创新无限 智橙PDM系统瞄…

QListView的使用(正逆序插入)

首先在介绍QListView之前&#xff0c;先说一下QListView和QListWidget的区别&#xff1a; 1、QListView是model&#xff08;模型/视图&#xff09;表格类型&#xff0c;QListWidget它是Item表格类型。 2、QListView使用较复杂&#xff0c;一般需要配合数据模型QAbstractListMod…

【Linux】公网环境下Ubuntu系统SSH远程树莓派

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于恒川的日常汇报系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏C语言初…

Nginx与Tomcat的区别,什么是HTTP服务器(处理静态资源的服务器),什么是处理动态资源的服务器

Nginx和Tomcat都是常用的Web服务器&#xff0c;但它们的主要作用不同。Nginx是一个HTTP服务器&#xff0c;反向代理服务器和通用TCP/UDP代理服务器。它通常用于静态内容、媒体流和负载均衡。在高流量和高并发负载下&#xff0c;Nginx表现更出色&#xff0c;并且能够轻松处理静态…

解决android studio妙明奇妙出现的模拟器

1&#xff0c;查看设备 adb devices 2&#xff0c; adb命令断开指定的设备 要断开ADB与特定设备的连接&#xff0c;可以使用以下命令&#xff1a; adb disconnect <设备ID> 将 <设备ID> 替换为您要断开连接的设备的实际ID。设备ID可以在运行 adb devices 命令…

C语言刷题------(2)

C语言刷题——————&#xff08;2&#xff09; 刷题网站&#xff1a;题库 - 蓝桥云课 (lanqiao.cn) First Question&#xff1a;时间显示 题目描述 小蓝要和朋友合作开发一个时间显示的网站。 在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表…

Ansible的安装和配置

安装和配置 Ansible 安装所需的软件包 创建名为 /home/greg/ansible/inventory 的静态清单文件&#xff0c;以满足以下要求&#xff1a; 172.25.250.9 是 dev 主机组的成员 172.25.250.10 是 test 主机组的成员 172.25.250.11 和 172.25.250.12 是 prod 主机组的成员 172.2…

电测知识分享——10分钟学会!超火网络应用测试教程来了,火速收藏

在当今的网络应用中&#xff0c;当属以太网最流行&#xff0c;特别是终端电脑的连接几乎是以太网的天下&#xff0c;即使无线网卡最终也是以太网协议。终端网络分为有线以太网和无线WF,目前都形成了IEEE的标准。 今天&#xff0c;我们就来学习一下&#xff0c;1000M BASE-T&am…

war和war exploded

war和war exploded的区别 war模式&#xff1a;将WEB工程以包的形式上传到服务器 &#xff1b; war exploded模式&#xff1a;将WEB工程以当前文件夹的位置关系上传到服务器&#xff1b;>> war包是自己打包生成的&#xff0c;如pom文件中<packaging>war</packag…

【设计模式】责任链的基本概念及使用Predicate灵活构造校验链

文章目录 1. 概述1.1.背景1.2.责任链模式的概念 2.责任链的基本写法2.1.链表实现2.2.数组实现 3.Predicate校验链2.1.使用Predicate改写代码2.1.更丰富的条件拓展 4.总结 1. 概述 1.1.背景 在最近的开发中遇到了这么一个需求&#xff0c;需要对业务流程中的各个参数做前置校验…

社科院与杜兰大学能源管理硕士——环境不会改变,解决之道在于改变自己

随着社会经济的不断发展&#xff0c;职场竞争也愈发激烈、工作要求不断提高&#xff0c;许多从业人员既不想放弃工作&#xff0c;又想提升专业能力&#xff0c;深化对专业知识的理解&#xff0c;获取优质的证书。那么考研便是一个不错的方式。考研的专业有很多&#xff0c;我们…

dubbo之整合SpringBoot

目录 zookeeper安装 1.拉取ZooKeeper镜像 2.新建文件夹 3.挂载本地文件夹并启动服务 4.查看容器 5.进入容器&#xff08;zookeeper&#xff09; Dubbo Admin安装 1.下载dubbo-admin 2.zip包解压 3.修改配置文件 4.打包项目 5.启动jar 6.访问 构建项目 api模块 1.创建…

Tik Tok本土mcn怎么入驻,泰国市场发展概况分析!

近日&#xff0c;TikTok海外本土MCN机构“Ma Go”获得数千万级别的A轮融资&#xff0c;本轮融资由北太平投资独家投资。 2021年10月份&#xff0c;“Ma Go”曾获得过知名投资人以及网红朱一旦的千万级别融资。 Ma Go联合创始人倪泽铭表示&#xff0c;本轮融资将继续扩展申请找…