当前位置: 首页 > news >正文

UNIX网络编程卷一 学习笔记 第十九章 密钥管理套接字

随着IP安全体系结构(IPsec)的引入,密钥加密和认证密钥的管理越来越需要一套标准机制。RFC 2367介绍了一个通用密钥管理API,可用于IPsec和其他网络安全服务,该API创建了一个新协议族,即PF_KEY域,这这个密钥管理域中,只支持原始套接字。

在大多系统上,常值AF_KEY被定义成与PF_KEY有相同的值,但RFC 2367明确密钥管理套接字必须用PF_KEY常值。

打开原始密钥管理套接字需要权限,在权限按需分割的系统上,对于打开密钥管理套接字这样的操作会有一个单独的权限,在普通Unix系统上,密钥管理套接字只有超级用户能打开。

IPsec基于安全关联(SA,Security Association)为分组提供安全服务。SA描述了源地址与目的地址(加上可选的传输协议和端口)、机制(如认证)、密钥素材的组合。一个流上可以有多个SA(如一个用于认证、一个用于加密)。存放在系统中的所有SA构成的集合称为安全关联数据库(SADB,Security Association DataBase)。

一个系统的SADB可能用于IPsec以外的场合,如OSPFv2、RIPv2、RSVP、Mobile-IP等在SADB中也可能有各自的表项。因此PF_KEY套接字不仅限IPsec使用。

IPsec还需要一个安全策略数据库(SPDB,Security Policy DataBase),SPDB描述分组流通的需求,如主机A和主机B之间的分组流通必须通过IPsec AH认证,未经认证的一律丢弃。SADB描述如何执行所需的安全步骤,如主机A和主机B之间的分组流通按照策略在使用IPsec AH,SADB就含有所用的算法和密钥。但SPDB没有标准的维护机制,尽管PF_KEY可以维护SADB,但对SPDB无能为力。KAME的IPsec实现使用PF_KEY的扩展来维护SPDB,但它没有标准可循。

密钥管理套接字上支持3种操作:
1.通过写到密钥管理套接字,进程可以往内核和打开着密钥管理套接字的其他进程发消息。SADB表项的增加和删除也采用这种操作实现,如OSPFv2等自行进行安全检查的进程也采用这种方式从某个密钥管理守护进程请求密钥。

2.通过读密钥管理套接字,进程可以从内核或其他进程接收消息。内核可以采用这种操作请求某个密钥管理守护进程为依照策略需要受到保护的新TCP会话安装一个SA。

3.进程可以往内核发送一个倾泻(dumping)请求,内核作为应答倾泻出当前SADB,这是一个调试功能,并非所有系统都可用。

所有密钥管理套接字的消息都有同样的首部,每个消息后跟各种扩展,取决于可提供的或所请求的信息。所有相关结构都定义在头文件net/pfkeyv2.h中。每个消息和扩展都是64位对齐的,消息长度是8字节的整数倍;所有长度字段都是64位的;每个消息如果不足64位的整数倍,必须填充到下一个64位边界,填充字节的具体值没有定义。以下是密钥管理消息首部:
在这里插入图片描述
sadb_msg_type成员的值确定密钥管理消息类型,可选值如下:
在这里插入图片描述
每个sadb_msg首部后跟零个或多个扩展,大多消息类型都有必需和可选的扩展,以下是所有扩展类型:
在这里插入图片描述
在这里插入图片描述
进程使用SADB_DUMP消息倾泻当前SADB,它是最简单的密钥管理消息,不需要任何扩展,单纯是16字节的sadb_msg首部。一个进程通过某个密钥管理套接字发送一个SADB_DUMP消息到内核后,内核通过同一个套接字响应一系列SADB_DUMP消息,每个消息对应一个SADB表项,这个列表的末尾由成员sadb_msg_seq值为0来指示。

通过把SADB_DUMP请求消息的sadb_msg_satype成员设为下图中的某个值,进程可限制返回的SA的类型,若该成员的值为SADB_SATYPE_UNSPEC常值,则SADB中所有SA都会返回:
在这里插入图片描述
并非所有系统都支持所有SA类型,KAME实现仅支持IPsec的两类SA(SADB_SATYPE_AH和SADB_SATYPE_ESP),因此如果试图倾泻SADB_SATYPE_RIPV2类型的SA,将返回EINVAL错误。如果SADB中没有所请求类型的SA,将返回ENOENT错误。

