05-调用API

上一篇: 04-JNI函数


        调用 API 允许软件供应商将 Java VM 加载到任意本地应用程序中。供应商可以提供支持 Java 的应用程序,而无需链接 Java VM 源代码。

5.1 概述

        下面的代码示例说明了如何使用调用 API 中的函数。在这个示例中,C++ 代码创建了一个 Java VM 并调用了一个名为 Main.test 的静态方法。为清晰起见,我们省略了错误检查。

#include <jni.h>       /* where everything is defined */
...
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *env;       /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 19 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_19;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;/* load and initialize a Java VM, return a JNI interface* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);/* We are done. */
jvm->DestroyJavaVM();

        本示例使用了 API 中的两个函数。调用 API 允许本地应用程序使用 JNI 接口指针访问虚拟机功能。

5.1.1 创建虚拟机

        JNI_CreateJavaVM() 函数加载并初始化 Java VM,并返回一个指向 JNI 接口指针的指针。调用 JNI_CreateJavaVM() 的线程被视为主线程,并连接到 Java VM

        注意:根据操作系统的不同,原始进程线程可能会受到特殊处理,从而影响其作为正常 Java 线程正常运行的能力(如堆栈大小受限和可以抛出 StackOverflowError )。强烈建议不要使用原始线程加载 Java VM,而应专门为此创建一个新线程

5.1.2 附加到虚拟机

        JNI 接口指针( JNIEnv )只在当前线程中有效。如果另一个线程需要访问 Java 虚拟机,它必须首先调用 AttachCurrentThread() 将自己附加到虚拟机并获取 JNI 接口指针。附加到虚拟机后,本地线程的工作方式与运行在本地方法中的普通 Java 线程无异,唯一的例外是在调用对调用者敏感的方法时没有 Java 调用者。本地线程会一直附着在虚拟机上,直到调用 DetachCurrentThread() 将自己分离。

        附加线程应有足够的堆栈空间来执行合理的工作。每个线程的堆栈空间分配取决于操作系统。例如,使用 pthreads 时,可以在 pthread_create 的 pthread_attr_t 参数中指定堆栈大小。

5.1.3 脱离虚拟机

        连接到虚拟机的本地线程必须在终止前调用 DetachCurrentThread() 来自行分离。如果调用栈上有 Java 方法,线程则无法自行退出。

5.1.4 终止虚拟机

        DestroyJavaVM() 功能可终止 Java 虚拟机。

        该函数会等到没有非守护进程线程执行时才实际终止虚拟机。非守护进程线程包括: Java 线程和附加的本地线程。等待的原因是非守护进程线程可能会占用系统资源,如锁或窗口。Java 应用程序或附加本地代码的程序员有责任在线程终止或分离前释放这些资源。虚拟机无法自动释放这些资源,因此会等待程序员在终止前释放这些资源

5.2 库和版本管理

        本地程序库可以与虚拟机动态链接或静态链接。库与虚拟机映像的结合方式取决于实现。必须使用 System.loadLibrary 或等效的 API 才能将库视为已加载,这既适用于动态链接的库,也适用于静态链接的库

        本地库一旦加载,所有类加载器都能看到它。因此,不同类加载器中的两个类可能会链接到同一个本地方法。这会导致两个问题:

                ①. 一个类可能会错误地与不同类加载器中同名类加载的本地库链接。

                ②. 本地方法很容易混合来自不同类加载器的类。这会破坏类加载器提供的名称空间分隔,并导致类型安全问题

        每个类加载器都管理自己的本地库。同一个 JNI 本地库不能加载到多个类加载器中。这样做会导致 UnsatisfiedLinkError 抛出。例如,当使用 System.loadLibrary 将一个本地库加载到两个类加载器时,就会抛出 UnsatisfiedLinkError 。这种方法的优点是:

                ①. 本地程序库中保留了基于类加载器的名称空间分隔。本地库不能轻易混合来自不同类加载器的类。

                ②. 此外,当相应的类加载器被垃圾回收时,本地库也可以被卸载

