【并发程序设计】13.信号机制

13.信号机制

概念

信号机制是Unix、类Unix以及其他POSIX兼容的操作系统中的一种进程间通讯方式,它允许进程在发生特定事件时接收通知

信号机制是操作系统中的一个重要概念,它提供了一种异步的通知机制,用于在进程之间传递消息。信号可以被看作是一种软中断,它们可以在任何时间被发送给一个进程,以通知该进程发生了某个特定的事件。信号的本质是在软件层次上模拟硬件中断的行为,但它完全由软件控制,因此被称为“软中断”。

信号的处理过程通常涉及以下几个步骤:

  1. 信号的产生:当某个事件发生时,如用户按下Ctrl+C,或者程序访问了非法内存地址,操作系统会产生一个信号。
  2. 信号的传递:产生的信号会被操作系统传递给目标进程。这个过程是异步的,意味着信号可以在任何时间点到达。
  3. 信号的接收:进程通过注册信号处理函数来接收和处理信号。当信号到达时,如果进程已经为该信号注册了处理函数,操作系统会调用该函数。
  4. 信号的处理:在信号处理函数中,进程可以决定如何处理信号。常见的处理方式包括忽略信号、采取默认行为(如终止进程)或执行自定义的操作。
  5. 信号的屏蔽:进程可以选择暂时屏蔽某些信号,这样即使在信号产生时也不会被立即处理。这通常用于避免在某些关键操作中被信号中断。

在Linux系统中,信号机制是通过内核实现的。内核负责管理信号的发送和接收,并通过软中断的方式通知进程。进程可以通过系统调用来设置信号处理函数,从而定义对不同信号的响应方式。

常用信号

信号名代号含义默认操作
SIGHUP1该信号在用户终端关闭时产生,通常是发给和该 终端关联的会话内的所有进程终止
SIGINT2该信号在用户键入INTR字符(Ctrl-C)时产生,内核发送此信号送到当前终端的所有前台进程终止
SIGQUIT3该信号和SIGINT类似,但由QUIT字符(通常是 Ctrl-)来产生终止
SIGILL4该信号在一个进程企图执行一条非法指令时产生终止
SIGSEV5该信号在非法访问内存时产生,如野指针、缓 冲区溢出终止
SIGPIPE13当进程往一个没有读端的管道中写入时产生,代 表“管道断裂”终止
SIGKILL9该信号用来结束进程,并且不能被捕捉和忽略终止
SIGSTOP19该信号用于暂停进程,并且不能被捕捉和忽略暂停进程
SIGTSTP20该信号用于暂停进程,用户可键入SUSP字符( 通常是Ctrl-Z)发出这个信号暂停进程
SIGCONT18该信号让进程进入运行态继续运行
SIGALRM14该信号用于通知进程定时器时间已到终止
SIGUSR1/210/12该信号保留给用户程序使用终止
SIGCHLD17是子进程状态改变发给父进程的。忽略

kill -l 命令查看所有信号

在这里插入图片描述

用到的命令

Kill 命令

  1. 格式kill [参数] [进程号]
  2. 功能:Linux中的kill命令的功能是向指定进程发送信号以终止该进程的运行
  3. 参数
    • -[信号名或代号],例 kill -9 pid 结束进程pid
    • -l(小写L):列出所有可用的信号名称。如果不加信号编号,使用此参数会显示全部信号。
    • -s:指定要发送的信号的名称或编号。这允许用户选择发送不同的信号到进程。
    • -a:当处理当前进程时,不限制命令名和进程号的对应关系。这在批量脚本中尤其有用。
    • -p:指定kill命令只打印相关进程的进程号,而不发送任何信号。
    • -u:指定用户。这个参数用来限定只向特定用户的进程发送信号

