snort:预处理器开发HelloWorld

snort:预处理器开发HelloWorld文章目录参考 1 预处理器回顾 2 README PLUGINS3 spp template c 参考 Snort 预处理插件 HelloWorld 程序开发 Snort 预处理器介绍 详细 本专栏所有相关博文使用的 snort 版本均为 2 9 151 预处理器回顾预处理器在 Snort 应用规则前处理接收到的数据预处理器对每一个数据包只执行一次被捕获的数据包首先经过预处理器 然后经过探测引擎根

参考

本专栏所有相关博文使用的snort版本均为 2.9.15.1

0.开胃菜

1. 预处理器回顾

  • 预处理器在Snort应用规则前处理接收到的数据
  • 预处理器对每一个数据包只执行一次
  • 被捕获的数据包首先经过预处理器,然后经过探测引擎根据规则处理。
  • 输入插件和预处理器是同一概念
  • 当Snort接收到数据包的时候,主探测引擎并不能对它们进行处理和应用规则,比如,数据包有可能是分片的,需要重新组装,预处理器就是做这样的工作,使数据能够被探测引擎处理,另外,一些预处理器还可以做一些其它工作,比如探测包中的一些明显错误。
  • 你可以编写自己的预处理器,察看Snort源代码doc目录中的README.PLUGINS文件,你可以获得相关的资料,也可以在templates目录中查看源代码示例。

2. README.PLUGINS

Plugin Info 12/5/99 Martin Roesch Overview: Snort version 1.5 introduces a major new concept, plugins. There are two types of plugin currently available in Snort: detection plugins and preprocessors. Detection plugins check a single aspect of a packet for a value defined within a rule and determine if the packet data meets their acceptance criteria. For example, the tcp flags detection plugin checks the flags section of TCP packets for matches with flag combinations defined in a particular rule. Detection plugins may be called multiple times per packet with different arguments. Preprocessors are only called a single time per packet and may perform highly complex functions like TCP stream reassembly, IP defragmentation, or HTTP request normalization. They can directly manipulate packet data and even call the detection engine directly with their modified data. They can perform less complex tasks like statistics gathering or threshold monitoring as well. Adding New Plugins to Snort as a User: Right now, adding a new plugin to Snort is straightforward but requires you to edit two files by hand. The plugin should consist of two files, "sp_something.c"/"sp_something.h" for detection plugins, and "spp_something.c"/"spp_something.h" for preprocessors. For detection plugins, there are two steps to integrating it with Snort: 1) Edit plugbase.h and insert the line #include "sp_something.h" into the file with the other "#include" statements. Save and close the file. 2) Edit the plugbase.c file and in the InitPlugins() function, add the name of the setup function to the list with the other Setup functions. Save and close the file. 3) Edit the Makefile.am and add the names of the two files to the list of names on the "snort_SOURCES" line. Save and exit the file. Run "automake". Someday, there will be a nice little program that will do all this work for you! Writing New Snort Plugins as a Developer: This process is also pretty straight forward, and the best place to look for information on doing these things is at the files in the "templates" directory. The sp_* files are setup for detection plugins, and the spp_* files refer to the preprocessor files. The main thing to remember once you've got it written is to put the proper includes and function calls into plugbase[.c|.h]. I should probably flesh out this document more, but I think that the best info is in the template files. If you have any questions or comments, don't hesitate to send me an e-mail! 

机翻来自百度