以下是用于倾泻SADB的程序:

void sadb_dump(int type) {int s;char buf[4096];struct sadb_msg msg;int goteof;// 需要特定系统权限,因为它允许访问敏感的密钥素材s = Socket(PF_KEY, SOCK_RAW, PF_KEY_V2);/* Build and write SADB_DUMP request */bzero(&msg, sizeof(msg));// 调用socket时第三个参数为PF_KEY_V2,因此此处的版本也设为该值msg.sadb_msg_version = PF_KEY_V2;msg.sadb_msg_type = SADB_DUMP;msg.sadb_msg_satype = type;// sadb_msg_len的单位是8字节块msg.sadb_msg_len = sizeof(msg) / 8;// 将sadb_msg_pid设置为自己的进程id// 从进程到内核的所有消息都要以发送者的PID来标识msg.sadb_msg_pid = getpid();printf("Sending dump message:\n");// 使用我们的print_sadb_msg函数显示本消息// 我们不给出该冗长无味的函数的源码,它以直观可读方式// 显示正写到或已读自密钥管理套接字的某个消息print_sadb_msg(&msg, sizeof(msg));Write(s, &msg, sizeof(msg));printf("\nMessages returned:\n");/* Read and print SADB_DUMP replies until done */goteof = 0;while (goteof == 0) {int msglen;struct sadb_msg *msgp;msglen = Read(s, &buf, sizeof(buf));msgp = (struct sadb_msg *)&buf;print_sadb_msg(msgp, msglen);if (msgp->sadb_msg_seq == 0) {goteof = 1;}}close(s);
}int main(int argc, char **argv) {int satype = SADB_SATYPE_UNSPEC;int c;opterr = 0;    /* don't want getopt() writing to stderr */// POSIX的getopt函数的第三个参数指定允许出现的命令行选项字符// 本例中t字符后跟一个冒号,表示这个选项需要一个参数// 如果该参数为Oi:l:v,则表明程序接受4个选项// i和l需要参数O和v不需要参数// getopt函数与在unistd.h头文件中定义的一下4个全局变量协同工作// extern char *optarg;// extern int optind, opterr, optopt;// 在调用getopt前把opterr设为0,表示发生命令行参数与第3个参数不// 匹配等错误时该函数不把出错消息写到标准错误,因为我们想自行处理错误// POSIX声称第3个参数以“:”打头也能阻止函数写出到标准错误,但有些实现不支持while ((c = getopt(argc, argv, "t:")) != -1) {switch (c) {case 't':// 使用我们的getsatypebyname函数从文本串得到类型值// 用于指定倾泻指定的SA类型if ((satype = getsatypebyname(optarg)) == -1) {err_quit("invalid -t option %s", optarg);}break;default:err_quit("unrecognized option: %c", c);}}sadb_dump(satype);
}

在一个具有2个静态SA(预先配置和固定的安全关联)的系统上运行本倾泻程序的输出:
在这里插入图片描述
在这里插入图片描述
向SADB增加一个SA最直接的方法是手动填写所有参数并发送一个SADB_ADD消息。虽然手动指定密钥素材会导致不易更改密钥(易于更改密钥对避免密码分析攻击很重要),但配置起来很容易:Alice和Bob使用带外数据达成一个密钥和算法,然后使用它们。

SADB_ADD消息必需的扩展有3种:SA、地址和、密钥,可选的扩展也有3种:生命期、身份、敏感性。

SA扩展由sadb_sa结构描述:
在这里插入图片描述
sadb_sa_spi成员含有安全参数索引(SPI,Security Parameters Index),SPI结合目的地址、所用协议(如IPsec AH)唯一标识一个SA。接收分组时,SPI用于查找该分组的SA;发送分组时,SPI插入到分组中供对端使用。SPI没有别的含义,因此其值可以顺序地或随机地分配,也可使用目的系统想使用的方法进行分配。sadb_sa_replay指定反重放窗口的大小。sadb_sa_state成员值在动态创建的SA的生命周期内会发生变化,可取值如下:
在这里插入图片描述
手动创建的SA总是处于SADB_SASTATE_MATURE状态。

