『 Linux 』信号的捕捉及部分子问题

文章目录

    • 信号的捕捉
    • sigaction函数
    • 未决信号集的置零时机
    • 信号处理过程的阻塞
    • 可重入函数
    • volatile 关键字
    • SIGCHLD 信号


信号的捕捉

请添加图片描述

该图为基于信号处理为用户自定义动作的图解;

  • 信号的捕捉

    当一个信号被递达时,如果该信号的处理动作是用户自定义的函数(如int sighandler(int))时就会调用这个函数,该步骤被称为捕捉信号;

  • 用户程序注册新号处理函数

    用户程序中注册了SIGQUIT信号的处理函数sighandler;

  • 切换到内核态

    当前正在执行main函数时发生了中断或是异常,这导致程序从用户态切换到内核态(如图中标注的12);

  • 内核态处理信号

    内核在处理完中断或异常之后,在返回用户态的main函数前检测到有信号SIGQUIT递达(图中标注2);

  • 独立控制流程

    sighandlermain函数使用不同的堆栈空间,之间没有调用和被调用的关系,是两个独立的控制流程(图中标注4);

  • 返回用户态

    sighandler函数返回后通过执行的特殊的系统调用sys_sigreturn再次进入内核(图中标注4);

  • 恢复主流程

    如果没有新的信号被注册,这次返回用户态时将恢复main函数中断前的上下文继续执行(图中标注5);


sigaction函数

请添加图片描述

sigaction()函数与signal()函数相同,功能都为捕捉信号;

NAMEsigaction - examine and change a signal actionSYNOPSIS#include <signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCEsiginfo_t: _POSIX_C_SOURCE >= 199309LRETURN VALUEsigaction() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.

该函数的具体功能用于检查和改变指定型号的处理方式;

它允许程序定义或更改信号处理函数,也可以获取当前信号处理函数的信息;

函数调用成功时返回0,调用失败时返回-1并设置errno;

参数如下:

  • int signum

    表示传入一个int类型的参数,该参数表明需要传入的信号编号;

  • const struct sigaction *act

    传入一个struct sigaction *参数,其中加了const作修饰表示是一个传入型参数;

    该参数用于指定新的信号处理动作,其结构体包含以下成员:

    struct sigaction {void (*sa_handler)(int);      // 信号处理函数指针void (*sa_sigaction)(int, siginfo_t *, void *); // 另一个信号处理函数指针,用于接收额外信息sigset_t sa_mask;             // 在信号处理期间需要阻塞的信号集int sa_flags;                 // 影响信号处理行为的标志位void (*sa_restorer)(void);    // 不常用的字段,一般设置为NULL
    };
    

    在处理普通信号时只需要关注void (*sa_handler)(int)成员与sigset_t sa_masksigset_t sa_mask成员即可;

    • void (*sa_handler)(int)

      该成员为一个函数指针类型,用于指向信号处理函数,如SIG_IGN,SIG_DFL或是用户自定义函数void userhandler(int);

    • sigset_t sa_mask

      该成员为一个sigset_t类型的数据,其中sigset_t类型为操作系统封装的一个位图结构用于依靠该结构对信号集进行操作;

      当一个进程在处理一个信号的时候会将对应的信号添加到其信号屏蔽字(阻塞信号集)中以避免信号方法重复调用;

      设置该成员可以使得进程在处理一个信号时同时将多个信号添加至信号屏蔽字中;

    其余成员可默认设为0;

  • struct sigaction *oldact

    该参数类型与act类型相同,不被const修饰为一个输出型参数;

    该参数用于保存之前的信号处理动作,如果不需要保存旧的信号处理则传递nullptr;

