openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

文章目录

    • openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法
    • 概述
    • 笔记
    • 查看特性列表
    • openssl3.2编译脚本 - 加入enable-crypto-mdebug
    • 看看有没有替代内存诊断的方法?
    • main.cpp
    • my_openSSL_lib.h
    • my_openSSL_lib.c
    • 备注
    • 备注
    • 这招不行啊
    • 显势调用默认上下文也不行
    • 找到一种还可以的解决方法, 现在看来可以准确观测到openssl内存泄漏点
    • 内存泄漏观测的程序实现
    • main.cpp
    • CMemHookRec.h
    • CMemHookRec.cpp
    • 备注
    • 备注
    • END

openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

概述

调用openssl接口后, 如果用到了openssl对象, 需要释放, 否则会发生内存泄漏.
即使不是新手, 也不能保证释放函数都调用了. 想想我们自己写程序, new后, 没有delete的情况就知道, 可以理解.
谁能保证自己手搓的应用实现100%没内存泄漏呢?

看资料时, 发现openssl本身有这个检查库本身发生内存泄漏的特性, 大概就是申请内存时, openssl自己记录了一下, free内存时, 将对应记录删掉.
这样, 在程序退出前, 再调用一下内存分配记录列表接口, 就知道哪里的内存没释放.
那试试, 加入crypto-mdebug特性, 模拟一下内存泄漏(调用openssl_new(), 不调用openssl_free()), 看看啥效果.

笔记

查看特性列表

perl configdata.pm --dump > my_log.txt

查看my_log.txt, 就有openssl 特性列表.
有启用的特性列表, 也有被禁掉的特性列表.
如果要加入特性, 就看禁止列表中的特性.
在这里插入图片描述
怎么打开crypto-mdebug特性呢?
看Configure可知, 只要带上参数 enable-crypto-mdebug即可.
在这里插入图片描述
结合我最后实验可用的编译脚本, 加入 enable-crypto-mdebug

openssl3.2编译脚本 - 加入enable-crypto-mdebug

解开官方源码包打开vs2019x64本地命令行, 选择管理员身份运行cd /d D:\3rd_prj\crypt\openssl-3.2.0set path=c:\nasm;%path%perl Configure VC-WIN64A --debug enable-crypto-mdebug zlib-dynamic --with-zlib-include=D:\my_dev\lib\zlib_1d3 --with-zlib-lib=.\my_zlib_1d3.dll --prefix=c:\openssl_3d2 --openssldir=c:\openssl_3d2\commonnmake手工拷贝, 将 my_zlib_1d3.dll 拷贝到以下4个目录
.\
.\apps
.\fuzz
.\testnmake testnmake install手工拷贝
D:\my_dev\lib\zlib_1d3\my_zlib_1d3.dll => C:\openssl_3d2\bin\my_zlib_1d3.dll归档
C:\openssl_3d2 剪切到自己的库目录 => D:\my_dev\lib\openssl_3d2

写个测试程序, 调用一下内存泄漏检查的相关接口, 看看能否编译过, 然后试试接口怎么用.

/*!
* \file main.cpp
*/#include "my_openSSL_lib.h"#include <openssl/crypto.h> // for mem leak APIint main(int argc, char** argv)
{CRYPTO_mem_leaks(NULL);return 0;
}/*!
编译错误
已启动重新生成…
1>------ 已启动全部重新生成: 项目: prj_template, 配置: Debug x64 ------
1>main.cpp
1>D:\my_dev\my_local_git_prj\study\openSSL\exp\call_mem_leak_API\main.cpp(12,2): error C4996: 'CRYPTO_mem_leaks': Since OpenSSL 3.0
1>已完成生成项目“prj_template.vcxproj”的操作 - 失败。
========== 全部重新生成: 成功 0 个,失败 1 个,跳过 0 个 ==========
*/

直接编译不过…
看官方说明 file:///D:/3rd_prj/crypt/openssl-3.2.0/doc/html/man3/OPENSSL_malloc.html