5.2.1 支持静态链接库

        以下规则适用于静态链接库,这些示例中给出的静态链接库名为 "L":

                ①. 当且仅当库导出一个名为 JNI_OnLoad_L的函数时,其映像已与虚拟机结合的库 "L "才被定义为静态链接库。

                ②. 如果静态链接库 L 输出了一个名为 JNI_OnLoad_L 的函数和一个名为 JNI_OnLoad 的函数,那么 JNI_OnLoad 函数将被忽略。

                ③. 如果函数库 L 是静态链接的,那么在首次调用 System.loadLibrary("L") 或等效 API 时,将调用一个 JNI_OnLoad_L 函数,其参数和预期返回值与为 JNI_OnLoad函数指定的参数和预期返回值相同。

                ④. 静态链接的库 L 将禁止动态加载同名库

                ⑤. 当包含静态链接本地程序库 L 的类加载器被垃圾回收时,如果程序库导出了 JNI_OnUnload_L函数,虚拟机将调用该函数。

                ⑥. 如果静态链接库 L 输出了一个名为 JNI_OnUnload_L 的函数和一个名为 JNI_OnUnload 的函数,那么 JNI_OnUnload 函数将被忽略。

        程序员还可以调用 JNI 函数 RegisterNatives() 来注册与类相关的本地方法。 RegisterNatives() 函数对静态链接函数特别有用

        如果动态链接库定义了 JNI_OnLoad_L 和/或JNI_OnUnload_L函数,这些函数将被忽略。

5.2.2 库钩子函数的生命周期

        为了便于版本控制和资源管理,JNI 库可以定义加载和卸载函数钩子。这些函数的命名取决于库是动态链接还是静态链接

5.2.3 JNI_OnLoad

/*** @brief 动态链接库定义的可选函数。*        虚拟机在加载本地库时调用 JNI_OnLoad (例如通过 System.loadLibrary )* * @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针* @return jint 返回所需的 JNI_VERSION 常量(另见 GetVersion )*/
jint JNI_OnLoad(JavaVM *vm, void *reserved);

        为了使用在 JNI API 的某个版本中定义的函数, JNI_OnLoad 必须返回一个至少定义了该版本的常量。例如,希望使用 JDK 1.4 引入的1 个函数的库至少需要返回 JNI_VERSION_1_4 个常量。如果本地库没有导出 JNI_OnLoad 个函数,虚拟机就会认为该库只需要 JNI 版本 JNI_VERSION_1_1 如果虚拟机无法识别 JNI_OnLoad 所返回的版本号,虚拟机将卸载该库,就像从未加载过该库一样

        从包含本地方法实现的动态链接本地库导出。

5.2.4 JNI_OnUnload

/*** @brief 动态链接库定义的可选函数。*        当包含本地库的类加载器被垃圾回收时,虚拟机会调用 JNI_OnUnload 。** @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针*/
void JNI_OnUnload(JavaVM *vm, void *reserved);

        该函数可用于执行清理操作。由于该函数是在未知上下文(如来自 finalizer)中调用的,因此程序员在使用 Java VM 服务时应保持谨慎,避免任意回调 Java 服务

        从包含本地方法实现的动态链接本地库导出。

5.2.5 JNI_OnLoad_L

/*** @brief 静态链接库必须定义的强制函数** @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针* @return jint 返回所需的 JNI_VERSION 常量(另见 GetVersion )。返回的最小版本至少为 JNI_VERSION_1_8* @since JDK/JRE 1.8*/
jint JNI_Onload_<L>(JavaVM *vm, void *reserved);

        如果一个名为 "L "的库是静态链接的,那么在首次调用 System.loadLibrary("L") 或等效 API 时,将调用一个 JNI_OnLoad_L 函数,其参数和预期返回值与为 JNI_OnLoad 函数指定的参数和预期返回值相同。 JNI_OnLoad_L 必须返回本地库所需的 JNI 版本。该版本必须是 JNI_VERSION_1_8 或更高版本。如果虚拟机无法识别 JNI_OnLoad_L 所返回的版本号,虚拟机将把该库当作从未加载过。

5.2.6 JNI_OnUnload_L