sadb_sa_auth成员和sadb_sa_encrypt成员本别指定本SA的认证算法和加密算法,可取值如下:
在这里插入图片描述
sadb_sa_flags成员目前只定义了一个标志,即SADB_SAFLAGS_PFS,该标志要求完备前向安全(PFS,perfect forward security),即密钥的值不依赖于先前的密钥或某个主密钥,PFS确保即使长期的私钥泄露,先前的通信内容也无法被解密,在传统的加密协议中,使用的是长期有效的密钥,如果这些密钥被泄露,攻击者可以将其用于解密先前截获的通信数据,而PFS使用的是临时生成的一次性密钥,这些密钥仅用于加密和解密单个会话或通信。该标志值用于从密钥管理守护进程请求密钥的场合,增加静态SA时不用。

除了以上SA扩展外,SADB_ADD消息的另一种必需的扩展是地址扩展。由常值SADB_EXT_ADDRESS_SRC和SADB_EXT_ADDRESS_DST指定的是源地址和目的地址,这两个地址是必需的;而常值SADB_EXT_ADDRESS_PROXY指定的是代理地址,代理地址是可选的。代理地址详细信息可见RFC 2367。地址扩展使用以下sadb_address结构:
在这里插入图片描述
sadb_address结构中的sadb_address_exttype成员确定本地址是源地址、目的地址还是代理地址。sadb_address_proto成员指定本SA匹配的协议,若为0则匹配所有协议。sadb_address_prefixlen成员给出sadb_address结构表示的地址的有效位数(如IPv4是32位)。sadb_address结构后跟匹配地址族的sockaddr结构(如sockaddr_in或sockaddr_in6)。sockaddr中的端口仅在sadb_address_proto指定的协议支持端口号的前提下(如IPPROTO_TCP)才有效。

SADB_ADD消息的最后一种必需的扩展是认证和加密密钥,分别由SADB_EXT_KEY_AUTH和SADB_EXT_KEY_ENCRYPT指定,由sadb_ley结构描述:
在这里插入图片描述
sadb_key_exttype成员定义本密钥是认证密钥还是加密密钥。sadb_key_bits成员指定本密钥的位数。密钥本身紧跟在sadb_key结构后。

增加一个静态SADB表项的代码:

void sadb_add(struct sockaddr *src, struct sockaddr *dst, int type, int alg,int spi, int keybits, unsigned char *keydata) {int s;char buf[4096], *p;    /* XXX */struct sadb_msg *msg;struct sadb_sa *saext;struct sadb_address *addrext;struct sadb_key *keyext;int len;int mypid;s = Socket(PF_KEY, SOCK_RAW, PF_KEY_V2);mypid = getpid();/* Build and write SADB_ADD request */// 构造SADB_ADD消息首部bzero(&buf, sizeof(buf));p = buf;msg = (struct sadb_msg *)p;msg->sadb_msg_version = PF_KEY_V2;msg->sadb_msg_type = SADB_ADD;msg->sadb_msg_satype = type;msg->sadb_msg_pid = getpid();len = sizeof(*msg);p += sizeof(*msg);// 添加必需的SA扩展saext = (struct sadb_sa *)p;saext->sadb_sa_len = sizeof(*saext) / 8;saext->sadb_sa_exttype = SADB_EXT_SA;saext->sadb_sa_spi = htonl(spi);    // 必须以网络字节序存放// 关闭重放保护saext->sadb_sa_replay = 0;    /* no replay protection with static keys */saext->sadb_sa_state = SADB_SASTATE_MATURE;// 设置认证算法saext->sadb_sa_auth = alg;// 设置加密算法saext->sadb_sa_encrypt = SADB_EALG_NONE;saext->sadb_sa_flags = 0;len += saext->sadb_sa_len * 8;p += saext->sadb_sa_len * 8;// 将源地址以SADB_EXT_ADDRESS_SRC扩展形式添加到本消息addrext = (struct sadb_address *)p;// 长度字段先加7再除8,是按64位边界填充后的长度addrext->sadb_address_len = (sizeof(*addrext) + salen(src) + 7) / 8;addrext->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;// 本SA适用于所有协议addrest->sadb_address_proty = 0;    /* any protocol */// 设置地址长度,IPv4为32位,IPv6为128位addrext->sadb_address_prefixlen = prefix_all(src);addrext->sadb_address_reserved = 0;memcpy(addrext + 1, src, salen(src));len += addrext->sadb_address_len * 8;p += addrext->sadb_address_len * 8;// 与源地址一样的方式将目的地址加入本消息// 但sadb_address_exttype为SADB_EXT_ADDRESS_DSTaddrext = (struct sadb_address *)p;addrext->sadb_address_len = (sizeof(*addrext) + salen(dst) + 7) / 8;addrext->sadb_address_exttype = SADB_EXT_ADDRESS_DST;addrext->sadb_address_proto = 0;    /* any protocol */addrext->sadb_address_prefixlen = prefix_all(dst);addrext->sadb_address_reserved = 0;memcpy(addrext + 1, dst, salen(dst));len += addrext->sadb_address_len * 8;p += addrext->sadb_address_len * 8;// 添加认证密钥keyext = (struct sadb_key *)p;/* "+7" handles alignment requirements */keyext->sadb_key_len = (sizeof(*keyext) + (keybits / 8) + 7) / 8;keyext->sadb_key_exttype = SADB_EXT_KEY_AUTH;keyext->sadb_key_bits = keybits;keyext->sadb_key_reserved = 0;// 把密钥数据复制到本扩展首部后面memcpy(keyext + 1, keydata, keybits / 8);len += keyext->sadb_key_len * 8;p += keyext->sadb_ley_len * 8;msg->sadb_msg_len = len / 8;printf("Sending add message:\n");print_sadb_msg(buf, len);Write(s, buf, len);printf("\nReply returned:\n");/* Read and print SADB_ADD reply, discarding any others */for (; ; ) {int msglen;struct sadb_msg *msgp;msglen = Read(s, &buf, sizeof(buf));msgp = (struct sadb_msg *)&buf;// 寻找pid与本进程一致的消息if (msgp->sadb_msg_pid == mypid && msgp->sadb_msg_type == SADB_ADD) {print_sadb_msg(msgp, msglen);break;}}close(s);
}