The following functions have been deprecated since OpenSSL 3.0, and can be hidden entirely by defining OPENSSL_API_COMPAT with a suitable version value, see openssl_user_macros(7):int CRYPTO_mem_leaks(BIO *b);int CRYPTO_mem_leaks_fp(FILE *fp);int CRYPTO_mem_leaks_cb(int (*cb)(const char *str, size_t len, void *u),void *u);int CRYPTO_set_mem_debug(int onoff);int CRYPTO_mem_ctrl(int mode);int OPENSSL_mem_debug_push(const char *info);int OPENSSL_mem_debug_pop(void);int CRYPTO_mem_debug_push(const char *info, const char *file, int line);int CRYPTO_mem_debug_pop(void);
DESCRIPTION

看到官方说, 这些内存诊断函数已经弃用了, 用clang的检查代替(忘了是哪个文档了, 反正是官方文档中说的).
去看内存诊断函数的实现, 都是空的, 官方确实已经弃用了.
在这里插入图片描述

尝试将vs2019的工具链改为clang, 看不到效果, 且不能单步进入库函数内部.
在这里插入图片描述
用clang工具链编译, 可以编译过, 也可以运行, 不过无法进入调试版的pdb实现中.

已启动重新生成…
1>------ 已启动全部重新生成: 项目: prj_template, 配置: Debug x64 ------
1>main.cpp(12,2): warning : 'CRYPTO_mem_leaks' is deprecated: Since OpenSSL 3.0 [-Wdeprecated-declarations]
1>D:\my_dev\lib\openssl_3d2\include\openssl/crypto.h(411,1): message : 'CRYPTO_mem_leaks' has been explicitly marked deprecated here
1>D:\my_dev\lib\openssl_3d2\include\openssl/macros.h(194,49): message : expanded from macro 'OSSL_DEPRECATEDIN_3_0'
1>D:\my_dev\lib\openssl_3d2\include\openssl/macros.h(44,22): message : expanded from macro 'OSSL_DEPRECATED'
1>prj_template.vcxproj -> D:\my_dev\my_local_git_prj\study\openSSL\exp\call_mem_leak_API\x64\Debug\prj_template.exe
1>'pwsh.exe' 不是内部或外部命令,也不是可运行的程序
1>或批处理文件。
1>已完成生成项目“prj_template.vcxproj”的操作。
========== 全部重新生成: 成功 1 个,失败 0 个,跳过 0==========

那就不用clang来编译.

看看有没有替代内存诊断的方法?

找到一个opensslAPI CRYPTO_get_alloc_counts(), 可以取当前内存分配次数.
将这个API封装一下, 卡在openssl应用函数外边, 就可以间接的知道是否有内存泄漏, 如果有opensslAPI调用引起的内存泄漏, 可以迅速的缩小排查范围.

main.cpp

/*!
* \file main.cpp
*/#include "my_openSSL_lib.h"#include <openssl/crypto.h>bool is_OSSL_mem_leak();
void test_mem_leak(bool b_have_mem_leak);int main(int argc, char** argv)
{openssl_mdebug_begin();test_mem_leak(true);openssl_mdebug_end(true, false); // 如果需要断言, 参数2为trueopenssl_mdebug_begin();test_mem_leak(false);openssl_mdebug_end(true, true);/*run resulttest_mem_leak(test have mem leak)>> malloc_count = 0, realloc_count = 0, free_count = 0<< malloc_count = 1, realloc_count = 0, free_count = 0err : !!! mem leak happentest_mem_leak(test no mem leak)*/return 0;
}void test_mem_leak(bool b_have_mem_leak)
{void* pBuf = NULL;printf("test_mem_leak(%s)\n", (b_have_mem_leak ? "test have mem leak" : "test no mem leak"));pBuf = OPENSSL_malloc(2);// do some task ...if (!b_have_mem_leak){OPENSSL_free(pBuf);pBuf = NULL;}
}

my_openSSL_lib.h