killall 命令

  1. 格式killall [参数] [进程名]
  2. 功能结束所有与给定名称匹配的运行中的进程。这可以简化操作,因为用户不需要先查找进程ID(PID),再使用kill命令来终止进程。
  3. 参数
    • -e | --exact:要求进程名与指定名称完全匹配。
    • -I | --ignore-case:在匹配进程名时忽略大小写。
    • -g | --process-group:结束整个进程组而不仅仅是单个进程。
    • -i | --interactive:在结束进程前询问用户确认,实现交互式操作。
    • -l | --list:列出所有已知的信号名称。
    • -q | --quiet:在进程没有结束时不输出任何信息。
    • -s | --signal:发送指定的信号到进程,默认为SIGTERM。
    • -v | --verbose:报告信号是否成功发送到每个匹配的进程。
    • -w | --wait:等待每个被发送信号的进程终止

发送信号的函数

kill 函数

  1. 原型

    #include <signal.h>
    int kill(pid_t pid, int sig)
    
  2. 功能:指定的进程或进程组发送信号

  3. 参数

    • pid:指定要接收信号的进程或进程组
      • pid > 0:信号将被发送到进程ID为pid的进程。
      • pid = 0:信号将被发送到与调用kill()的进程属于同一个进程组的所有进程。
      • pid = -1:信号将被广播发送到系统中所有调用进程有权发送信号的进程,除了进程1(init)。
      • pid < -1:信号将被发送到以-pid为进程组标识的所有进程。
    • int sig:指定要发送的信号的编号。不同的信号编号代表不同的信号,例如SIGTERM代表终止信号,SIGKILL代表强制终止信号。
  4. 返回值

    • 成功,返回0
    • 失败,返回非零数

alarm 函数

  1. 原型

    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);
    
  2. 功能:设置一个定时器,在定时器到期时向调用进程发送SIGALRM信号

  3. 参数seconds:指定定时器的数。如果seconds为0,则取消之前设置的定时器,并返回剩余的时间片。如果seconds非零,则设置一个新的定时器,定时器到期时,将向调用alarm函数的进程发送SIGALRM信号。

  4. 返回值

    • 上一个定时器的剩余时间(以秒为单位)
    • 如果没有设置过定时器,则返回值为0
  5. 注意:如果在定时器到期前再次调用alarm函数设置了新的定时器,那么原来的定时器会被取消,新的定时器将从当前时间开始计时。

pause 函数

  1. 原型

    #include <stdlib.h>
    int pause(void);
    
  2. 功能暂停程序执行,直到接收到一个信号

  3. 参数:无

  4. 返回值

    • 如果成功,则返回-1
    • 如果发生错误,则返回-1并设置errno为相应的错误代码

ualarm 函数

  1. 原型

    #include <unistd.h>
    int ualarm(unsigned int seconds, int interval);
    
  2. 功能:设置一个定时器,在定时器到期时向进程发送SIGALRM信号

  3. 参数

    • seconds:指定定时器的秒数。如果seconds为0,则取消之前设置的定时器。
    • interval:指定间隔时间(以jiffies为单位)。如果interval为0或负数,则表示只执行一次定时器。
  4. 返回值

    • 成功,返回0
    • 失败,返回-1
  5. 注意ualarm函数已经被标记为已废弃(deprecated),因为它的行为在不同的系统和平台上可能不一致。在新的代码中,建议使用setitimer函数来代替ualarm函数,以实现更可靠和可移植的定时器功能。

setitimer函数

  1. 原型

    #include <sys/time.h>
    int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
    
  2. 功能:设置一个定时器,该定时器在到期时会向调用进程发送一个SIGALRM信号

  3. 参数

    • which:指定定时器类型,可以是以下三种之一:
      • ITIMER_REAL:基于真实的时间间隔(Real timer),即按照日历时间来计算。
      • ITIMER_VIRTUAL:基于虚拟的时间间隔(Virtual timer),即按照进程在用户态消耗的CPU时间来计算。
      • ITIMER_PROF:基于进程时间(Profiling timer),即按照进程在用户态和内核态消耗的CPU时间来计算。
    • new_value:指向struct itimerval结构的指针,该结构定义了定时器的间隔时间和总时间。如果这个指针为NULL,则定时器被取消。
    • old_value:指向struct itimerval结构的指针,用于存储之前的定时器设置。如果这个指针为NULL,则不保存之前的定时器设置。
  4. 结构体

    • struct itimerval {struct timeval it_interval; /* 定时器触发的时间间隔 */struct timeval it_value;    /* 第一次触发定时器的时间 */
      };
      
    • struct timeval {time_t tv_sec; // 秒数suseconds_t tv_usec; // 微秒数
      };
      
  5. 返回值

    • 成功,返回0
    • 失败,返回-1
  6. :可以通过调用gettimeofday(&t_start, NULL)来获取当前时间,并将其存储在timeval结构体变量t_start中。