运行以上程序,发送SADB_ADD消息为127.0.0.1和127.0.0.1之间的分组流通增设一个SA:
在这里插入图片描述
上图中,应答消息中没有给出密钥内容,这是因为应答消息被发送到所有PF_KEY套接字,但不同的套接字可能属于不同的保护域(保护域是一种安全机制,用于隔离和控制不同进程或实体之间的访问权限,每个保护域都有自己的访问规则和权限设置),密钥数据不应该跨越保护域(这是为了防止密钥数据被未经授权的实体或进程访问、修改或泄露)。把这个SA添加到SADB后,我们对127.0.0.1执行ping命令使该SA真正被使用,然后倾泻出SADB以检查所添加的SA:
在这里插入图片描述
在这里插入图片描述
从倾泻的结果可见,内核把我们的IP协议从0改为了255,这是本实现的一个特性(实际是一个缺陷),而非PF_KEY套接字的普遍特性。此外内核把前缀长度由32改为了128(另一个缺陷),它看起来是由内核混淆IPv4和IPv6地址所引起。内核还返回了另一个我们的倾泻程序不认识的扩展(编号19),不认识的扩展我们的倾泻程序会根据它的长度字段跳过。内核还返回了生命期扩展,含有本SA的当前生命期信息,生命期扩展相关的结构如下:
在这里插入图片描述
生命期扩展有3种,SADB_LIFETIME_SOFT和SADB_LIFETIME_HARD这两个扩展分别指定一个SA的软生命期和硬生命期。当软生命期结束时,内核发送一个SADB_EXPIRE消息;当硬生命期结束后,该SA不能再用。最后一种生命期扩展是SADB_LIFETIME_CURRENT扩展,它用于指出相应SA的当前生命期,它会在SADB_DUMP、SADB_EXPIRE、SADB_GET消息中返回。

周期性地重新产生密钥(动态维护安全关联)有助于进一步提高安全性,这种操作通常由诸如IKE之类的协议执行。

为了获悉何时需要为一对主机提供新的SA,密钥管理守护进程应预先用SADB_REGISTER请求消息向内核注册自身,其中的sadb_msg_satype成员指出所能处理的SA类型。如果守护进程能处理多种SA类型,它就为其中每个类型发送一个SADB_REGISTER请求消息。在SADB_REGISTER应答消息中,内核提供一系列受支持算法扩展,用来指出哪些加密和认证机制、哪些密钥长度得到支持。受支持算法扩展由sadb_supported结构描述:
在这里插入图片描述
紧跟在每个sadb_supported结构(相当于该扩展的首部)后的是一系列以sadb_alg结构给出的加密或认证算法的描述。