/*!
\file my_openSSL_lib.h
*/#ifndef __MY_OPENSSL_LIB_H__
#define __MY_OPENSSL_LIB_H__#ifdef __cplusplus
extern "C" {
#endif#ifdef  _WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") // for select()#include <windows.h>#include <stdbool.h>#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")#endif /* #ifdef  _WIN32 */// --------------------------------------------------------------------------------
// 开关宏 - begin
// --------------------------------------------------------------------------------#define MY_USE_APPLINK// --------------------------------------------------------------------------------
// 开关宏 - END
// --------------------------------------------------------------------------------typedef struct _tag_openssl_mem_counter{int malloc_count_begin;int realloc_count_begin;int free_count_begin;int malloc_count_end;int realloc_count_end;int free_count_end;
} TAG_OPENSSL_MEM_COUNTER;void openssl_mdebug_begin();
bool openssl_mdebug_end(bool show_tip, bool b_assert);#ifdef __cplusplus
}
#endif#endif /* #ifndef __MY_OPENSSL_LIB_H__ */

my_openSSL_lib.c

/*!
* \file D:\my_dev\my_local_git_prj\study\openSSL\nmake_test\test_c\prj_005_afalgtest.c\my_openSSL_lib.c
*/#include "my_openSSL_lib.h"
#include "openssl/crypto.h" // for CRYPTO_get_alloc_counts#include <assert.h>#ifdef MY_USE_APPLINK
#include <openssl/applink.c> /*! for OPENSSL_Uplink(00007FF8B7EF0FE8,08): no OPENSSL_Applink */
#endif // #ifdef MY_USE_APPLINKstatic TAG_OPENSSL_MEM_COUNTER gs_openssl_mdebug;void openssl_mdebug_begin()
{CRYPTO_get_alloc_counts(&gs_openssl_mdebug.malloc_count_begin, &gs_openssl_mdebug.realloc_count_begin, &gs_openssl_mdebug.free_count_begin);
}bool openssl_mdebug_end(bool show_tip, bool b_assert)
{bool b_rc = false;long lCntBegin = 0;long lCntEnd = 0;CRYPTO_get_alloc_counts(&gs_openssl_mdebug.malloc_count_end, &gs_openssl_mdebug.realloc_count_end, &gs_openssl_mdebug.free_count_end);lCntBegin = gs_openssl_mdebug.malloc_count_begin + gs_openssl_mdebug.realloc_count_begin - gs_openssl_mdebug.free_count_begin;lCntEnd = gs_openssl_mdebug.malloc_count_end + gs_openssl_mdebug.realloc_count_end - gs_openssl_mdebug.free_count_end;b_rc = (lCntBegin == lCntEnd);if (!b_rc && show_tip){printf(">> malloc_count = %d, realloc_count = %d, free_count = %d\n", gs_openssl_mdebug.malloc_count_begin, gs_openssl_mdebug.realloc_count_begin, gs_openssl_mdebug.free_count_begin);printf("<< malloc_count = %d, realloc_count = %d, free_count = %d\n", gs_openssl_mdebug.malloc_count_end, gs_openssl_mdebug.realloc_count_end, gs_openssl_mdebug.free_count_end);printf("err : !!! mem leak happen\n");}if (b_assert){assert(true == b_rc);}return b_rc;
}

备注

以后有机缘再查查怎么用clang来查openssl应用是否有内存泄漏.
查资料时, 很多情况下都不是想查就能查到的.
很多时候, 是心里带着问题, 查其他资料时, 突然给了启发, 才将以前的问题搞掉.

这种调用CRYPTO_get_alloc_counts()来间接的排查是否调用opensslAPI时, 是否没有成对的分配,释放内存.
没有那么直接和方便, 不过也算是一种方法了. 有总比没有强.

希望以后能找到其他更好的方法来定位opensslAPI调用时的内存泄漏点.

备注

openssl的编译检查, 测试用例还是很严格的.
对外提供的API, 都有测试程序.
CRYPTO_get_alloc_counts()这个API的调用, 也能找到至少一处.
在这里插入图片描述