捕获信号

捕获流程

在这里插入图片描述

signal函数

  1. 原型

    #include <signal.h>
    typedef void (*sighandler_t)(int);` 
    sighandler_t signal(int signum, sighandler_t handler);
    
  2. 功能:设置指定信号的处理函数

  3. 参数

    • signum:指定要处理的信号的编号。
    • handler:指向信号处理函数的指针。如果设置为SIG_IGN,则忽略该信号;如果设置为SIG_DFL,则采用系统默认处理方式。
  4. 返回值:返回先前为指定信号设置的处理函数指针,或者如果之前没有设置处理函数,则返回SIG_DFL

  5. 注:typedef void (*sighandler_t)(int);

    • (*sighandler_t) 表示定义了一个名为 sighandler_t 的函数指针类型。这个函数指针类型指向的函数返回值为 void,即没有返回值,同时接受一个整型参数 int,通常用于传递信号编号
  6. 示例

    捕捉SIGINT信号,打印"I cath the SIGINT \n"

    程序执行后按ctrl +c 打印“I cath the SIGINT \n”后信号功能复原,再按一次ctrl +c,程序退出

    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <linux/posix_types.h>typedef void (*sighandler_t)(int);sighandler_t oldact;void handle(int sig){printf("I cath the SIGINT \n");signal(SIGINT,oldact);//回复信号原来的功能
    }int main(){oldact = signal(SIGINT,handle);//设定信号执行的函数,并将原来的处理信号的函数指针赋值给oldactwhile(1){sleep(1);}

sigaction函数

  1. 原型

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    
  2. 功能

    • sigaction 函数用于设置指定信号 signum 的信号处理函数、信号集和标志。
    • 它允许更细致的控制信号的行为,比 signal 函数提供了更多的选项。
  3. 参数

    • signum:要处理的信号编号。
    • act:指向 sigaction 结构体的指针,该结构体定义了新的信号处理行为。
    • oldact:指向 sigaction 结构体的指针,用于存储原来的信号处理行为。如果不需要保存旧的行为,可以设置为 NULL
  4. 返回值

    • 成功时返回 0
    • 失败时返回 -1
  5. sigaction 结构体

    struct sigaction {void (*sa_handler)(int);      // 信号处理函数void (*sa_sigaction)(int, siginfo_t *, void *); // 带有额外信息的信号处理函数(POSIX标准)sigset_t sa_mask;             // 信号集,指定在处理信号时阻塞哪些信号int sa_flags;                 // 影响信号处理行为的标志
    };
    
    • sa_flags 标志
      • SA_NOCLDSTOP:如果子进程被终止,父进程不会成为停止状态。
      • SA_ONSTACK:使用用户栈中的额外空间执行信号处理函数。
      • SA_RESTART:在信号处理完成后,重启被中断的系统调用。
      • SA_RESETHAND:在信号处理结束后,将信号处理函数重置为默认行为。
      • SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数,以提供额外的信号信息。
  6. 注意

    • 不同的操作系统可能对信号的支持不同,因此在使用前应检查操作系统的文档。
    • 在多线程环境中,信号处理可能会被任何线程接收,因此信号处理函数应设计为异步安全的。
    • 当一个信号被捕获时,它的默认行为会被禁止,除非在 sa_handlersa_sigaction 中明确指定。

sigemptyset函数

  1. 原型

    #include <signal.h>
    int sigemptyset(sigset_t *set);
    
  2. 功能:初始化或清空信号集

  3. 参数

    • set:指向要清空的信号集的指针。
  4. 返回值

    • 成功时返回0
    • 失败时返回-1

示例

使用信号回收子进程

  • 信号 17)SIGCHLD 是子进程状态改变发给父进程的。

  • SIGCHLD的产生条件

    1. 子进程终止时

    2. 子进程接收到SIGSTOP信号停止时

    3. 子进程处在停止态,接受到SIGCONT后唤醒时

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>// 信号处理函数
void handle(int sig){wait(NULL); // 等待子进程结束printf("Get sig =%d\n",sig); // 输出接收到的信号编号
}int main()
{pid_t pid; // 用于存储fork()函数的返回值struct sigaction myact; // 用于存储信号处理结构体myact.sa_handler = handle; // 设置信号处理函数为handlemyact.sa_flags = 0; // 设置信号处理标志为0sigemptyset(&myact.sa_mask); // 清空信号集pid = fork(); // 创建子进程// 父进程部分if(pid>0){//wait(NULL);sigaction(SIGCHLD,&myact,NULL); // 设置SIGCHLD信号的处理函数为handlewhile(1){printf("this is father process\n"); // 输出信息sleep(1); // 休眠1秒}}// 子进程部分else if(pid==0){sleep(5); // 休眠5秒exit(0); // 退出子进程}
}

信号阻塞和信号集

有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。

概念:信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

信号的状态:

  • 信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)
  • 信号未决(Pending):从产生到递达之间的状态

信号集

信号集是一组用于表示信号的集合,它包含了一组信号的状态,通常用来表示这些信号是否已经发生或被处理

信号集操作函数

  1. sigset_t set; 自定义信号集。 是一个32bit 64bit 128bit的数组。
  2. sigemptyset(sigset_t *set); 清空信号集
  3. sigfillset(sigset_t *set); 全部置1
  4. sigaddset(sigset_t *set, int signum); 将第signum个信号添加到集合中
  5. sigdelset(sigset_t *set, int signum); 将第signum个信号从集合中移除
  6. sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。

信号屏蔽函数

sigprocmask函数
  1. 原型

    #include <signal.h>
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    
  2. 功能:设定对信号集内的信号的处理方式(阻塞或不阻塞)

  3. 参数

    • how:指定函数的行为,可以是以下值之一:

      • SIG_BLOCK:将参数set中的信号加入到进程的信号屏蔽字中。

      • SIG_UNBLOCK:从进程的信号屏蔽字中移除参数set中的信号。

      • SIG_SETMASK:将参数set设置为进程的信号屏蔽字。

      • SIG_MASK:清空信号集,然后加入原来的信号屏蔽字。

      • SIG_BLOCKSET:类似于SIG_BLOCK,但不会改变原来的信号屏蔽字。

      • SIG_UNBLOCKSET:类似于SIG_UNBLOCK,但不会改变原来的信号屏蔽字。

    • set:指向sigset_t类型的指针,包含了要添加到信号屏蔽字中的信号集合。

    • oldset:指向sigset_t类型的指针,用于存放调用前的信号屏蔽字。

  4. 返回值

    • 成功时返回0,
    • 失败时返回-1,并设置errno为错误码。

示例

SIGINT信号的处理和屏蔽

在刚开始运行时的5秒内按下ctrl+c终端没有反应,五秒结束后信号屏蔽解除,终端打印"I get sig=%d\n"

#include <signal.h>
#include <stdio.h>   
#include <stdlib.h>
#include <unistd.h>void handle(int sig)  // 定义一个信号处理函数,参数为接收到的信号值
{printf("I get sig=%d\n", sig);  // 打印接收到的信号值
}int main()  // 主函数
{struct sigaction act;  // 定义一个sigaction结构体变量actact.sa_handler = handle;  // 设置信号处理函数为handlesigemptyset(&act.sa_mask);  // 初始化清空信号集act.sa_flags = 0;  // 设置标志位为0sigaction(SIGINT, &act, NULL);  // 将SIGINT信号的处理方式设置为actsigset_t set;  // 定义一个信号集变量setsigemptyset(&set);  // 初始化清空信号集sigaddset(&set, SIGINT);  // 将SIGINT信号添加到信号集中sigprocmask(SIG_BLOCK, &set, NULL);  // 阻塞信号集set中的信号(即禁止接收SIGINT信号)sleep(5);  // 暂停执行5秒sigprocmask(SIG_UNBLOCK, &set, NULL);  // 解除对信号集中的信号的阻塞(即允许接收SIGINT信号)while (1) {sleep(1);  }
}

信号阻塞函数

pause函数
  1. 原型

    #include <unistd.h>
    int pause(void);
    
  2. 功能pause函数使当前进程进入睡眠状态,直到收到一个信号为止。在睡眠期间,进程不会占用CPU资源。

  3. 返回值

    • 如果成功,则返回-1,并将errno设置为EINTR(表示被中断)。
    • 如果失败,则返回-1,并将errno设置为错误码。
sigsuspend函数
  1. 原型

    #include <signal.h>
    int sigsuspend(const sigset_t *mask);
    
  2. 功能

    • sigsuspend函数使当前进程进入睡眠状态,直到收到一个信号为止。在睡眠期间,进程不会占用CPU资源。
    • pause函数不同,sigsuspend函数可以指定要阻塞的信号集,即在等待信号时,哪些信号应该被阻塞。
  3. 参数

    • mask:指向sigset_t类型的指针,表示要阻塞的信号集。如果为NULL,则不阻塞任何信号
  4. 返回值

    • 如果成功,则返回-1,并将errno设置为EINTR(表示被中断)
    • 如果失败,则返回-1,并将errno设置为错误码

示例

展示两个方法阻塞信号

  • 法一:通过sigprocmask和pause实现
  • 法二:通过sigsuspend实现
#include <signal.h>  // 引入信号处理相关的头文件
#include <stdio.h>   // 引入标准输入输出相关的头文件
#include <stdlib.h>  // 引入标准库函数相关的头文件
#include <unistd.h>  // 引入Unix系统调用相关的头文件void handle(int sig)  // 信号处理函数
{printf("I get sig=%d\n", sig);  // 打印接收到的信号值
}void mytask()  // 任务函数
{printf("My task start\n");  // 打印任务开始信息sleep(3);  // 暂停3秒printf("My task end\n");  // 打印任务结束信息
}int main()  // 主函数
{struct sigaction act;  // 定义一个sigaction结构体变量actact.sa_handler = handle;  // 设置信号处理函数为handleact.sa_flags = 0;  // 设置标志位为0sigemptyset(&act.sa_mask);  // 初始化清空信号集sigaction(SIGINT, &act, NULL);  // 将SIGINT信号的处理方式设置为actsigaction(SIGHUP, &act, NULL);  // 将SIGHUP信号的处理方式设置为actsigset_t set, set2;  // 定义两个信号集变量set和set2sigemptyset(&set2);  // 初始化清空set2信号集sigaddset(&set, SIGHUP);  // 将SIGHUP信号添加到信号集set中sigaddset(&set, SIGINT);  // 将SIGINT信号添加到信号集set中pause();  // 暂停执行,等待信号的到来while (1)  // 无限循环{sigprocmask(SIG_BLOCK, &set, NULL);  // 阻塞信号集中的信号(即禁止接收SIGHUP和SIGINT信号)mytask();  // 执行自定义的任务函数/*法一*///sigprocmask(SIG_UNBLOCK,&set,NULL);  // 解除对信号集中的信号的阻塞(即允许接收SIGHUP和SIGINT信号)//pause();//暂停执行,等待信号的到来/*法二*/sigsuspend(&set2);  // 挂起进程,直到收到信号为止}printf("After pause\n");  // 打印"After pause"信息while (1)  // 无限循环{sleep(1);  // 每次循环暂停执行1秒}
}

具体分析:

  • sigprocmask:这个函数用于修改进程的信号屏蔽字,即它可以阻止某些信号的传递。当设置了信号屏蔽字后,指定的信号将不会传递给进程,直到取消屏蔽。这种方法的缺点是,如果在取消屏蔽和调用pause之间有信号发生,那么这个信号可能会丢失。
  • pause:这个函数会使进程进入睡眠状态,直到收到一个信号。如果在使用sigprocmask解除信号屏蔽之后调用pause,在这两个调用之间的时间窗口内发生的信号可能会导致pause永远挂起,因为pause只有在接收到信号后才会返回。
  • sigsuspend:这个函数结合了sigprocmaskpause的功能,它在解除信号屏蔽的同时使进程进入睡眠状态。这是一个原子操作,意味着它保证了在信号解除屏蔽和等待信号之间不会有时间窗口,从而避免了信号丢失的问题。因此,如果需要等待某个信号,建议使用sigsuspend而不是单独使用sigprocmaskpause

总的来说,sigprocmask主要用于改变信号屏蔽字,pause用于等待任何信号的到来,而sigsuspend则是在等待特定信号时使用的更为安全的方法,因为它可以保证在等待期间不会错过任何信号

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

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

相关文章

【Python】解决Python报错:IndexError: queue index out of range

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

【Unity Shader入门精要 第12章】屏幕后处理效果(二)

1. 卷积 在图像处理中&#xff0c;卷积操作就是使用一个卷积核对一张图像中的每个像素做一系列的操作。 卷积核通常是一个四方形网格结构&#xff0c;如2x2、3x3的方形区域&#xff0c;该区域内每个方格都有一个权重值。 当对图像中的某个像素进行卷积操作时&#xff0c;将卷…

Linux域名解析不了/网络不可达/虚拟机连接不了的问题

记录域名解析不了/网络不可达/虚拟机连接不了的问题问题 目录 文章目录 记录域名解析不了/网络不可达/虚拟机连接不了的问题问题1.首先确定已经连接上路由器(我的就是在这嗝屁了....)1.1 查看路由表1.2查看当前的网络连接状态&#xff0c;包括网关1.3查看网络接口的状态&…

如何解决研发数据传输层面安全可控、可追溯的共性需求?

研发数据在企业内部跨网文件交换&#xff0c;是相对较为普遍而频繁的文件流转需求&#xff0c;基于国家法律法规要求及自身安全管理需要&#xff0c;许多企业进行内部网络隔离。不同企业隔离方案各不相同&#xff0c;比如银行内部将网络隔离为生产网、办公网、DMZ区&#xff0c…

十四天学会Vue——Vue核心下篇(理论+实战)(第三天)

一、Vue核心下篇 1.15 常用的内置指令 1. v-text <!--准备好一个容器 --><div id"root"><!-- 1.v-text中的字符替换掉div整个字符 --><div v-text"name">你好,{{name}}</div><!-- 2.将标签当做字符串解析 --><di…

网络原理-TCP/IP --应用层

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 目录 3.网络原理 -TCP/IP3.1 应用层 3.网络原理 -TCP/IP 3.1 应用层 应用层是程序员打交道最多的一层,与应用程序直接相关 而应用层的协议,实际上就规定了你写的程序,通过网络传输的时候,按…

【LINUX】LINUX基础(目录结构、基本权限、基本命令)

文章目录 LINUX的目录结构LINUX的基本权限LINUX基本命令 LINUX的目录结构 /&#xff1a;表示根目录bin&#xff1a;存放二进制可执行文件(命令ls、cat、mkdir等)boot&#xff1a;存放系统引导文件dev&#xff1a;存放设备文件etc&#xff1a;存放系统配置文件home&#xff1a;…

LeeCode热题100(爬楼梯)

爬楼梯这个题我断断续续看了不下5遍&#xff0c;哪次看都是懵逼的&#xff0c;就会说是满足动态规划&#xff0c;满足斐波那契数列&#xff0c;也不说为什么。 本文一定让你明白怎么分析这个题的规律&#xff08;利用数学的递推思想来分析&#xff09;&#xff0c;看不懂来打我…

1114 全素日

你好哇&#xff0c;新的一天开始啦&#xff01; solution 取数值的不同部分&#xff0c;联想到借助string #include<iostream> #include<string> using namespace std; bool judge(string s){int n atoi(s.c_str());if(n 1 || n 0) return false;for(int i 2…

618大促买什么数码最划算?数码好物清单整理,买到就是赚到!

618年中大促数码产品爱好者们纷纷摩拳擦掌&#xff0c;准备在这个购物狂欢节里抢购心仪已久的数码好物&#xff0c;在繁多的品牌和型号中挑选出最划算的产品&#xff0c;并不是一件容易的事情&#xff0c;为了帮助大家更好地把握这次购物机会&#xff0c;我们精心整理了一份数码…

【qt】多窗口开发

多窗口开发 一.应用场景二.嵌入的窗口1.设计Widget窗口2.创建窗口3.添加窗口4.总代码 三.独立的窗口1.创建窗口2.显示窗口 四.总结 一.应用场景 多窗口,顾名思义,有多个窗口可以供我们进行操作! 截个小图,你应该就知道了 OK,话不多说,直接开干,先来设计我们的主窗口 需要蔬菜…

《异常检测——从经典算法到深度学习》29 EasyTSAD: 用于时间序列异常检测模型的工业级基准

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …

java mybatis处理大数据量,开启和配置二级缓存,及注意事项,已解决

注意事项&#xff1a; 尽量避免使用下面方式写sql否则会降低服务器性能&#xff1a; mybatis二级缓存开启后&#xff0c;避免使用事务注解&#xff08;加上事务注解后二级缓存数据会导致两次访问不一致问题&#xff09;&#xff1a; 3. 返回的对象实体类&#xff0c;要实现Se…

基恩士激光 速度 曝光等关系

一、基恩士 CtrlN 二、速度设置 计算扫描速度 曝光时间&#xff1a; 1:1 相机点间隔是0.025 &#xff0c;我们要扫描的图像也是1&#xff1a;1的话&#xff0c;速度可以为 采样周期我们设定为3K&#xff0c;假如我们的7000行就够了 速度V0.025&#xff08;线间隔&#xff0…

基于SSM框架的手机商城项目

后端: 订单管理 客户管理&#xff1a; 商品管理 类目管理 前端&#xff1a; 首页&#xff1a;

HTML 转义字符(escape characters)及其对应的符号(symbols)

以下是常见的 HTML 转义字符及其对应的符号&#xff0c;这些可以用于在 HTML 或 JSX 中避免解析错误和特殊字符的冲突&#xff1a; 空格 ( ): 或 引号: 单引号&#xff08;&#xff09;&#xff1a;&apos;、&lsquo;、、&rsquo;双引号&#xff08;"&#x…

字符串 | 字符串匹配之 KMP 算法以及 C++ 代码实现

目录 1 为什么使用 KMP&#xff1f;2 什么是 next 数组&#xff1f;2.1 什么是字符串的前后缀&#xff1f;2.2 如何计算 next 数组&#xff1f; 3 KMP 部分的算法4 完整代码 &#x1f608;前言&#xff1a;这篇文章比较长&#xff0c;但我感觉自己是讲明白了的 1 为什么…

让低代码平台插上AI的翅膀 - 记开源驰骋AI平台升级

让低代码系统插上AI的翅膀——驰骋低代码开发平台引领新时代 在当今日新月异的科技世界中&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各个行业不可或缺的一部分。从制造业的自动化生产到金融行业的智能风控&#xff0c;再到医疗领域的精准诊断&#xff0c;AI技术…

Kafka自定义分区器编写教程

1.创建java类MyPartitioner并实现Partitioner接口 点击灯泡选择实现方法&#xff0c;导入需要实现的抽象方法 2.实现方法 3.自定义分区器的使用 在自定义生产者消息发送时&#xff0c;属性配置上加入自定义分区器 properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,&q…

基于STM32的轻量级Web服务器设计

文章目录 一、前言1.1 开发背景1.2 实现的功能1.3 硬件模块组成1.4 ENC28J60网卡介绍1.5 UIP协议栈【1】目标与特点【2】核心组件【3】应用与优势 1.6 添加UIP协议栈实现创建WEB服务器步骤1.7 ENC28J60添加UIP协议栈实现创建WEB客户端1.8 ENC28J60移植UIP协议并编写服务器测试示…