sadb_supported扩展首部后的每个sadb_alg结构代表系统支持的一个算法,下图是为处理SA类型SADB_SATYPE_ESP 而发出的SADB_REGISTER请求的一个可能应答:
在这里插入图片描述
以下程序使用SADB_REGISTER请求向内核注册自身进程,然后显示内核在应答中返回的受支持算法列表:

void sadb_register(int type) {int s;char buf[4096];    /* XXX */struct sadb_msg msg;int goteof;int mypid;s = Socket(PF_KEY, SOCK_RAW, PF_KEY_V2);mypid = getpid();/* Build and write SADB_REGISTER request */bzero(&msg, sizeof(msg));msg.sadb_msg_version = PF_KEY_V2;msg.sadb_msg_type = SADB_REGISTER;msg.sadb_msg_satype = type;msg.sadb_msg_len = sizeof(msg) / 8;msg.sadb_msg_pid = mypid;printf("Sending register message:\n");print_sadb_msg(&msg, sizeof(msg));Write(s, &msg, sizeof(msg));// SADB_REGISTER请求消息不需要任何扩展printf("\nReply returned:\n");/* Read and print SADB_REGISTER reply, discarding any others */for (; ; ) {int msglen;struct sadb_msg *msgp;msglen = Read(s, &buf, sizeof(buf));msgp = (struct sadb_msg *)&buf;// 应答消息中的pid是本进程的pid,且类型为SADB_REGISTERif (msgp->sadb_msg_pid == mypid &&msgp->sadb_msg_type == SADB_REGISTER) {print_sadb_msg(msgp, msglen);break;}}close(s);
}

在一个不仅仅支持RFC 2367中规定协议的系统上运行以上register程序:
在这里插入图片描述
当内核需要与某个目的地址通信时,如果根据策略该单向分组流必须经由一个SA而内核没有可用SA时,内核就向注册了所需SA类型的密钥管理套接字发送一个SADB_ACQUIRE消息,其中含有一个描述内核所提议算法及密钥长度的提议扩展,该提议可能综合了系统支持的配置与限制该单向分组流的预配置策略。提议内容是一个由算法、密钥长度、生命期所构成的按照优先顺序排列的列表。当一个密钥管理守护进程收到一个SADB_ACQUIRE消息后,它执行必要的操作以选择一个内核提议的密钥,再把该密钥安装到内核中。密钥管理守护进程使用SADB_GETSPI消息请求内核从一个期望的范围内选择一个SPI,内核对于该SADB_GETSPI消息的响应还包括建立一个处于larval(幼虫)状态的SA,然后守护进程使用由内核提供的SPI与远端协商安全参数,接着使用SADB_UPDAE更新该SA,使它进入mature(成熟)状态。动态创建的SA通常含有关联的软生命期和硬生命期,任何一个生命期结束时,内核将发送一个SADB_EXPIRE消息,其中指出期满的是软生命期还是硬生命期,如果软生命期结束,SA就进入dying(垂死)状态,期间它仍可使用,但内核应该为它获取一个新SA;如果硬生命期结束,去SA就进入dead(死亡)状态,这种状态的SA不能继续使用,必须从SADB中删除。

密钥管理套接字用于在内核、密钥管理守护进程、诸如路由守护进程等安全相关的消费进程间交换SA。SA既可以手工静态安装,也可以使用密钥协商协议自动动态安装。动态密钥有关联的生命期,当软生命期结束时,密钥守护进程得到通知,这样的SA如果在硬生命期结束前未被新SA替换,那就不能再使用。

进程和内核通过密钥管理套接字交换的信息共有10种类型,每种消息类型都有关联的扩展,有的扩展是必需的,有的是可选的。每个由进程发送的消息的应答被内核发送到所有打开着的密钥管理套接字,但其中含有敏感数据的扩展都会被抹除。

相关文章:

UNIX网络编程卷一 学习笔记 第十九章 密钥管理套接字