插件信息 1999512日 马丁·罗斯奇 概述: Snort版本1.5引入了一个重要的新概念,即插件。有两种类型 Snort中当前可用插件的数量:检测插件和预处理器。 检测插件检查数据包的一个方面是否有在 一种规则,用于确定包数据是否满足其接受标准。为了 例如,tcp标志检测插件检查tcp数据包的标志部分 用于与特定规则中定义的标志组合匹配。检测 每个包可以使用不同的参数多次调用插件。 预处理器在每个包中只调用一次,并且可以执行很高的性能 复杂功能,如TCP流重组、IP碎片整理或HTTP 请求规范化。它们可以直接操作包数据,甚至调用 检测引擎直接用他们修改过的数据。他们能做的更少 复杂的任务,例如统计数据收集或阈值监视。 以用户身份向Snort添加新插件: 现在,向Snort添加一个新插件很简单,但是需要您 手工编辑两个文件。插件应该由两个文件组成, “sp_something.c”或“sp_something.h”用于检测插件,以及 “spp_something.c”或“spp_something.h”表示预处理器。对于检测插件, 将它与Snort集成有两个步骤: 1) 编辑plugbase.h并插入行 #包括“sp_something.h” 与其他“#include”语句一起放入文件。保存并关闭 文件。 2) 编辑plugbase.c文件,在InitPlugins()函数中,添加 列表中的设置函数和其他设置函数。保存和 关闭文件。 3) 编辑Makefile.am并将两个文件的名称添加到 “snort_SOURCES”行中的名称。保存并退出文件。跑步 “汽车制造”。 总有一天,会有一个很好的小程序来帮你完成所有的工作! 作为开发人员编写新的Snort插件: 这个过程也是非常直接的,而且是最好的地方 有关执行这些操作的信息位于“templates”目录中的文件中。 sp U*文件是为检测插件设置的,spp U*文件是指 预处理器文件。一旦你写好了,最重要的是要记住 是将适当的include和函数调用放入plugbase[.c |.h]。我 也许应该更详细地说明这个文件,但是我认为最好的信息 在模板文件中。如果你有任何问题或意见,不要 不好意思给我发电子邮件! 

3. spp_template.c

/* $Id$ */ /* Snort Preprocessor Plugin Source File Template */ /* spp_template * * Purpose: * * Preprocessors perform some function *once* for *each* packet. This is * different from detection plugins, which are accessed depending on the * standard rules. When adding a plugin to the system, be sure to * add the "Setup" function to the InitPreprocessors() function call in * plugbase.c! * 注意:上面是Snort源文件中spp_template.c (预处理器模板)中的注释, * 这个注释没有及时更新 * 原文中的InitPreprocessors( ) 函数已经被替换成了 RegistPreprocessors( ) * * Arguments: * * This is the list of arguments that the plugin can take at the * "preprocessor" line in the rules file * * Effect: * * What the preprocessor does. Check out some of the default ones * (e.g. spp_frag2) for a good example of this description. * * Comments: * * Any comments? * */ #include <sys/types.h> #include <stdlib.h> #include <ctype.h> #include <rpc/types.h> /* * If you're going to issue any alerts from this preproc you * should include generators.h and event_wrapper.h */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "generators.h" #include "event_wrapper.h" #include "util.h" #include "plugbase.h" #include "parser.h" /* * put in other inculdes as necessary */ /* * your preprocessor header file goes here if necessary, don't forget * to include the header file in plugbase.h too! */ #include "spp_template.h" /* * define any needed data structs for things like configuration */ typedef struct _TemplateData { 
    /* Your struct members here */ } TemplateData; /* * If you need to instantiate the preprocessor's * data structure, do it here */ TemplateData SomeData; /* * function prototypes go here */ static void TemplateInit(u_char *); static void ParseTemplateArgs(char *); static void PreprocFunction(Packet *); static void PreprocCleanExitFunction(int, void *); static void PreprocRestartFunction(int, void *); /* * Function: SetupTemplate() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupTemplate() { 
    /* * link the preprocessor keyword to the init function in * the preproc list */ RegisterPreprocessor("keyword", TemplateInit); DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template is setup...\n"); } /* * Function: TemplateInit(u_char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void TemplateInit(u_char *args) { 
    DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template Initialized\n"); /* * parse the argument list from the rules file */ ParseTemplateArgs(args); /* * perform any other initialization functions that are required here */ /* * Set the preprocessor function into the function list */ AddFuncToPreprocList(PreprocFunction); AddFuncToCleanExitList(PreprocCleanExitFunction, NULL); AddFuncToRestartList(PreprocRestartFunction, NULL); } /* * Function: ParseTemplateArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ static void ParseTemplateArgs(char *args) { 
    /* your parsing function goes here, check out the other spp files for examples */ } /* * Function: PreprocFunction(Packet *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void PreprocFunction(Packet *p) { 
    /* your preproc function goes here.... */ /* * if you need to issue an alert from your preprocessor, check out * event_wrapper.h, there are some useful helper functions there */ } /* * Function: PreprocCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocCleanExitFunction(int signal, void *data) { 
    /* clean exit code goes here */ } /* * Function: PreprocRestartFunction(int, void *) * * Purpose: This function gets called when Snort is restarting on a SIGHUP, * if there's any initialization or cleanup that needs to happen * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocRestartFunction(int signal, void *foo) { 
    /* restart code goes here */ } 

4. spp_template.h