void sighandler(int signo) {// 自定义处理动作printf("sighandler get a signal: %d\n", signo);
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该例子未使用可以同时阻塞多个信号的特性;

运行结果为:

$ ./mysignal 
I am a Process ,id : 29791
^Csighandler get a signal: 2
I am a Process ,id : 29791
^Csighandler get a signal: 2
I am a Process ,id : 29791
^\Quit

当使用Ctrl + C对进程发送SIGINT信号时被捕捉而后执行自定义动作;


未决信号集的置零时机

请添加图片描述

当信号被处理后未决信号集对应位置应置零从而表示该信号已经被处理完毕为递达状态;

实际上未决信号集的置零时机为执行信号处理前,即先将未决信号集对应位置置零再执行处理信号的函数;

以上文代码为基础进行修改:

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作PrintPending();printf("sighandler get a signal: %d\n", signo);
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

添加了PrintPending()函数用于打印整张Pending位图;

运行结果为:

$ ./mysignal 
I am a Process ,id : 29858
^C0000000000000000000000000000000
sighandler get a signal: 2
I am a Process ,id : 29858
^C0000000000000000000000000000000
sighandler get a signal: 2
I am a Process ,id : 29858
^C0000000000000000000000000000000

当进行信号处理并对Pending位图进行打印时并未出现SIGINT位置变为1的情况;

这意味着实际上在进行信号处理函数前对应的Pending位图已经被置零;


信号处理过程的阻塞

请添加图片描述

当一个信号在被进行处理时将会把正在处理的信号添加进信号屏蔽字以阻塞下一个相同的信号;

本质上是防止同一个信号处理函数被重复调用;

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作//   PrintPending();printf("sighandler get a signal: %d\n", signo);while (1) {PrintPending();cout << endl;sleep(1);}
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该程序中当进程捕获到一个信号时将会调用用户自定义动作进行信号处理;

用户自定义动作为打印对应捕捉到的信号而后无限循环打印pending表以验证相同信号在第二次注册时是否会被阻塞保留其未决状态;

运行并在另一个窗口中使用kill -signo <pid>命令向进程发送SIGINT信号,结果为:

$ ./mysignal 
I am a Process ,id : 29921
sighandler get a signal: 20000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000100000000000000000000000000000010^\Quit

当第一次注册SIGINT信号时将执行用户自定义动作,即先打印sighandler get a signal: 2再循环打印pending表;

一开始的pendingSIGINT信号处已经置零表示正在对该信号进行处理;

当再次注册SIGINT信号时由于上一个相同信号未被处理完成,处于未递达状态,相同的信号被阻塞,停留在未决信号集中;

可调用sigaction()函数并设置struct sigaction *act成员中的sigset_t sa_mask位图以能够在进行一个信号的处理时阻塞多个信号;

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作//   PrintPending();printf("sighandler get a signal: %d\n", signo);while (1) {PrintPending();cout << endl;sleep(1);}
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));sigset_t mask;sigemptyset(&mask);// 使用sigaddset()设置信号集sigaddset(&mask, 3);sigaddset(&mask, 4);sigaddset(&mask, 1);sigaddset(&mask, 5);act.sa_mask = mask;  // 将设置好的信号集进行赋值从而设置处理时阻塞act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该函数中定义了一个sigset_t类型的mask,并用sigaddset()将信号1,3,4,5分别加入了信号集,并将该sigset_t类型赋值给sa_mask成员,使其阻塞多个信号;

运行程序并在另一个终端中使用kill命令分别以2,1,2,3,4,5的顺序依次注册信号并观察结果;

对应的结果为:

$ ./mysignal 
I am a Process ,id : 30083
I am a Process ,id : 30083
sighandler get a signal: 2
00000000000000000000000000000100000000000000000000000000000011000000000000000000000000000011100000000000000000000000000011110000000000000000000000000011111

结果为首先处理2号信号SIGINT并进入死循环打印pending表;

向进程再次发送1-5号信号其都被阻塞至未决信号集中;


可重入函数

请添加图片描述

当一个进程在调用一个函数,在函数执行过程中接收到了一个信号发生了中断;

而信号的处理中又需要调用一次该函数,若是出现了数据错误或是数据不一致等错误问题则称该函数为不可重入函数,反之则称为可重入函数;

以该图为例;

全局环境中存在一个链表并对其进行一次头插操作,其头插操作的核心代码为:

	/* 伪代码 */
insert(...){// ...NodeA->next = head;head = &NodeA;// ...
}

即将新头插的节点的next指针指向head所指向的节点;

head重新指向新插入的头结点;

若是在插入过程中,即执行完了NodeA -> next = head;后进程接收到了一个信号并对信号进行处理;

而在信号的处理中需要调用insert()函数进行头插而再次执行该函数时这个过程被称为 “重入” ,即相同函数重复进入;

当执行主控制流时执行insert()函数,在调用insert()时接收信号并处理型号,在处理信号中再次调用了一次insert();

流程图如下:

结果为main()函数和sighandler()先后向链表中插入两个头结点;

而最后只有一个节点被真正插入链表中,使得另一个节点出现 节点丢失 的内存泄漏问题;

这意味着该insert()函数为一个不可重入函数;

可重入函数 , 不可重入函数 都只为一个函数的特点;

如果一个函数符合以下条件之一的则是不可重入函数:

  • 调用了mallocfree

    malloc也是用全局链表来管理堆的;

  • 调用了标准I/O库函数

    标准I/O库的很多实现都以不可重入的方式使用全局数据堆;


volatile 关键字

请添加图片描述

编译器在编译时可以将代码进行优化;