随着IP安全体系结构(IPsec)的引入,密钥加密和认证密钥的管理越来越需要一套标准机制。RFC 2367介绍了一个通用密钥管理API,可用于IPsec和其他网络安全服务,该API创建了一个新协议族,即PF_KEY域,…...

excel如何实现识别文本在对应单元格填上数据?

要实现 Excel 识别文本在对应单元格填上数据,有以下两种方法: 方法一:使用 VLOOKUP 函数 1. 在 Excel 工作表中,输入一个表格,列名为对应的文本,行名为不同条目。 2. 准备输入数据,在一个新的…...

Groovy 基本语法

一、简介 类型转换:当需要时,类型之间会自动发生类型转换: 字符串(String)、基本类型(如int) 和类型的包装类(如Integer) 类说明:如果在一个groovy 文件中没有任何类定义,它将被当做script 来处理,也就意味着这个文件将…...

系统学习IT技术的方法与实践

系统学习IT技术的方法与实践 作为一名技术人员,在学习新的IT技术时,采取系统性的学习方法是至关重要的。这样可以帮助我们更好地理解和掌握技术,并提高学习效率。下面我将分享一些我个人在系统学习IT技术方面的方法和实践。 1. 设定明确的学…...

聊聊TCP协议的粘包、拆包以及http是如何解决的?

目录 一、粘包与拆包是什么? 二、粘包与拆包为什么发生? 三、遇到粘包、拆包怎么办? 解决方案1:固定数据大小 解决方案2:自定义请求协议 解决方案3:特殊字符结尾 四、HTTP如何解决粘包问题的&#xf…...

一百二十、Kettle——用kettle把Hive数据同步到ClickHouse

一、目标 用kettle把hive数据同步到clickhouse,简单运行、直接全量导入数据 工具版本:kettle:8.2 Hive:3.1.2 ClickHouse21.9.5.16 二、前提 (一)kettle连上hive (二)kettle连上cli…...

PyTorch 提示和技巧:从张量到神经网络

张量和梯度 我们将深入探讨使用 PyTorch 构建自己的神经网络必须了解的 2 个基本概念:张量和梯度。 张量 张量是 PyTorch 中的中央数据单元。它们是类似于数组的数据结构,在功能和属性方面与 Numpy 数组非常相似。它们之间最重要的区别是 PyTorch 张量…...

第五期:字符串的一些有意思的操作

文章目录 1. 替换空格2. 字符串的左旋转3. 答案代码3.1 替换空格3.2 字符串的左旋转 PS:每道题解题方法不唯一,欢迎讨论!每道题后都有解析帮助你分析做题,答案在最下面,关注博主每天持续更新。 1. 替换空格 题目描述 请…...

使用Anaconda3结合vscode来实现django项目的建立(绝好的介绍)20230608

问题:如何使用Anaconda3结合vscode来实现django项目的建立? 回答: 知识背景 Anaconda3的安装包默认会安装最新版本的Python解释器。如果您想在安装时指定Python解释器的版本,您需要下载对应版本的Anaconda3。例如,如果您想使用Python 3.7&…...

【软件测试】软件测试的基本概念和开发模型

1. 前言 在进行软件测试的学习之前,我们要了解软件测试一些基本概念. 这些基本概念将帮助我们更加明确工作的目标以及软件测试到底要做什么. 2. 软件测试的基本概念 软件测试的基本概念有3个,分别是需求,测试用例和BUG. 2.1 需求 这里的需求还可以分为 用户需求和软件需求,用户…...

接口测试 —— 接口测试定义

1、接口测试概念 (重点) 接口测试是测试系统组件间接口的一种测试,它界于单元测试与系统测试中间。 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。 测试的重点是要检查数据的交换,传递和控制管理过…...

2015 年一月联考逻辑真题

2015 年一月联考逻辑真题 真题(2015-26) 26.晴朗的夜晚我们可以看到满天星斗,其中有些是自身发光的恒星,有些是自身不发光但可以反射附近恒星光的行星。恒星尽管遥远,但是有些可以被现有的光学望远镜“看到”。和恒星不…...

基于GD32的定时器不完全详解--定时、级联

SysTick 定时器 SysTick 是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器中的使能位清除, 就永不停息。 该定时器的介绍在MCU的手册中一般不会介绍,因为…...