/* $Id$ */ /* Snort Preprocessor Plugin Header File Template */ /* This file gets included in plugbase.h when it is integrated into the rest * of the program. */ #ifndef __SPP_TEMPLATE_H__ #define __SPP_TEMPLATE_H__ /* * list of function prototypes to export for this preprocessor */ void SetupTemplate(); #endif /* __SPP_TEMPLATE_H__ */ 

5. 预处理器的Hello World

5.1 复制模板

进入snort-2.9.15.1/

cp templates/spp_template.h src/preprocessors/spp_helloworld.h 
cp templates/spp_template.c src/preprocessors/spp_helloworld.c 

5.2 包含新文件

vim src/plugbase.c 

在64行,插入

#include "preprocessors/spp_helloworld.h" 

在这里插入图片描述
703行插入

SetupHelloWorld(); 

稍后会在spp_helloworld.c编写这个函数并在spp_helloworld.h中声明

在这里插入图片描述
保存退出

5.3 修改spp_helloworld.h

vim src/preprocessors/spp_helloworld.h 

#ifndef改掉
13行声明上文修改的那个函数

void SetupHelloWorld(); 

在这里插入图片描述

保存退出

5.4 修改spp_helloworld.c

我这个写的挺丑的,应该把声明放一块,函数放一块…下次一定改

先总体看一下,此文件应有如下几个函数及其声明,从上到下:

  • 空函数:HelloWorldReloadFunction。snort reload时调用的函数,可不写函数体
  • 声明:HelloWorldInit
  • 函数:SetupHelloWorld。主程序启动,运行plugbase.c时,调用此函数。此函数内调用RegisterPreprocessor进行预处理器注册,以及调用HelloWorldInit进行预处理器的初始化
  • 函数:HelloWorldMain。预处理器的主函数,即每收到一个符合条件的包都会调用此函数
  • 函数:HelloWorldInit。预处理器的初始化函数,此函数内调用AddFuncToPreprocList,将预处理器的主函数(功能函数)HelloWorldMain添加到对应链表中。在新版snort中,需要有一句session_api->enable_preproc_all_ports(sc,PP_HELLOWORLD_RT, 0x01);
    否则预处理器主函数不能正常工作。
vim src/preprocessors/spp_helloworld.c 

58行,修改为

#include "spp_helloworld.h" 

在这里插入图片描述
然后加上

#include "session_api.h" #include "decode.h" 
// 添加函数到预处理器链表时用的 #define PROTO_MASK 0x0001 

5.4.1 空函数 HelloWorldReloadFunction

// 重加载的函数,可不写函数体 void HelloWorldReloadFunction(){ 
   } 

5.4.2 声明 HelloWorldInit

// 声明 static void HelloWorldInit(); 

5.4.3 函数 主配置函数 SetupHelloWorld

修改函数SetupTemplate()SetupHelloWorld()

// 主配置函数 void SetupHelloWorld() { 
    /* * link the preprocessor keyword to the init function in * the preproc list */ // RegisterPreprocessor("keyword", TemplateInit); // DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template is setup...\n"); #ifndef SNORT_RELOAD // 预处理器名称, 用于初始化预处理器的函数 RegisterPreprocessor("HelloWorld", HelloWorldInit); printf("*wuming-register* HelloWorld start\n"); #else // ReloadFunction可以暂时不写 RegisterPreprocessor("HelloWorld", HelloWorldInit, HelloWorldReloadFunction,NULL,NULL, NULL); printf("*wuming-register* HelloWorld reload\n"); #endif printf("*wuming-setup done*\n"); DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: HelloSnort is setup...\n");); } 

5.4.4 函数 HelloWorldMain

static void PreprocFunction(Packet *p) 
// 实际执行的主函数 static void HelloWorldMain(Packet *p){ 
    printf("*wuming-main* Hello World's main function*\n"); } 

5.4.5 函数 HelloWorldInit

修改 TemplateInit()如下