对应的使用不同优化级别使编译时对代码进行优化,常见的有-O0,-O1,-O2,-O3等选项,其中-O0为默认优化,即表示不进行优化;

volatile关键字是用来修饰一个变量以防止编译器过度优化;

int flag = 1;void sighandler(int signo) {printf("sighandler get a signal: %d\n", signo);flag = 0;printf("falg : %d\n", flag);
}int main() {signal(SIGINT, sighandler);while (flag);cout << "process quit sucess" << endl;
}

在这段代码中定义了一个全局变量,且设置了对SIGINT信号的捕获;

在主控制流main函数中使用whileflag为条件进行循环,当条件为真时循环,条件为假时跳出循环;

当捕捉到SIGINT信号时对该信号进行处理,处理方案为使用自定义动作,为将全局变量flag设为0,即条件为假并对当前flag进行一次打印;

使用g++-O3选项进行编译并进行编译优化;

g++ -o mysignal mysignal.cc -g -O3 -Wall -std=c++11

运行该程序并向该程序发送2号信号SIGINT;

$ ./mysignal 
^Csighandler get a signal: 2
falg : 0
^Csighandler get a signal: 2
falg : 0
^Csighandler get a signal: 2
falg : 0

从结果看出,即使注册了SIGINT信号且执行了自定义动作将全局变量flag设为了0但进程仍不退出;

本质原因是由于进行了优化后,其flag参数将被存放至寄存器当中,而main()函数中的循环条件始终以寄存器中的flag进行条件判断,修改的flag确实内存中的flag;

两者出现了隔离,寄存器不会向内存再去读取flag变量而是由于while循环不停判断寄存器中的变量从而使得进程无法正常退出;

该行为即为编译器的一种编译过度优化;

可通过使用volatile关键字修饰来放置过度优化行为;

int flag修改为volatile int flag;

重新使用-O3选项编译并运行,并使用Ctrl + C向进程发送一个2号信号SIGINT信号;

$ ./mysignal 
^Csighandler get a signal: 2
falg : 0
process quit sucess
$ 

结果为使用Ctrl + C发送2号信号SIGINT时进程被终止;


SIGCHLD 信号

请添加图片描述

当一个子进程退出时将会为其父进程发送一个信号,该信号为17号信号SIGCHLD;

该信号默认行为SIG_DFL为忽略;

可在父进程中调用signal()接口捕捉SIGCHLD函数进行验证;