/*** @brief 静态链接库定义的可选函数。*        当包含静态链接本地程序库 "L "的类加载器被垃圾回收时,如果程序库导出了 JNI_OnUnload_L 函数,虚拟机将调用该函数。* @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针*/
void JNI_OnUnload_<L>(JavaVM *vm, void *reserved);

        该函数可用于执行清理操作。由于该函数是在未知上下文(如来自 finalizer)中调用的,因此程序员在使用 Java VM 服务时应保持谨慎,避免任意回调 Java 服务。

        从包含本地方法实现的静态链接本地库中导出。

信息说明:

        加载本地库的行为是使 Java 虚拟机和运行时了解并注册该库及其本地入口点的完整过程。请注意,仅仅执行操作系统级别的操作来加载本地库(如在 UNIX(R) 系统上执行 dlopen 操作)并不能完全实现这一目标本机函数通常由 Java 类加载器调用,以执行对主机操作系统的调用,将库加载到内存中,并返回本机库的句柄。该句柄将被存储并用于后续本地库入口点的搜索。一旦句柄成功返回,Java 本地类加载器将完成加载过程,并注册该库

5.3 调用 API 函数

        JavaVM 类型是指向调用 API 函数表的指针。下面的代码示例显示了该函数表:

typedef const struct JNIInvokeInterface *JavaVM;const struct JNIInvokeInterface ... = {NULL,NULL,NULL,DestroyJavaVM,AttachCurrentThread,DetachCurrentThread,GetEnv,AttachCurrentThreadAsDaemon
};

        请注意, JNI_GetDefaultJavaVMInitArgs() JNI_GetCreatedJavaVMs()JNI_CreateJavaVM() 这三个调用 API 函数不在 JavaVM 函数表中这些函数可以在没有预先存在的 JavaVM 结构的情况下使用

5.3.1 JNI_GetDefaultJavaVMInitArgs

/*** @brief 返回 Java 虚拟机的默认配置。*        调用此函数前,本地代码必须将 vm_args->version 字段设置为它期望虚拟机支持的 JNI 版本。*        此函数返回后,vm_args->version 将被设置为 VM 实际支持的 JNI 版本。** @param vm_args 指向 JavaVMInitArgs 结构的指针,缺省参数填入该结构中,不能是 NULL 结构* @return jint 如果请求的版本受支持,则返回 JNI_OK ;如果请求的版本不受支持,则返回 JNI 错误代码(负数)。*/
jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

        从实现 Java 虚拟机的本地库中导出。

5.3.2 JNI_GetCreatedJavaVMs

/*** @brief 返回已创建的所有 Java 虚拟机。*        虚拟机的指针将按照创建顺序写入缓冲区 vmBuf 。最多写入1 个条目。*        创建的虚拟机总数将在 \*nVMs 中返回。** @param vmBuf 指向放置虚拟机结构的缓冲区的指针,不得为 NULL* @param bufLen 缓冲区的长度* @param nVMs 整数指针。可为 NULL 值* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)。*/
jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

5.3.3 JNI_CreateJavaVM

/*** @brief 加载并初始化 Java VM。*        当前线程将连接到 Java VM 并成为主线程。将 p_env 参数设置为主线程的 JNI 接口指针。** @param p_vm 指针,指向放置生成的虚拟机结构的位置。不能为 NULL* @param p_env 指向主线程 JNI 接口指针位置的指针。不能为 NULL* @param vm_args Java VM 初始化参数。不能为 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)。*/
jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

        不支持在单个进程中创建多个虚拟机

        JNI_CreateJavaVM 的第二个参数始终是指向 JNIEnv * 的指针,而第三个参数是指向 JavaVMInitArgs 结构的指针,该结构使用选项字符串对任意虚拟机启动选项进行编码

typedef struct JavaVMInitArgs {jint version;jint nOptions;JavaVMOption *options;jboolean ignoreUnrecognized;
} JavaVMInitArgs;

        options 字段是以下类型的数组:

typedef struct JavaVMOption {char *optionString;  /* the option as a string in the default platform encoding */void *extraInfo;
} JavaVMOption;

        数组的大小由 JavaVMInitArgs 中的 nOptions 字段表示。如果 ignoreUnrecognized 为 JNI_TRUE , JNI_CreateJavaVM 会忽略所有以 " -X "或 " _ "开头的未识别选项字符串。如果 ignoreUnrecognized 为 JNI_FALSE , JNI_CreateJavaVM 在遇到任何未识别的选项字符串时立即返回 JNI_ERR 。所有 Java 虚拟机都必须识别以下一组标准选项:

        与模块相关的选项 --add-reads 、 --add-exports 、 --add-opens 、 --add-modules 、 --limit-modules 、 --module-path 、 --patch-module 和 --upgrade-module-path 必须以选项字符串形式传递,使用 "option=value "格式,而不是 "option value "格式(注意,"option "和 "value "之间必须有 " = "。)例如,要将 java.management/sun.management 输出为 ALL-UNNAMED ,必须传递选项字符串 "--add-exports=java.management/sun.management=ALL-UNNAMED" 。

        此外,每个虚拟机实现都可能支持自己的一套非标准选项字符串非标准选项名称必须以 " -X "或下划线(" _ ")开头。例如,JDK/JRE 支持 -Xms 和 -Xmx 选项,允许程序员指定初始堆大小和最大堆大小。以 " -X "开头的选项可以通过 " java "命令行访问。

        下面是在 JDK/JRE 中创建 Java VM 的示例代码:

JavaVMInitArgs vm_args;
JavaVMOption options[3];options[0].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
options[1].optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
options[2].optionString = "-verbose:jni";                   /* print JNI-related messages */vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 3;
vm_args.ignoreUnrecognized = TRUE;/* Note that in the JDK/JRE, there is no longer any need to call* JNI_GetDefaultJavaVMInitArgs.*/
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res < 0) ...

5.3.4 DestroyJavaVM

/*** @brief 终止 Java 虚拟机的运行,尽力释放虚拟机资源。*        JavaVM 接口函数表中的索引 3** @param vm 将被销毁的 Java 虚拟机。不能为 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint DestroyJavaVM(JavaVM *vm);

        任何线程,无论是否已连接,都可以调用此函数。如果当前线程尚未附加,则首先附加该线程。如果当前线程已被附加,那么如果它的调用栈中有任何 Java 方法,则会出错

        该函数等待所有非守护进程线程(如果当前线程为非守护进程线程,则不包括该线程)都已终止,然后启动关闭序列(请参阅 java.lang.Runtime)。关闭序列结束时,Java VM 将终止,导致任何仍在执行 Java 代码的线程停止执行该代码,并释放其能够释放的任何相关 VM 资源。此时,当前线程不再依附于 Java VM,此函数返回给调用者。

        请注意,Java VM 终止时仍在执行本地代码的任何线程都将继续执行该代码;但如果它们试图恢复执行 Java 代码,则将停止执行。这包括守护进程线程和关闭序列启动后启动的任何非守护进程线程。守护进程线程和非守护进程线程仅对附加的本地线程有意义;非附加的本地线程不受 Java VM 终止的影响。

5.3.5 AttachCurrentThread

/*** @brief 将当前线程作为非守护进程线程附加到 Java 虚拟机。在 p_env 参数中返回一个 JNI 接口指针*        JavaVM 接口函数表中的索引 4** @param vm 当前线程将连接的虚拟机,必须不是 NULL* @param p_env 指向当前线程的 JNI 接口指针所在位置的指针。不能为 NULL* @param thr_args 可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);

        尝试附加已附加的线程时,只会在 p_env 参数中返回其现有的 JNI 接口指针。调用此方法后,已附加线程的守护进程状态将保持不变

        一个本地线程不能同时连接到两个 Java 虚拟机

        当线程连接到虚拟机时,上下文类加载器就是引导加载器。

        thr_args:可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息:

typedef struct JavaVMAttachArgs {jint version;char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */jobject group; /* global ref of a ThreadGroup object, or NULL */
} JavaVMAttachArgs

5.3.6 AttachCurrentThreadAsDaemon