static void HelloWorldInit(struct _SnortConfig *sc,u_char *args) { 
    // DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template Initialized\n"); /* * parse the argument list from the rules file */ // ParseTemplateArgs(args); /* * perform any other initialization functions that are required here */ /* * Set the preprocessor function into the function list */ DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: HelloSnortInit Initialized\n");); // printf("HelloSnortInit ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^is setup"); // 参数分别为:结构体指针 预处理器实际执行的函数 处理器优先级 预定义常数 自定义解码类型标志 AddFuncToPreprocList(sc, HelloWorldMain, 0x01, PP_HELLOWORLD_RT, PROTO_MASK); session_api->enable_preproc_all_ports(sc,PP_HELLOWORLD_RT, 0x01); printf("*wuming-init done*\n"); // 不需要总结输出的话这行可以删掉 // 预处理器名称, //RegisterPreprocStats("HelloWorld", ProfinetPrintStats); // AddFuncToCleanExitList(PreprocCleanExitFunction, NULL); // AddFuncToRestartList(PreprocRestartFunction, NULL); } 

保存退出

5.5 修改preprocids.h

spp_helloworld.c中预定义常数PP_HELLOWORLD_RT需要添加到 src/preprocids.h

vim src/preprocids.h 

增加一行,然后PP_MAX增加1

在这里插入图片描述

5.6 MakeFile.am

vim src/preprocessors/Makefile.am 

把预处理器的两个文件添加到libspp_a_SOURCES

在这里插入图片描述

5.7 snort.conf

vim your_path/snort.conf 

270行附近 step5,添加5.4中定义的预处理器名称HelloWorld
在这里插入图片描述

5.8 automake && make && make install

automake && make && make install 

在这里插入图片描述
没有报错的话就可以启动了,有报错的话就回去DEBUG
一定要注意对应的函数名、常量名是否正确,否则会出现各种奇怪的BUG

5.9 启动snort

我使用-A参数直接打印到控制台

snort -A console -i ens33 -c your_path/snort.conf 

在这里插入图片描述

6. 完整的 spp_helloworld.c

再次说明一下,写的很丑,其实原来的模板中都有注释的,最好还是按对应的位置来写。

/* $Id$ */ /* Snort Preprocessor Plugin Source File Template */ /* spp_template * * Purpose: * * Preprocessors perform some function *once* for *each* packet. This is * different from detection plugins, which are accessed depending on the * standard rules. When adding a plugin to the system, be sure to * add the "Setup" function to the InitPreprocessors() function call in * plugbase.c! * * Arguments: * * This is the list of arguments that the plugin can take at the * "preprocessor" line in the rules file * * Effect: * * What the preprocessor does. Check out some of the default ones * (e.g. spp_frag2) for a good example of this description. * * Comments: * * Any comments? * */ #include <sys/types.h> #include <stdlib.h> #include <ctype.h> #include <rpc/types.h> /* * If you're going to issue any alerts from this preproc you * should include generators.h and event_wrapper.h */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "generators.h" #include "event_wrapper.h" #include "util.h" #include "plugbase.h" #include "parser.h" #include "session_api.h" #include "decode.h" /* * put in other inculdes as necessary */ /* * your preprocessor header file goes here if necessary, don't forget * to include the header file in plugbase.h too! */ #include "spp_helloworld.h" // 添加函数到预处理器链表时用的 #define PROTO_MASK 0x0001 /* * define any needed data structs for things like configuration */ typedef struct _TemplateData { 
    /* Your struct members here */ } TemplateData; /* * If you need to instantiate the preprocessor's * data structure, do it here */ TemplateData SomeData; /* * function prototypes go here */ static void TemplateInit(u_char *); static void ParseTemplateArgs(char *); static void PreprocFunction(Packet *); static void PreprocCleanExitFunction(int, void *); static void PreprocRestartFunction(int, void *); /* * Function: SetupTemplate() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ // 重加载的函数,可不写 void HelloWorldReloadFunction(){ 
   } // 声明 static void HelloWorldInit(); // 此函数用于调用下面的初始化函数 HelloWorldRTInit() void SetupHelloWorld() { 
    /* * link the preprocessor keyword to the init function in * the preproc list */ // RegisterPreprocessor("keyword", TemplateInit); // DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template is setup...\n"); #ifndef SNORT_RELOAD // 预处理器名称, 初始化预处理器的函数 RegisterPreprocessor("HelloWorld", HelloWorldInit); printf("*wuming-register* HelloWorld start\n"); #else // ReloadFunction可以暂时不写 RegisterPreprocessor("HelloWorld", HelloWorldInit, HelloWorldReloadFunction,NULL,NULL, NULL); printf("*wuming-register* HelloWorld reload\n"); #endif printf("*wuming-setup done*\n"); DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: HelloSnort is setup...\n");); } // 实际执行的主函数 static void HelloWorldMain(Packet *p){ 
    printf("*wuming-main* Hello World's main function*\n"); } /* * Function: TemplateInit(u_char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ // 初始化预处理器 static void HelloWorldInit(struct _SnortConfig *sc,u_char *args) { 
    // DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template Initialized\n"); /* * parse the argument list from the rules file */ // ParseTemplateArgs(args); /* * perform any other initialization functions that are required here */ /* * Set the preprocessor function into the function list */ DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: HelloSnortInit Initialized\n");); // 参数分别为:结构体指针 预处理器实际执行的函数 处理器优先级 预定义常数 自定义解码类型标志 AddFuncToPreprocList(sc, HelloWorldMain, 0x01, PP_HELLOWORLD_RT, PROTO_MASK); session_api->enable_preproc_all_ports(sc,PP_HELLOWORLD_RT, 0x01); printf("*wuming-init done*\n"); // 不需要总结输出的话这行可以删掉 // 预处理器名称, //RegisterPreprocStats("HelloWorld", ProfinetPrintStats); // session_api->enable_preproc_all_ports(sc, PP_HELLOWORLD_RT, PROTO_BIT_IP); // AddFuncToCleanExitList(PreprocCleanExitFunction, NULL); // AddFuncToRestartList(PreprocRestartFunction, NULL); } /* * Function: ParseTemplateArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ static void ParseTemplateArgs(char *args) { 
    /* your parsing function goes here, check out the other spp files for examples */ } /* * Function: PreprocFunction(Packet *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void PreprocFunction(Packet *p) { 
    /* your preproc function goes here.... */ /* * if you need to issue an alert from your preprocessor, check out * event_wrapper.h, there are some useful helper functions there */ } /* * Function: PreprocCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocCleanExitFunction(int signal, void *data) { 
    /* clean exit code goes here */ } /* * Function: PreprocRestartFunction(int, void *) * * Purpose: This function gets called when Snort is restarting on a SIGHUP, * if there's any initialization or cleanup that needs to happen * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocRestartFunction(int signal, void *foo) { 
    /* restart code goes here */ } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233419.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 知识图谱(二)——知识推理

    知识图谱(二)——知识推理知识推理是知识图谱中很重要的一部分,主要用于推理暗含的知识(丰富知识图谱),检查知识库的不一致(知识清洗)知识推理分类演绎推理从一般到特殊的过程.从一般性的前提出发,通过推导,得到具体描述或个别结论(三段论),结论已经蕴含一般性知识中,只是通过演绎推理揭示出来,不能得到新知识.归纳推理从特殊到一般的推理过程.从一类事物的大量特殊事例出发,去推出该类事物的一般性结论(数学归纳法)…

    2022年6月1日
    62
  • 我看撸啊撸

    我看撸啊撸题图:byfrombilojackson360行,行行出状元。我相信今天大家的朋友圈应该被IG刷屏了。ig牛逼。可能有的人还一脸懵逼,不知道到底发生了什么。就…

    2022年8月22日
    3
  • Windows上Nginx的安装教程详解[通俗易懂]

    不要觉得看起来简单就不去做,动手操作是另外一回事,相信我! –阿飞一 背景为了方便本地的开发和验证,于是整理了这一篇Windows上安装Nginx的博文,建议一般学习还是使用Linux,一般正规公司都是在Linux上安装Nginx服务! 本篇内容相对比较简单,如果有Linux上安装过Nginx的伙伴,那么看这一篇应该是比较轻松,而且使用也会很方便!二 下载安装包…

    2022年2月27日
    41
  • 11种经典滤波算法「建议收藏」

    11种经典滤波算法「建议收藏」1、限幅滤波法(又称程序判断滤波法)  A、方法:      根据经验判断,确定两次采样允许的最大偏差值(设为A)      每次检测到新值时判断:      如果本次值与上次值之差&lt;=A,则本次值有效      如果本次值与上次值之差&gt;A,则本次值无效,放弃本次值,用上次值代替本次值  B、优点:      能有效克服因偶然因素引起的脉冲干扰…

    2022年5月3日
    66
  • 怎么新建pytest的ini文件_pytest.ini配置

    怎么新建pytest的ini文件_pytest.ini配置前言pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行查看pytest.ini的配置选项pytest-h找到以下

    2022年7月30日
    1
  • visdom总结[通俗易懂]

    visdom总结[通俗易懂]1叫做env=environment2叫做win=window

    2022年6月17日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号