用SI将openssl源码编译的目录中的能识别的东东都包进来搜索, 必然能看到测试用例或者API调用的痕迹.
如果某个API虽然定义, 但是官方源码编译目录的实现中都没有用到, 那么咱么就不能用这个API.
在这里插入图片描述

这招不行啊

今天正好写个测试程序, 用到了这种内存泄漏检测方法. 有断言, 不好使.

int main(int argc, char** argv)
{BIO* bio = NULL;openssl_mdebug_begin();test_bio_mem_leak();// test_bio_new_mem_buf();openssl_mdebug_end(false, true);return 0;
}void test_bio_mem_leak(void)
{BIO* bio = BIO_new_mem_buf("Hello World\n", 12);BIO_free(bio);
}

在这里插入图片描述
只能先关注这事, 以后再想办法了.
跟了一下openssl代码, 是产生默认库上下文中有很多openssl_malloc()引起的内存分配.
后续再看看, 自己显势调用产生销毁默认上下文的API, 是否可以继续用这种方法.

显势调用默认上下文也不行

int main(int argc, char** argv)
{OSSL_LIB_CTX* _ossl_lib_ctx = NULL;BIO* bio = NULL;do {openssl_mdebug_begin();_ossl_lib_ctx = OSSL_LIB_CTX_get0_global_default();if (NULL == _ossl_lib_ctx){assert(false);break;}test_bio_mem_leak();// test_bio_new_mem_buf();OSSL_LIB_CTX_free(_ossl_lib_ctx);openssl_mdebug_end(true, false); // 到这里, 还是报错} while (false);return 0;
}void test_bio_mem_leak(void)
{BIO* bio = BIO_new_mem_buf("Hello World\n", 12);BIO_free(bio);
}

报错原因, openssl函数调用中, 会有其他默认的上下文建立会调用内存分配.
具体是啥函数, 要去单步.
这种用内存分配计数的方法, 不能真实的间接观察到openssl内部的内存泄漏, 做了一个没用的实验…

还是要老老实实的看官方例子, 手工调用对应API的释放函数.

找到一种还可以的解决方法, 现在看来可以准确观测到openssl内存泄漏点

openssl设计的可以, 预留了很多有用的API.
翻翻openssl提供的API, 找到了以下有用的API.

CRYPTO_get_mem_functions()
CRYPTO_set_mem_functions()
OPENSSL_init_crypto()
OPENSSL_cleanup()

这些可以用于辅助观测openssl内存泄漏点, 不止是openssl API, openssl内部发生的内存泄漏也能观测到(如果有的话)

内存泄漏观测的程序实现

main.cpp

/*!
* \file main.cpp
*/#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>#include <stdlib.h>
#include <stdio.h>
#include <assert.h>#include "CMemHookRec.h"void test_bio_mem_leak(bool b_have_mem_leak);int main(int argc, char** argv)
{setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞// mem IF hook, 必须在执行任何openssl API之前替换mem_hook();// 自己的openssl接口应用test_bio_mem_leak(true);test_bio_mem_leak(false);// mem IF un hook, 必须在openssl结束后, 再恢复原始函数mem_unhook();/* run result, 如果有openssl应用接口没释放引起的内存泄漏, 有断言, stdout有输出一个openssl API引起的内存泄漏可能对应这多条内存分配记录, 不过排查范围缩小多了, 线索也清晰多了 结合对自己的openssl调用代码块的注释排除法, 很容易找到自己哪里没调用正确的openssl释放API.free map, g_mem_hook_map.size() = 42AAA2F36560 128 crypto\bio\bio_lib.c:832AAA2F4EC40 16 crypto\bio\bss_mem.c:1112AAA2FEA470 32 crypto\bio\bss_mem.c:1192AAA2FEAA10 32 crypto\buffer\buffer.c:35error : !!! find openssl call memory leakAssertion failed: false, file D:\my_dev\my_local_git_prj\soft\xx\src\case\exp002_mem_hook\main.cpp, line 57*/return 0;
}void test_bio_mem_leak(bool b_have_mem_leak)
{BIO* bio = BIO_new_mem_buf("Hello World\n", 12);if (false == b_have_mem_leak){BIO_free(bio);}
}

找内存泄漏时很方便, 只要在程序入口处调用mem_hook(), 然后自己正常调用openssl API, 程序结束前调用mem_unhook()
如果有openssl 释放API没调用引起的内存泄漏, 就会有断言.
如果是控制台程序, 就可以直接看到内存泄漏点.
如果不是控制台程序, 将mem_unhook()中的内存泄漏改写到其他输出(e.g. 文件).
这排查起来就容易多了.

CMemHookRec.h

/*!
\file CMemHookRec.h
\note openssl 分配API hook 之后, 记录的内存分配记录
*/#ifndef __CMEMHOOKREC_H__
#define __CMEMHOOKREC_H__#include <cstdint>
#include <stdio.h>
#include <map>
#include <openssl/crypto.h>#define SAFE_DELETE(p) \
do { \if (NULL != (p)) { \delete (p);\} \(p)=NULL; \
} while (0);class CMemHookRec
{
public:void show_info();public:uint64_t u64_addr;size_t num;const char* file;int line;uint64_t rec_sn;
};void mem_hook();
void mem_unhook();void* my_CRYPTO_malloc(size_t num, const char* file, int line);
void* my_CRYPTO_realloc(void* addr, size_t num, const char* file, int line);
void my_CRYPTO_free(void* ptr, const char* file, int line);
bool free_map();#endif // #ifndef __CMEMHOOKREC_H__

CMemHookRec.cpp

/// \file CMemHookRec.cpp#include "CMemHookRec.h"
#include <assert.h>void CMemHookRec::show_info()
{printf("entry sn = [%I64d],  %I64X %zu %s:%d\n", this->rec_sn, u64_addr, num, ((NULL != file) ? file : "NULL"), line);
}std::map<uint64_t, CMemHookRec*> g_mem_hook_map;
typedef std::pair<uint64_t, CMemHookRec*> Map_Pair_mem_hook;
typedef std::map<uint64_t, CMemHookRec*>::iterator It_mem_hook;// 		malloc_fn_org	0x00007ffb0796614f {libcrypto-3-x64.dll!CRYPTO_malloc}	void *(*)(unsigned __int64, const char *, int)
CRYPTO_malloc_fn malloc_fn_org = NULL;// 		realloc_fn_org	0x00007ffb07967a1d {libcrypto-3-x64.dll!CRYPTO_realloc}	void *(*)(void *, unsigned __int64, const char *, int)
CRYPTO_realloc_fn realloc_fn_org = NULL;// 		free_fn_org	0x00007ffb079631b1 {libcrypto-3-x64.dll!CRYPTO_free}	void(*)(void *, const char *, int)
CRYPTO_free_fn free_fn_org = NULL;void mem_hook()
{int i_rc = 0;uint64_t u64_init_opt = 0;CRYPTO_get_mem_functions(&malloc_fn_org, &realloc_fn_org, &free_fn_org);CRYPTO_set_mem_functions(my_CRYPTO_malloc, my_CRYPTO_realloc, my_CRYPTO_free);// openssl init allOPENSSL_INIT_SETTINGS* _ossl_init_setting = OPENSSL_INIT_new();u64_init_opt = OPENSSL_INIT_LOAD_CRYPTO_STRINGS |OPENSSL_INIT_ADD_ALL_CIPHERS |OPENSSL_INIT_ADD_ALL_DIGESTS |OPENSSL_INIT_LOAD_CONFIG |OPENSSL_INIT_ASYNC |OPENSSL_INIT_NO_ATEXIT;// 将openssl库中的静态初始化函数显势调用一下, 免得在我们手工调用API时(因为有些openssl对象或变量没初始化)自动调用静态初始化函数引起多余的非我们应用产生的内存分配// 初始化函数在应用中只能调用一次.i_rc = OPENSSL_init_crypto(u64_init_opt, _ossl_init_setting);assert(1 == i_rc);
}void mem_unhook()
{// openssl uninit all// 只能调用一次OPENSSL_cleanup();// 必须在openssl cleanup之后调用map显示, 这样才能过滤掉系统自动分配的内存.// 如果map不为空, 这些记录才是我们自己应用没释放的内存// 在free_map()时, 如果看到多条未被释放的内存, 也一定是那么多个API调用没释放, 因为一条openssl API会对应者多个openssl_mallc(), e.g. BIO_new_mem_buf() 就有4个内存分配动作.if (!free_map()){printf("error : !!! find openssl call memory leak\r\n");assert(false);/* run resultfree map, g_mem_hook_map.size() = 42AAA2F36560 128 crypto\bio\bio_lib.c:832AAA2F4EC40 16 crypto\bio\bss_mem.c:1112AAA2FEA470 32 crypto\bio\bss_mem.c:1192AAA2FEAA10 32 crypto\buffer\buffer.c:35error : !!! find openssl call memory leakAssertion failed: false, file D:\my_dev\my_local_git_prj\soft\krgy_software_protect\src\case\exp002_mem_hook\main.cpp, line 57*/}CRYPTO_set_mem_functions(malloc_fn_org, realloc_fn_org, free_fn_org);
}bool free_map()
{It_mem_hook it = g_mem_hook_map.end();size_t size = g_mem_hook_map.size();printf("free map, g_mem_hook_map.size() = %zd\n", size);for (it = g_mem_hook_map.begin(); it != g_mem_hook_map.end(); it++) {if (NULL != it->second) {it->second->show_info();}SAFE_DELETE(it->second);}g_mem_hook_map.clear();return (0 == size);
}static uint64_t g_u64_malloc_cnt_all = 0;
void* my_CRYPTO_malloc(size_t num, const char* file, int line)
{void* p = NULL;It_mem_hook it;CMemHookRec* rec = NULL;p = malloc(num);if (NULL != p){it = g_mem_hook_map.find((uint64_t)p);if (it != g_mem_hook_map.end()) {// printf("find key\n");assert(false);}else {// printf("not find key\n");rec = new CMemHookRec();if (NULL != rec){rec->rec_sn = ++g_u64_malloc_cnt_all;rec->u64_addr = (uint64_t)p;rec->num = num;rec->file = file;rec->line = line;g_mem_hook_map.insert(Map_Pair_mem_hook(rec->u64_addr, rec));}}}return p;
}void* my_CRYPTO_realloc(void* addr, size_t num, const char* file, int line)
{void* p = NULL;It_mem_hook it;CMemHookRec* rec = NULL;p = realloc(addr, num);if (NULL != p){it = g_mem_hook_map.find((uint64_t)addr);if (it != g_mem_hook_map.end()) {// printf("find key\n");SAFE_DELETE(it->second);g_mem_hook_map.erase(it);}rec = new CMemHookRec();if (NULL != rec){rec->rec_sn = ++g_u64_malloc_cnt_all;rec->u64_addr = (uint64_t)p;rec->num = num;rec->file = file;rec->line = line;g_mem_hook_map.insert(Map_Pair_mem_hook(rec->u64_addr, rec));}}return p;
}void my_CRYPTO_free(void* ptr, const char* file, int line)
{It_mem_hook it;CMemHookRec* rec = NULL;if (NULL != ptr){free(ptr);it = g_mem_hook_map.find((uint64_t)ptr);if (it != g_mem_hook_map.end()) {// printf("find key\n");SAFE_DELETE(it->second);g_mem_hook_map.erase(it);}else {// printf("not find key\n");assert(false);}}
}

备注

此笔记前面实验的CRYPTO_get_alloc_counts()方法, 在mem_hook中用不了了, 因为将内存分配函数换掉了.
不过, 可以在上面这个实验上改改, 不hook内存分配函数, 只用 OPENSSL_init_crypto() + OPENSSL_cleanup() + CRYPTO_get_alloc_counts(), 应该也能看到内存泄漏的计数, 但是看不到哪个文件哪行发生的泄漏.

因为这个想法不好, 我就没去再实验.
毕竟有多个解决方法, 只取最优的那个.

mem_hook()这个方法好, 能看到发生泄漏的具体库文件和行数, 排查起来可操作性强.

备注

将 void CMemHookRec::show_info()的输出改了一下, 上面的实验代码已经更新.

void CMemHookRec::show_info()
{printf("entry sn = [%I64d],  %I64X %zu %s:%d\n", this->rec_sn, u64_addr, num, ((NULL != file) ? file : "NULL"), line);
}

这样的话, 等观测到内存泄漏时, 号码连续的就是一个openssl API的调用引起的内存泄漏, 更方便调试排查.
在这里插入图片描述

END

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

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

相关文章

【AIGC大模型】跑通wonder3D (windows)

这两天看了AI大神李某舟被封杀&#xff0c;课程被下架的新闻&#xff0c;TU商 认为&#xff1a;现在这种玩概念、徒具高大上外表却无实质内容的东西太多了&#xff0c;已经形成一种趋势和风潮&#xff0c;各行各业各圈层都在做大做强这种势&#xff0c;对了&#xff0c;这种行为…

apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$@“

[TOC](apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$”) 1、问题描述 apache 启动报错 apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$” 2、问题分析 参考链接: https://stackoverflow.com/questions/43726930/apache…

外包干了四年,技术明显退步。。。

在湖南的一个安静角落&#xff0c;我&#xff0c;一个普通的本科生&#xff0c;开始了我的软件测试之旅。四年的外包生涯&#xff0c;让我在舒适区里逐渐失去了锐气&#xff0c;技术停滞不前&#xff0c;仿佛被时间遗忘。然而&#xff0c;生活的转机总是在不经意间降临。 与女…

AxureCloud配置文件详细介绍

AxureCloud配置文件详细介绍 原文地址&#xff1a;https://docs.axure.com/axure-cloud/business/custom-settings-json/ 通过修改 customsettings.json 可以修改AxureCloud私有部署的域名、端口、HTTPS、存储目录、是否开启插件等, 默认安装的路径为: C:\Program Files\Axure…

OPENSSL-PKCS7入门知识介绍

1 PKCS7数据结构说明 p7包括6种数据内容&#xff1a;数据(data),签名数据&#xff08;sign&#xff09;&#xff0c;数字信封数据&#xff08;enveloped&#xff09;&#xff0c;签名数字信封数据&#xff08;signed_and_enveloped&#xff09;&#xff0c;摘要数据&#xff08…

【kubernetes】关于k8s集群中kubectl的陈述式资源管理

目录 一、k8s集群资源管理方式分类&#xff1a; &#xff08;1&#xff09;陈述式资源管理方式&#xff1a;增删查比较方便&#xff0c;但是改非常不方便 &#xff08;2&#xff09;声明式资源管理方式&#xff1a;yaml文件管理 二、陈述式资源管理方法&#xff1a; 三、ku…

重学Java 18.学生管理系统项目

臣无祖母&#xff0c;无以至今日&#xff0c;祖母无臣&#xff0c;无以终余年 母孙二人&#xff0c;更相为命&#xff0c;是以区区不能废远 —— 陈情表.李密 —— 24.2.20 一、编写JavaBean public class Student {//学号private int id;//姓名private String name;//年龄pr…

【C++精简版回顾】12.友元函数

1.友元函数 1.class class MM { public:MM(int age,string name):age(age),name(name){}friend void print(MM mm); private:int age;string name;void print() {cout << age << "岁的" << name << "喜欢你" << endl;} }; f…

用html编写的小广告板

用html编写的小广告板 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</tit…

【洛谷 P8780】[蓝桥杯 2022 省 B] 刷题统计 题解(贪心算法+模拟+四则运算)

[蓝桥杯 2022 省 B] 刷题统计 题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a a a 道题目&#xff0c;周六和周日每天做 b b b 道题目。请你帮小明计算&#xff0c;按照计划他将在第几天实现做题数大于等于 n n n 题? 输入格式 输入一…

可视化图文报表

Apache Echarts介绍 Apache Echarts是一款基于Javascript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网&#xff1a;Apache ECharts 入门案例&#xff1a; <!DOCTYPE html> <html>…

Springboot+vue图书管理系统(小白)

图书管理系统 简介&#xff1a;一个最简约的图书管理系统&#xff0c;适用于小白用来练手 前端&#xff1a;VueElementUIechars 后端&#xff1a;SpringbootMybatisMySQL 功能模块&#xff1a; 信息管理&#xff1a;公告信息 操作日志 用户管理&#xff1a;用户信息 图书…

快速搭建宠物医院服务小程序的步骤,无需编程经验

如果你是一家宠物医院或者宠物服务机构&#xff0c;想要拥有一款方便用户预约、查询信息的小程序&#xff0c;那么乔拓云网提供的轻应用小程序是你的不二选择。下面将为你详细介绍如何轻松打造宠物医院服务小程序。 1. 进入乔拓云网后台&#xff0c;点击【轻应用小程序】中的【…

Three.js-05坐标轴AxesHelper

1.构建对象 说明&#xff1a;参数一表示坐标轴的长度。红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴. const axesHelper new THREE.AxesHelper( 1 ); 2.设置位置 axesHelper.position.y1 axesHelper.position.x1 axesHelper.position.z1 3. 网格 说明&#xff1a;立方体…

Python爬虫-爬取B站番剧封面

本文是本人最近学习Python爬虫所做的小练习。如有侵权&#xff0c;请联系删除。 页面获取url 代码 import requests import os import re# 创建文件夹 path os.getcwd() /images if not os.path.exists(path):os.mkdir(path)# 当前页数 page 1 # 总页数 total_page 2# 自动…

MySQL:开始深入其数据(一)DML

在上一章初识MySQL了解了如何定义数据库和数据表&#xff08;DDL&#xff09;&#xff0c;接下来我们开始开始深入其数据,对其数据进行访问&#xff08;DAL&#xff09;、查询DQL&#xff08;&#xff09;和操作(DML)等。 通过DML语句操作管理数据库数据 DML (数据操作语言) …

2/22作业

1.按位置插入 void insert_pos(seq_p L,datetype value,int pos) { if(LNULL) { printf("入参为空\n"); return; } if(seq_full(L)) { printf("表已满\n"); return; } if(pos>L->len|…

学生成绩管理系统

用单链表数据结构完成c的学生成绩管理系统&#xff0c;此系统的具体功能如下&#xff1a; 本人小萌新一个&#xff0c;遇到BUG是正常现象。并且类与对象写的不太理想。- 写了一个Database存放所有数据&#xff0c;但这肯定浪费资源&#xff0c;你们看着改改吧。 class DataBase…

SpringCloud Alibaba 2022之Nacos学习

SpringCloud Alibaba 2022使用 SpringCloud Alibaba 2022需要Spring Boot 3.0以上的版本&#xff0c;同时JDK需要是17及以上的版本。具体的可以看官网的说明。 Spring Cloud Alibaba版本说明 环境搭建 这里搭建的是一个聚合项目。项目结构如下&#xff1a; 父项目的pom.xm…

矩阵的范数 matrix norm Frobenius norm 弗罗贝尼乌斯 范数

1&#xff0c;矩阵范数的定义 矩阵的范数&#xff0c;matrix norm即矩阵的模&#xff1b;它把一个矩阵空间变成为赋范线性空间&#xff1b; 从一个矩阵空间映射到非负实数的函数 满足以下条件&#xff1a; 1&#xff0c;严格的正定性。对于 , 则 ; and if , must ; 2&…