/*** @brief 将当前线程作为守护线程附加到 Java VM。在 p_env 参数中返回一个 JNI 接口指针*        JavaVM 接口函数表中的索引 7** @param vm 当前线程将连接的虚拟机实例。不能为 NULL* @param p_env 指向当前线程 JNIEnv 接口指针所在位置的指针。它不能是 NULL* @param thr_args 可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint AttachCurrentThreadAsDaemon(JavaVM *vm, void **p_env, void *thr_args);

        尝试附加已附加的线程时,只会在 p_env 参数中返回其现有的 JNI 接口指针。调用此方法后,已附加线程的守护进程状态将保持不变。

        一个本地线程不能同时连接到两个 Java 虚拟机。

        当线程连接到虚拟机时,上下文类加载器就是引导加载器。

        thr_args :可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息:

typedef struct JavaVMAttachArgs {jint version;char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */jobject group; /* global ref of a ThreadGroup object, or NULL */
} JavaVMAttachArgs

5.3.7 DetachCurrentThread

/*** @brief 将当前线程从 Java VM 中分离。如果调用栈中有 Java 方法,则线程无法脱离。*        JavaVM 接口函数表中的索引 5** @param vm 当前线程将脱离的虚拟机。必须不是 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint DetachCurrentThread(JavaVM *vm);

        该线程仍持有的任何 Java 监视器都会被释放(不过在正确编写的程序中,所有监视器都会在此之前被释放)。现在,该线程被视为已终止,不再存活;所有等待该线程死亡的 Java 线程都会收到通知

        主线程可以从虚拟机中分离出来。

        试图分离未连接的线程是不可能的

        如果调用 DetachCurrentThread 时有异常等待处理,虚拟机可以选择报告该异常的存在。

5.3.8 GetEnv

/*** @brief JavaVM 接口函数表中的索引 6** @param vm 将从中检索接口的虚拟机实例。不得为 NULL* @param p_env 指向当前线程的 JNI 接口指针所在位置的指针。不能为 NULL* @param version 请求的 JNI 版本* @return jint 如果当前线程未连接到虚拟机,则将 *env 设为 NULL ,并返回 JNI_EDETACHED 。*              如果不支持指定的版本,则将 *env 设置为 NULL ,并返回 JNI_EVERSION 。*              否则,将 *env 设置为相应的接口,并返回 JNI_OK*/
jint GetEnv(JavaVM *vm, void **p_env, jint version);

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

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

相关文章

Discuz论坛网站报错Discuz!Database Error(0)notconnect的解决办法

运营服务器大本营有段时间了&#xff0c;在运营期间遇到两次Discuz&#xff01;Database Error&#xff08;0&#xff09;notconnect报错&#xff0c;和你们分享遇到Discuz报错的解决办法&#xff0c;希望可以帮助到你。 首先网站报错&#xff08;0&#xff09;notconnect&…

【Web】浅聊Java反序列化之C3P0——JNDI注入利用

目录 简介 原理分析 EXP 前文&#xff1a;【Web】浅聊Java反序列化之C3P0——URLClassLoader利用 【Web】浅聊Java反序列化之C3P0——不出网Hex字节码加载利用 简介 出网的情况下&#xff0c;这个C3P0的Gadget可以和fastjson&#xff0c;Snake YAML , JYAML,Yamlbeans , …

如何防御udp攻击

UDP Flood是互联网上最经典的DDoS&#xff08;Distributed Denial of Service&#xff09;攻击之一。攻击者在短时间内向目标设备发送大量的UDP报文&#xff0c;导致链路拥塞甚至网络瘫痪。一般的UDP报文由攻击工具伪造&#xff0c;通常在数据段具备相同的特征&#xff0c;另一…

【嵌入式高级C语言】9:万能型链表懒人手册

文章目录 序言单向不循环链表拼图框架搭建 - Necessary功能拼图块1 创建链表头信息结构体 - Necessary2 链表头部插入 - Optional3 链表的遍历 - Optional4 链表的销毁 - Necessary5 链表头信息结构体销毁 - Necessary6 获取链表中节点的个数 - Optional7 链表尾部插入 - Optio…

网站开发之旅:从概念到实现

在我成为一名专业的网站开发者的过程中&#xff0c;我有幸参与了多个激动人心的项目。其中&#xff0c;一个我印象尤为深刻的经历是&#xff0c;开发一个名为“文案推荐网”的主题网站&#xff08;www.zimeiti.love&#xff09;。这个项目不仅让我深入了解了网站开发的各个方面…