Clion开发STM32之ESP8266系列(四)

前言 上一篇: Clion开发STM32之ESP8266系列(三) 本篇主要内容 实现esp8266需要实现的函数串口3中断函数的自定义(这里没有使用HAL提供的)封装esp8266服务端的代码和测试 正文 主要修改部分 核心配置头文件(添加一些宏定义) sys_core_conf.h文件中…...

降本增效,StarRocks 在同程旅行的实践

作者:周涛 同程旅行数据中心大数据研发工程师 同程旅行是中国在线旅游行业的创新者和市场领导者。作为一家一站式平台,同程旅行致力于满足用户旅游需求,秉持 "让旅行更简单、更快乐" 的使命,主要通过包括微信小程序、AP…...

INTP型人格适合选择哪些专业?

INTP人格内倾理性人格、具有强烈的好奇心、创造性和独立性的特点。他们善于独立思考和寻找问题的本质,并对抽象的想法和理论感兴趣。 INTP人格的人具有很强的逻辑思维和分析能力,他们的思维方式非常系统,追求完美和准确。因此他们适合选择需…...

【LeetCode热题100】打卡第16天:组合总和

文章目录 组合总和⛅前言🔒题目🔑题解 组合总和 ⛅前言 大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏! 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数…...

tinkerCAD案例:1.戒子环

基本戒指 在本课中,您将学习使用圆柱形状制作戒指。来吧! 说明 将圆柱体拖动到工作平面上并使其成为孔。 圆柱体应缩放以适合其制造手指。 在本例中,我们将使用 17mm 作为直径,但请根据您的需要随意调整尺寸。 将“圆柱”形状拖…...

RPC接口测试技术-Tcp 协议的接口测试

【摘要】 首先明确 Tcp 的概念,针对 Tcp 协议进行接口测试,是指基于 Tcp 协议的上层协议比如 Http ,串口,网口, Socket 等。这些协议与 Http 测试方法类似(具体查看接口自动化测试章节)&#xf…...

MyBatis Plus基本用法-SpringBoot框架

依赖 使用 Mybatis Plus 框架时&#xff0c;需要添加以下依赖&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>latest-version</version> </dependency…...

指针--指针变量的定义和初始化

存放变量的地址需要一种特殊类型的变量&#xff0c;这种特殊的数据类型就是指针&#xff08;Pointer&#xff09;。 具有指针类型的变量&#xff0c;称为指针变量&#xff0c;它时专门用于存储变量的地址值和变量。 其定义形式如下&#xff1a; 类型关键字 * 指针变量名&#x…...

Web基本概念

一、前言 World Wide Web的简称&#xff0c;是一个由许多互相链接的超文本组成的系统&#xff0c;通过互联网访问 &#xff08;为用户提供信息&#xff09; 静态网页 仅适用于不能经常更改内容的网页&#xff1b; 动态网页 网络编程技术创建的页面&#xff1b;通过在传统的静态…...

Niagara—— Texture Sample 与 Particle Subuv 区别

目录 一&#xff0c;Texture Sample 二&#xff0c;Particle Subuv 一&#xff0c;Texture Sample 此节点是最基本的采样节点&#xff0c;依据UV坐标来采样Texture&#xff1b; MipValueMode&#xff0c;设置采样的Mipmap Level&#xff1b; None&#xff0c;根据当前Texture…...

如何在食品行业运用IPD?

食品是我国重要的民生产业之一&#xff0c;是保障和满足人民群众不断增长消费需求的重要支撑。食品指各种供人食用或者饮用的成品和原料以及按照传统既是食品又是药品的物品&#xff0c;包括加工食品&#xff0c;半成品和未加工食品&#xff0c;不包括烟草或只作药品用的物质。…...

如何用pandas进行条件分组计算?

Pandas提供了强大的分组聚合功能&#xff0c;可以轻松进行条件分组计算和统计。本文通过一个例子&#xff0c;展示如何使用Pandas的.groupby()和.agg()方法进行条件分组计算。 准备数据 假设有这样一个字典数据: dict { 姓名: [张三&#xff0c;李四&#xff0c;王五&#x…...

tomcat如何调优,涉及哪些参数?