void sighandler(int signo) {sleep(2);printf("parent process catch a signal: %d\n", signo);cout << endl;waitpid(-1, nullptr, WNOHANG); // 不考虑获取进程退出信息 参数2设置为nullptr
}int main() {signal(SIGCHLD, sighandler);pid_t id = fork();if (id == 0) {// childint cnt = 2;while (cnt--) {printf("I am child process,the PID is %d\n", getpid());cout << endl;sleep(1);}cout << "child process quit...." << endl<<endl;;exit(-1);}// parentwhile (1) {printf("I am parent process,the PID is %d\n", getpid());cout << endl;sleep(1);}return 0;
}

在父进程中使用signal()设置捕获17号信号SIGCHLD信号,并设置自定义动作为调用waitpid(-1,nullptr,WNOHANG)以非阻式来等待子进程退出;

fork()创建子进程,子进程在2s后退出进程并向父进程发送17号信号SIGCHLD;

当父进程获取到子进程所发的信号时将该信号进行捕获,而后调用自定义动作对已经僵尸的子进程进行等待处理;

运行程序并在另一个窗口使用shell脚本:

$ while :; do ps axj | head -1 && ps axj | grep mysignal | grep -v grep ; echo "----------------------------" ; sleep 1 ; done

观察父子进程的状态;

运行结果为:

$ ./mysignal 
I am parent process,the PID is 32131I am child process,the PID is 32132I am parent process,the PID is 32131I am child process,the PID is 32132I am parent process,the PID is 32131child process quit....parent process catch a signal: 17I am parent process,the PID is 32131I am parent process,the PID is 32131

2s过后子进程退出,父进程捕获到17号信号并对子进程进行waitpid()清理;

另一个会话显示的结果为:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 Z+    1001   0:00 [mysignal] <defunct>
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 Z+    1001   0:00 [mysignal] <defunct>
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------
^C

子进程僵尸了2s后被父进程回收;

若是不需要子进程的退出信息时父进程可对子进程发出的信号使用signal()进行忽略从而避免子进程停留在僵尸;

以上述代码为基础进行修改:

int main() {
//   signal(SIGCHLD, sighandler);signal(SIGCHLD, SIG_IGN);pid_t id = fork();if (id == 0) {// childint cnt = 2;while (cnt--) {printf("I am child process,the PID is %d\n", getpid());cout << endl;sleep(1);}cout << "child process quit...." << endl<<endl;;exit(-1);}// parentwhile (1) {printf("I am parent process,the PID is %d\n", getpid());cout << endl;sleep(1);}return 0;
}

对应结果为:

 # 程序所在会话$ ./mysignal 
I am parent process,the PID is 32213I am child process,the PID is 32214I am parent process,the PID is 32213I am child process,the PID is 32214I am parent process,the PID is 32213child process quit....I am parent process,the PID is 32213I am parent process,the PID is 32213^C# 脚本所在会话PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
32213 32214 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
32213 32214 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------

结果表示子进程退出时直接退出,其父进程并未接收到子进程的SIGCHLD而是直接将其进行忽略处理;

shell脚本所在会话显示子进程并未在僵尸状态下进行停留而是直接退出;

  • SIGCHLD信号的忽略行为

    SIGCHLD的默认动作是忽略与对SIGCHLD信号进行忽略是两种概念;

    第一种为默认动作实际上是调用signal(SIGCHLD,SIG_DFL),而其对应的行为为忽略;

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

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

相关文章

【AWS基础】AWS服务介绍与基本使用

AWS基础&#xff1a;AWS服务介绍与基本使用 目录 引言AWS概述AWS的核心服务 计算服务存储服务数据库服务网络服务管理和监控服务 AWS的基本使用 创建AWS账户使用EC2实例使用S3存储配置RDS数据库设置VPC网络 AWS的优势AWS的应用场景结论 引言 亚马逊网络服务&#xff08;AWS&…

文物实时状态监控的保护系统

文物是宝贵的历史遗产和文化瑰宝&#xff0c;其保护是我们共同的责任。为了实现文物的安全保护&#xff0c;现代科技提供了各种环境监测设备&#xff0c;可以实时监控文物的状态并采取相应的保护措施。本文将介绍一种利用各种环境监测设备实现文物实时状态监控的保护系统。 一…

【视频讲解】ResNet深度学习神经网络原理及其在图像分类中的应用|附Python代码

全文链接&#xff1a;https://tecdat.cn/?p37134 原文出处&#xff1a;拓端数据部落公众号 分析师&#xff1a;Canglin Li 本文深入探讨了卷积层&#xff08;Convolutional Layer&#xff09;在深度学习框架中的核心作用与操作机制&#xff0c;并分析了其在特征提取、网络构…

近期代码报错解决笔记

1.TypeError: ‘bool’ object is not callable 想print("Type of head:", type(entity_emb[head]))&#xff0c;结果报如下错误&#xff1a; 源代码&#xff1a; 因为 print 仍然被当作一个布尔值处理&#xff0c;而不是作为函数调用。这个问题的根源在于 print …

Android SurfaceFlinger——纹理的绘制流程(二十八)

在系统开机动画的播放流程中,会从给定的资源文件中加载纹理数据并初始化一个 OpenGL 纹理对象,这里我们就来解析软件模拟纹理的绘制流程。 一、纹理概述 在 Android 的 SurfaceFlinger 系统组件中,纹理(Texture)是一个核心概念,特别是在涉及到图形渲染和显示的过程中。 …

关于TS(typescript)的理论知识

关于TS&#xff08;typescript&#xff09;的理论知识 TypeScript 是一种由微软开发的开源编程语言&#xff0c;它是 JavaScript 的一个超集&#xff0c;添加了可选的静态类型和基于类的面向对象编程。TypeScript 最终会被编译成纯 JavaScript 代码&#xff0c;以便在任何支持 …

Adobe Photoshop(Ps)安装包软件下载

一、Adobe Photoshop简介 Adobe Photoshop&#xff08;简称PS&#xff09;是由Adobe Systems公司开发的图像处理软件&#xff0c;它是一款集图像扫描、编辑修改、图像制作、广告创意、图像输入与输出于一体的图形图像处理软件。广泛应用于专业测评、平面设计、广告摄影、影像创…

MongoDB多数据源配置与切换

在MongoDB中配置和使用多数据源主要涉及以下几个步骤&#xff1a; 定义多个数据源的配置&#xff1a; 在应用程序的配置文件中&#xff0c;定义多个MongoDB的数据源&#xff0c;例如在Spring Boot中可以通过application.yml或application.properties文件进行配置。 创建多个Mo…

学习小型gpt源码(自用)

数据集构建_哔哩哔哩_bilibili &#xff08;b站上有一系列课&#xff0c;从数据处理到模型构建和训练使用&#xff09; 什么是batch&#xff1f; 为什么一个batch内的句子要一样长&#xff1f; 不同batch的长度可以不一样&#xff0c;但是同一个batch内长度一样&#xff01;…

【MySQL进阶之路 | 高级篇】数据操作类型的角度理解共享锁,排他锁

1. 从数据操作的类型划分&#xff1a;读锁&#xff0c;写锁 对于数据库并发事务的读-读情况并不会引起什么问题。对于写-写&#xff0c;读-写操作或写-写操作这些情况可能会引起一些问题&#xff0c;需要使用MVCC或者加锁的方式来解决它们。在使用加锁的方式解决问题时&#x…

Vue3点击按钮实现跳转页面并携带参数

前提&#xff1a;有完整的路由规则 1.源页面 <template><div><h1>源页面</h1><!--通过js代码跳转--><template #default"scope"><button click"toTargetView(scope.row)">点击跳转携带参数</button><…

动态超参数优化:在Mojo模型中实现自定义搜索的艺术

动态超参数优化&#xff1a;在Mojo模型中实现自定义搜索的艺术 在机器学习领域&#xff0c;超参数的调整是提高模型性能的关键步骤。Mojo模型&#xff0c;作为机器学习模型的泛称&#xff0c;通常需要通过调整超参数来达到最优状态。然而&#xff0c;传统的超参数搜索方法往往…

3.3-LSTM的改进

文章目录 1改进点1.1多层化1.2 dropout1.2.1具体概念1.2.2应该插入到LSTM模型的哪里 1.3权重共享 2改进之后的LSTMLM的代码实现2.1初始化2.2前向计算2.3反向传播 3相应的学习代码的实现4总结 1改进点 1.1多层化 加深神经网络的层数往往能够学习更复杂的模式&#xff1b;因此这…

NOI大纲——普及组——素数筛法

素数筛的进化史 1.最为基础的筛法——试除法 时间复杂度 O ( n 2 ) O(n^2) O(n2) int a[10000],tot0,n; for (int i2;i<n;i){bool flagfalse;for (int j2;j<i;j){if (i%j0)flagtrue;}if (flagfalse){a[tot]i;tot;} } for (int i0;i<tot;i){cout<<a[i]<&l…

【0-1 架构问题集合】

常见问题 大数据量高并发情况下如何更新缓存二级目录三级目录 大数据量高并发情况下如何更新缓存 首先是查询的时候&#xff0c;一般先查询缓存&#xff0c;在查询数据库&#xff0c;同步的去更新缓存但是都是异步去更新&#xff0c;引入消息队列MQ 本质是个队列&#xff0c;F…

JVM之经典垃圾收集器

目录 Serial收集 ParNew收集器 Parallel Scavenge收集器 Serial Old收集器 Parallel Old收集器 CMS垃圾收集器&#xff08;重点&#xff09;&#xff1a; Garbage First 收集器&#xff08;重点&#xff09;&#xff1a; Serial收集 使用一个处理器或一条收集线程去完成垃…

利用换元法计算积分的常见题型(考研高数复习)

考研中常见的几种换元法积分计算题 (1)被积式仅包含一个根式&#xff1a;根号下为有 a a a 和 x x x 的平方和/平方差 此种类型的积分题型&#xff0c;可以通过构造单个锐角大小为 t t t 的直角三角形&#xff0c;利用勾股定理和三角函数进行代换。 平方和的情况 形如 ∫…

Java高级工程师应该怎样准备笔试?

哈哈&#xff0c;大早上&#xff0c;有个粉丝问&#xff1a;Java高级工程师应该怎样准备笔试&#xff1f; 这问题还真是小意思啊。Java高级工程师的笔试怎么准备&#xff1f;你问得好&#xff01;咱们混迹江湖这么久&#xff0c;什么风浪没见过&#xff0c;面试笔试这些东西&a…

Java:swagger/knife4j接口返回的json数据中文显示乱码问号???

目录 问题描述问题解决参考文章 问题描述 正常接口的中文返回是正确的 而只要发生异常处理&#xff0c;就会使用全局统一异常处理&#xff0c;输出包含中文字符的json字符串&#xff0c;发现&#xff0c;全都变成了问号?????? /*** 统一异常处理*/ ControllerAdvice Sl…

java学习----注释

简介 override介绍&#xff1a; 添加了这个注释其实是做了个语法校验的作用 override定义 Deprecated介绍&#xff1a; 源码&#xff1a; SuppressWarnings介绍&#xff1a; 源码&#xff1a; 元注解 Retention注解介绍&#xff1a; 案列 Target注解介绍&#xff1a; Documente…