excel常用操作备忘

目录 快捷键基础数据透视图统计某列的值出现的频数 数据有效性数据分列运算符顺序文本匹配中的通配符错误的类型&#xff08;常与IF嵌套使用&#xff09;函数RANK(num, ref, [order])MID(str, start, len)逻辑函数混合函数选取整列AVERAGEIF(range, criteria, average_range)TR…

【软件测试面试】银行项目测试面试题+答案(一)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试题&#xff1…

Unity类银河恶魔城学习记录9-2 P83 Explosive crystal源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Crystal_Skill_Controller using System.Collections; using System.Colle…

【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用

目录 一、Linux编译器-gcc/g1.1 gcc/g 使用方法1.2 程序的翻译过程1.3 链接 -- 动静态链接特点及区别 二、Linux调试器-gdb2.1 背景2.2 使用方法 三、Linux项目自动化构建工具-make/Makefile3.1 背景3.2 原理3.3 项目清理 一、Linux编译器-gcc/g 1.1 gcc/g 使用方法 格式&…

如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

用BIO实现tomcat

一、前言 本课程的难度较高&#xff0c;需要将Servlet原理和IO课程全部学完。 二、当前项目使用方式 (1).自定义servlet 自定义servlet需要实现WebServlet并且实现name和urlMapping 重启进行访问 http://localhost:8090/myServlet (2).自定义html 重启进行访问 http://loc…

项目的搭建与配置

vue create calendar_pro 选择如下配置选项 安装 vue3 支持 vue add vue-next package.json 关闭 eslint 检测。 vue.config.js 配置跨域同源策略。 const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true,devServe…

微服务:Docker篇

1. 初识Docker 1.1. 什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。 在数百上千台服务中重复部署&#xff0c;环境不一定一…

【计网】TCP协议安全与风险:深入探讨网络通信的基石

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 &#x1f310;前言 &#x1f512;正文 TCP (Transmission Control Protocol): UDP (User Datagram Protocol): HTTP (Hypertext Transfer …

git fatal: detected dubious ownership in repository at ‘xxx‘ 彻底解决方法

前言 在 windows 重置后&#xff0c; git 仓库无法正常使用 git 的所有 命令&#xff0c;运行任何 git 命令&#xff0c;都会提示如下&#xff1a; $ git log fatal: detected dubious ownership in repository at D:/rk/rk3568/nanopi/uboot-rockchip D:/rk/rk3568/nanopi/u…

Linux学习之线程

目录 线程概念 1.什么是线程&#xff1f; 2.线程的优缺点 3.线程异常 4.线程用途 线程操作 1.如何给线程传参 2.线程终止 3.获取返回值 4.分离状态 5.退出线程 线程的用户级地址空间&#xff1a; 线程的局部存储 线程的同步与互斥 互斥量mutex 数据不一致的主要过…

编译随笔(一)makefile基础知识

编译随笔系列文章目录 1. makefile基础知识 文章目录 编译随笔系列文章目录前言参考资料前置知识交叉编译链程序编译预处理&#xff08;Preprpcessing&#xff09;编译&#xff08;Compilation&#xff09;汇编&#xff08;Assemble&#xff09;链接&#xff08;Linking&#…

如何使用Postman创建Mock Server?

这篇文章将教会大家如何利用 Postman&#xff0c;通过 Mock 的方式测试我们的 API。 什么是 Mock Mock 是一项特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行单元测试。通常情况下&#xff0c;Mock 与其他方法的主要区别就是&#xff0c;用于取代代码依赖项的模拟对…

CVPR2023 | 提升图像去噪网络的泛化性,港科大上海AILab提出 MaskedDenoising,已开源!

作者 | 顾津锦 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/o4D4mNM3jL6sYuhUC6VgoQ 当前深度去噪网络存在泛化能力差的情况&#xff0c;例如&#xff0c;当训练集噪声类型和测试集噪声类型不一致时&#xff0c;模型的性能会大打折扣。作者认为其原因在于网络倾向于过度…

Python实现极限学习机分类模型(ELMClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 极限学习机&#xff08;ELMClassifier&#xff09;算法是一种基于单隐层前馈神经网络&#xff08;SLFN…