Tomcat是一个流行的开源Java Servlet容器&#xff0c;用于部署和管理Java Web应用程序。调优Tomcat可以提高性能、并发处理能力和稳定性。以下是一些常见的Tomcat调优参数和技巧&#xff1a; 1.调整内存参数&#xff1a; -Xms&#xff1a;指定Tomcat启动时的初始堆内存大小。 -…...

java培训机构学校教学教务选课管理平台springboot+vue

近年来&#xff0c;随着培训机构机构规模的逐渐增大&#xff0c;人工书写的方式已经不能满足如此庞大的数据。为了更好的适应信息时代的高效性&#xff0c;一个利用计算机来实现培训机构教务管理工作的系统将必然诞生。基于这一点&#xff0c;设计了一个培训机构教务管理系统&a…...

半导体(TSS)放电管的两大选购注意事项及选型小策略

固体放电管&#xff0c;是以半导体工艺制作而成的&#xff0c;因此我们也称为半导体&#xff08;TSS&#xff09;放电管&#xff0c;它常在电路中并联使用&#xff0c;具备伏安特性。 TSS放电管在电路中类似开关&#xff0c;在正常工作时不动作&#xff0c;但一般被保护电路受到…...

05-使用Vue3 + Vue CLI 实现前端模块的搭建

1、环境准备 流程:安装node得到npm,使用npm安装vue cli(脚手架),使用vue cli创建项目。 Vue CLI版本和Node版本有关,用Node V12只能下载到Vue CLI V4.X,必须用Node V18才能下载到Vue CLI V5.X IDEA支持配置多个版本的Node,类似配置多个JDK。 node.js安装 1、官网下载…...

3.1 增加多进程执行playwright

增加了多进程的方式执行测试代码&#xff0c;对代码改动比较大 1、case case目录依然是自动生成 2、config dir_collection.py新增了配置 mkdir_collections [case,log,img, ] del_collections [results,report ] del_regex temp3、data/img/log/resource/video data/im…...

天津网站设计/google付费推广

电脑公司GhostXPSP3快速装机海量驱动2012 文件名: DNGS_GHOSTXPSP3_V201206_NTFS.iso 大小: 870.54 MB MD5: 38972d1eed6c91214e3f3607a8806c0f SHA1: f02b9980941dde06dcfb9cda635f89f7dc554b64 ghost xp sp3 纯净版 http://siaina.cn 系统格式&#xff1a;NTFS光盘类型&…...

企业网站开发 文献综述/河南网站定制

1. 问题描述&#xff1a; 给你一个下标从 0 开始、由 n 个整数组成的数组 arr 。arr 中两个元素的间隔定义为它们下标之间的绝对差 。更正式地&#xff0c;arr[i] 和 arr[j] 之间的间隔是 |i - j| 。返回一个长度为 n 的数组 intervals &#xff0c;其中 intervals[i] 是 arr[…...

网站前台后台模板/域名查询服务器

问题描述 进行pip更新的时候&#xff0c;执行命令报错后&#xff0c;再次尝试的时候&#xff0c;出现&#xff1a; 问题解决 可以进行两个命令进行操作&#xff1a; python -m ensurepippython -m pip install --upgrade pip若第二条command 出现问题&#xff0c;可查看相…...

网站制作要多长时间/百度知道网页版

Redis 三主三从集群搭建方式一&#xff1a;源码安装下载源码包&#xff1a;wget http://download.redis.io/releases/redis-3.2.10.tar.gz解压并进入目录&#xff1a;tar xf redis-3.2.10 && cd redis-3.2.10makeredis命令做软连接ln -s /root/redis-3.2.10/src/* /usr…...

极验验证 wordpress/中山排名推广

文章目录0x01 DLL简介0x02 DLL 调用0x03 与 lib 文件区别0x04 DLL 编写0x01 DLL简介 ​ 动态链接库&#xff08;Dynamic-Link-Library&#xff0c;缩写dll&#xff09;, 是微软公司在微软视窗操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是 .DLL、.OCX&am…...

抖音电商/seo查询是什么

# 开始绘图 fig, ax1 plt.subplots() fig.set_size_inches(12, 6) plt.set_cmap(RdBu) # multiple line plot x np.arange(featureValuedf.shape[1]) # x坐标的范围 lw 4 # 控制线条的粗细 a, ax1.plot(x, featureValuedf.loc[Back].to_numpy(),linewidthlw, labelBack, ma…...