当前位置: 首页 > news >正文

Liunx系统编程:进程信号的概念及产生方式

目录

一. 进程信号概述

1.1 生活中的信号

1.2 进程信号

1.3 信号的查看

二. 信号发送的本质

三. 信号产生的四种方式

3.1 按键产生信号

3.2 通过系统接口发送信号

3.2.1 kill -- 向指定进程发送信号

3.2.2 raise -- 当自身发送信号

3.2.3 abort -- 向自身发送进程终止信号 

3.3 软件条件产生信号

3.3.1 管道通信读端关闭

3.3.2 时钟问题

3.4 硬件异常产生信号

3.4.1 除0错误

3.4.2 野指针、越界访问问题

四. Core dump问题

4.1 什么是core dump

4.2 进程退出状态中的core dump标记位

4.3 用core文件调试代码

五. 总结


一. 进程信号概述

1.1 生活中的信号

在生活中,信号作为信息通知的一种手段无处不在,我们看到信号后,需要根据信号的种类,来对信号做出特定的反应。如:红绿灯、起床闹钟、警铃、电话铃声等,都属于信号的范畴。

我们能对信号做出相应的反应,是因为我们能够识别信号,并且知道对应的处理动作。按照日常经常,对于生活中的信号,做出以下的特性总结:

  1. 我们能够认识识别某种信号。
  2. 即使没有出现特定信号,我们也知道信号发出之后应当做出什么反应。
  3.  我们可能暂时不对信号进行处理,等待合适时机再处理。
  4. 如果暂时不处理信号,我们需要暂时记住信号,等待处理。

1.2 进程信号

进程信号与生活中的信号,都是用于信息通知的。我们可以这样理解进程信号:进程信号是一种信息通知机制,由用户或操作系统发送给特定的进程,通知进程发生了某事件,进程可以稍后处理。

进程对于信号的处理,与生活中的信号处理策略本质相同,可归为以下三类:

  1. 按照默认方式处理。(红灯停绿灯行,铃响后马上起床等)
  2. 自定义处理方式。(铃响后再睡十分钟回笼觉然后起床)
  3. 忽视信号,即不做任何相应。(铃响后让它一直响,继续在床上睡觉)

同样,我们也可以推断出进程信号应用的特性如下:

  1. 进程有识别信号的能力。(每个信号都有对应的编号用于进程识别)
  2. 进程有默认处理信号的方法。(由程序员写的代码决定默认处理方法)
  3. 进程可以对信号进行延后处理,或根本不处理。
  4. 用户可以自定义对于特定信号的处理方式。(signal函数)
  5. 信号的产生和进程的运行是异步的,即:产生信号时,进程可能在忙自己的事情,延后对信号的处理,而不是立马处理。

1.3 信号的查看

在Linux系统中,通过kill -l指令,可以查看系统中所有内置信号的编号与对应名称,如图1.1所示。

其中2号信号SIGINT,就对应我们经常使用的 Ctrl + C 终止进程。

图1.1 信号列表的查看

观察图1.1,我们发现,信号的最高编号为64,没有0号信号,没有32、33号信号,Liunx总共定义有62个信号。其中:

  • 1 ~ 31:普通信号。
  • 34 ~ 64:实时信号。

普通信号用于OS通过正常的运行调度队列调度的进程,即:进程轮番拿到CPU上去运行,每个进程每次在CPU上运行特定的时间片长度,然后切换运行进程。 

实时信号只用于极少数的生产环境,这是OS要求要对某个进程运行完毕才可以从CPU上拿下,且进程不应在调度队列中等待调度,而是应当立马调度运行。如:车载系统接收到刹车指令,不可以等待其他进程运行,也必须一次完成运行。

通过man 7 signal指令,下翻查找,我们可以看到每种信号的默认处理方式

  • Term -- 进程终止,不发生核心转储。
  • Core -- 进程终止,发生核心转储。 
  • Ign -- 子进程暂停运行或终止。
  • Cont -- 继续运行进程。
  • Stop -- 暂停运行进程。
图1.2 信号的默认处理方式

二. 信号发送的本质

要理解信号发送的本质,首先要理解进程如何保存信号。对信号进行保存,需要记录:a.哪一种信号,b.特定信号是否产生

这让我们很容易想到位图这种数据结构,位图中通过对特定比特位设置0/1值,来表示某件事情是否发生或某对象是否存在。进程PCB中存有位图这种数据结构来保存信号,每种信号都有其对应的编号,每个编号对应位图中的一个bit位,1表示产生并保存了某信号,0表示没有保存某信号。

如果进程收到了某信号但暂时不对信号做出响应,那么就应当将位图中对应的bit位由1置0,等待进程做出了响应之后,在置回0。

总结信号发送的本质为:OS向目标进程写信号 -> OS改变进程中记录信号状态的位图中的特点bit位 -> 完成信号发送过程

Crtl + C 组合键终止进程最终也是改变进程中位图的bit位值,键盘中特定槽位的按键按下,键盘驱动就会将按键按下的信息传给OS,OS就会将按键信息转换为信号,然后发送给进程。

三. 信号产生的四种方式

3.1 按键产生信号

通过组合键的方式,可以向进程发送信号,如:

  • Ctrl + c:向前台进程发送2号信号SIGINT。
  • Ctrl + \:向前台进程发送3号信号SIGQUIT。

其中2号信号的默认处理方式为进程终止,不进行核心转储,3号信号的默认处理方式为终止进程,进程核心转储。(本文后面会对核心转储进行讲解)

如代码和图3.1所示,运行一个间隔1s输出进程pid的进程,通过Ctrl + C组合键和Ctrl + \组合键向这个死循环进程发送信号,进程都终止掉了,即使没有运行到最后的代码。

代码3.1:死循环打印进程pid

#include <iostream>
#include <unistd.h>int main()
{while(true){std::cout << "This is a process, pid:" << getpid() << std::endl;sleep(1);}return 0;
}
图3.1 代码3.1运行结果

3.2 通过系统接口发送信号

3.2.1 kill -- 向指定进程发送信号

函数原型:int kill(pid_t id, int sig)

头文件:#include<sys/types.h>、#include<signal.h>

函数功能:向特定进程发送指定信号

返回值:发送成功返回0,失败发挥-1

编写信号发送程序的源文件kill.cc如代码3.2所示,编译生成可执行文件取名为kill,在命令行参数中输入 ./kill [id] [sig],即可向指定进程发送特定信号。如图3.2所示,通过kill向死循环进程发送8号信号,进程被终止掉了。

8号信号SIGFPE:浮点数错误,常发生于 x/0 的情况(除0错误)

代码3.2:信号发送kill.cc

#include <iostream>
#include <sys/types.h>
#include <signal.h>int main(int argc, char** argv)
{// 发送信号,必须要有三个命令行参数if(argc != 3){std::cout << "参数个数错误" << std::endl;exit(1);}int id = atoi(argv[1]);    // 获取结束信号进程的idint sig = atoi(argv[2]);   // 获取信号编号kill(id, sig);    // 发信号return 0;
}
图3.2 通过kill发送信号

3.2.2 raise -- 当自身发送信号

函数原型:int raise(int sig)

头文件:#include<signal.h>

函数功能:向自身发送指定编号的信号

返回值:发送成功返回0,失败发挥-1

代码3.3在通过fork创建子进程,在子进程中循环5次后,通过raise向子进程本身发送8号信号,父进程阻塞等待子进程退出,接收子进程的返回信号并输出到屏幕,代码运行结果见图3.3。

代码3.3:raise向自身发信号

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){// 子进程代码int count = 0;while(true){std::cout << "[Child process]# [" << getpid() << \"], count:" << ++count << std::endl;if(count == 5){raise(SIGFPE);    //向吱声发送8号信号}sleep(1);}exit(0);}int status = 0;pid_t n = waitpid(id, &status, 0);std::cout << "signal:" << (status & 0x7F) << std::endl;return 0;
}
图3.3 代码3.3的运行结果

3.2.3 abort -- 向自身发送进程终止信号 

函数原型:void abort()

头文件:#include <stdlib.h>

函数功能:向自身发送6号终止信号SIGABRT

代码3.3:abort向自身发送进程终止信号

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){// 子进程代码while(true){std::cout << "[Child process]# pid:" << getpid() << ", ppid:" << getppid() << std::endl;abort();   // 向子进程自身发送进程终止信号}exit(0);}int status = 0;pid_t n = waitpid(id, &status, 0);std::cout << "signal:" << (status & 0x7F) << std::endl;return 0;
}

3.3 软件条件产生信号

软件条件产生信号,就是OS检测到某种软件条件触发或者不满足,由OS向指定进程发送信号。如:a. 管道通信中,读段关闭,写端进程退出   b.时钟问题

3.3.1 管道通信读端关闭

管道通信,是一种提供访问控制的进程间通信方式,一端进程负责写数据(写端),另一端进程负责读数据(读端),如果读端关闭,那么写端就不再有意义,OS会强制终止写端进程。OS终止写端进程的方法就是发送13号信号SIGPIPE。

代码3.4对上述现象进行复现,a. 通过fork创建子进程 -> b. 父进程读数据,子进程写数据 -> c. 父子进程进行一段时间的通信 -> d.关闭父进程读端管道 -> e.父进程阻塞等到子进程退出,输出退出状态信息。运行代码,可见子进程因接收到了13号SIGPIPE信号而终止退出。

代码3.4:管道通信中因软件条件产生信号

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 1.创建管道int pipefd[2] = {0};int n = pipe(pipefd);   // 管道创建if(n == -1){perror("pipe");exit(1);}// 2.创建子进程pid_t id = fork();   // 让子进程写数据, 父进程读数据if(id == 0){// 子进程发送数据// 3. 关闭不需要的fdclose(pipefd[0]);// 4. 写数据// char send_buffer[1024] = {0};std::string message = "[Child process]# Send message to you";while(true){// 子进程间隔一秒发送数据ssize_t sz = write(pipefd[1], message.c_str(), message.size());if(sz > 0) sleep(1);else break;}close(pipefd[1]);exit(1);}// 父进程读取数据// 3. 关闭不需要的fdclose(pipefd[1]);// 4. 开始读取数据char read_buffer[1024] = {0};int count = 0;while(true){ssize_t sz = read(pipefd[0], read_buffer, 1024);if(sz > 0){read_buffer[sz] = '\0';std::cout << "[" << getpid() << "]" << read_buffer << ", count:" << ++count << std::endl;if(count == 5){// 关掉读端// 终止读取死循环close(pipefd[0]);break;}}else break;}// 5. 父进程阻塞等待子进程退出int status = 0;waitpid(id, &status, 0);std::cout << "child process exit, write end, exit signal:" \<< (status & 0x7F) << std::endl;return 0;
}
图3.4 代码3.4的运行结果

3.3.2 时钟问题

首先要了解一下时钟接口函数alarm。

alarm函数:

函数原型:unsigned int alarm(unsigned int second)

头文件:#include<unistd.h>

功能:调用时钟函数second秒后,向进程发送14号SIGALRM信号,在倒计时期间进程继续运行不受影响。

返回值:如果之前有alarm函数被调用,返回上一个时钟倒计时还有几秒,如果之前没有alarm函数被调用,返回0。

代码3.5为算力评估函数,通过时钟函数,自定义处理14号SIGALRM信号的方法,计算1s内CPU能够进行多少次的++操作,在自定义信号处理方法中输出。

代码3.5:通过alarm函数进行算力评估

#include <iostream>
#include <unistd.h>
#include <signal.h>uint64_t count = 0;void handler(int sig)   // 自定义信号处理函数
{std::cout << "count end, final count:" << count << std::endl;exit(0);
}int main()
{signal(SIGALRM, handler);alarm(1);   // 倒计时1秒while(true) ++count;return 0;
}
图3.5 代码3.5的运行结果

3.4 硬件异常产生信号

硬件异常,被硬件以某种方式记录(通常为寄存器中的状态标记信息),由OS检测到,并产生信号发送给进程。比如:除0错误被CPU检查并记录,OS检测到并产生SIGFPE信号发送给进程,再比如野指针、越界访问内存的问题,MMU检测到了非法访问,由硬件记录异常状态,OS检测到后发送SIGSEGV信号给进程。

Linux中越界访问、野指针等问题通常都被记录为段错误Segmentation fault,对应11号SIGSEGV信号。

3.4.1 除0错误

代码3.6自定义了对SIGFPE信号的处理方法handler_signal,出现异常程序不会退出终止,在主函数中,先人为写出除0错误,然后while(true)死循环。

按照我们的一般认知,进程在接收到信号后只会执行一次对应的自定义处理动作,但是,如图3.6所示,handler_signal函数在不断地被运行,这是为什么?更加奇怪的是,如果将代码中发生/0错误的部分注释掉,通过命令行kill -8的方法,人为向进程发送8号信号,handler_signal就只会被执行一次!

代码3.6:除0错误引发硬件异常

#include <iostream>
#include <signal.h>
#include <unistd.h>void handler_signal(int sig)
{std::cout << "recieve a signal:" << sig << std::endl;sleep(1);
}int main()
{signal(SIGFPE, handler_signal);int a = 10;int b = a/0;while(true) {}return 0;
}
图3.6 代码3.6运行结果

自定义handler_signal后,除零错误产生的现象:

  • 如果代码本身发生除0错误,handler_signal被一次次执行。
  • 如果人为通过kill -8发送信号,handler_signal只被执行一次。 

产生这种现象的原因就在于,代码本身产生的除0错误为硬件异常,而人为kill -8发送信号不经由硬件,直接被OS处理后将信号发送给进程。

我们可以以如下的方式理解硬件产生的除0异常和上面的奇怪现象:

  • 计算是由硬件(CPU)来完成的,CPU会赋值进行溢出检查。
  • CPU的寄存器中会记录有状态信息,其中就包括溢出标志位,如果出现除0错误或其他溢出问题,CPU寄存器中的溢出标志位就会被置1,OS会在计算完成时对CPU寄存器中的状态信息进行检测,如果检测到溢出标记位为1,OS就会向进程发送8号SIGFPE信号。
  • 进程接收到了8号异常信号后,并不一定会退出(如果自定义了处理函数)。
  • 若进程不退出,OS就会对CPU寄存器中的状态信息进行轮巡检测,溢出标记在除零错误发生后并没有被置回0,OS就会不断检测到异常信息,进而不断向进程发送SIGFPE信号,进而一直在调用handler_signal处理信号。
  • 通过kill -8 发送信号,是一种纯软件行为,并没有经过硬件,没有设置过CPU中的标记位,在CPU轮巡检测的过程中也就不会被不断检测出异常。

3.4.2 野指针、越界访问问题

当某个进程需要访问物理内存资源时,需要先拿到进程地址空间的虚拟地址,再通过 页表 + MMU(Memery Manager Unit,内存管理模块) 映射到物理内存的方式来访问。

我们获取认为页表映射是OS所进行的软件行为,其实不是,试想一下,通过页表映射物理内存是进程运行过程中大量、高频执行的操作,如果有OS来进行这一操作,就严重延缓了计算机设备的整机效率。

实际的情况是:OS将页表和虚拟地址信息,以二进制的方式写入到硬件中去,通过虚拟地址 + 页表 映射物理内存的方法,是由硬件来完成的,那么出现越界、野指针等问题,也理应属于硬件异常。

我们可以按照以下方式理解野指针、越界访问的问题:

  • 进程必须要通过地址来访问物理内存,而现代计算机使用的都是虚拟地址。
  • 虚拟地址需要通过 页表 + MMU(Memery Manager Unit,硬件)来完成虚拟地址到实际地址的转换。
  • 在不考虑编译器优化的情况下,如果出现野指针、越界访问等问题,MMU一定会报错。
  • 页表 + MMU映射物理内存地址是硬件层面的操作,出错当然也属于硬件异常,硬件会对相应的寄存器标志位进行设置,供OS进行轮巡检测。

出现野指针、越界访问等问题在Linux中属于段错误,OS会向进程发送11号SIGSEGV信号。

代码3.7模拟了野指针问题产生的硬件异常行为,代码运行结果与3.6一致,都是不间断执行信号处理函数signal_handler,因为OS在对硬件寄存器中的信息进行轮巡检测。

代码3.7:野指针问题引发硬件异常

#include <iostream>
#include <signal.h>
#include <unistd.h>void handler_signal(int sig)
{std::cout << "recieve a signal:" << sig << std::endl;sleep(1);
}int main()
{signal(SIGSEGV, handler_signal);int* pa = nullptr;*pa = 10;while(true) {}return 0;
}
图3.7 代码3.7的运行结果

四. Core dump问题

4.1 什么是core dump

通过man 7 signal,查看每种信号对应的默认处理方法,如果为Core,那么默认会产生Core文件,OS会将当前进程内存中的核心数据全部保存到Core文件中,Core文件是用来调试的。

如:3号SIGQUIT信号、8号SIGFPE信号、11号SIGSEGV信号,默认触发响应都会产生Core文件,而2号SIGINT、9号SIGKILL信号,默认不会产生Core文件。

一般而言,生产环境、云服务器的core dump功能都是关闭的,如果想要在响应信号的时候产生core文件,还必须保证core dump功能打开。

  • ulimit -a 指令:可用于查看core dump功能是否打开,如果输出结果的第一行core file size为0,那么表示core dump功能没有打开。
  • ulimit -c [size] 指令 -- 通过指定file文件的大小(非0),来打开core file功能。
图4.1 查看和开启core dump

4.2 进程退出状态中的core dump标记位

通过wait/waitpid函数,可以实现父进程对子进程的等待,wait/waitpid函数都可以接收int*类型的输出型参数,用于记录子进程的退出状态,我们称之为int status,status在进程正常退出和异常退出的状态下,如图4.2所示根据退出状态和status比特位的划分,对应子进程退出状态。

图4.2 进程退出状态status的比特位使用情况

代码4.1创建进行死循环的子进程,并且waitpid阻塞等待子进程退出,输出信号编号和core dump标记位,在代码运行起来后,通过在命令行中向子进程发送3号信号(kill -3 [pid]),强制子进程终止并在进程终止后通过ll指令查看当前路径下的文件及属性,可以看到生成的core文件。

代码4.1:子进程信号编号和core dump标志的获取

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main()
{// 1. 创建子进程pid_t id = fork();   if(id < 0){perror("fork");exit(1);}else if(id == 0){// 2. 子进程代码while(true){std::cout << "[Child process]# pid:" << getpid() << ", getppid:" << getppid() << std::endl;sleep(1);}exit(0);}// 3. 父进程阻塞等待子进程,并获取退出状态int status = 0;waitpid(id, &status, 0);//4. 检查子进程退出状态if(WIFEXITED(status)){std::cout << "子进程正常终止" << std::endl;std::cout << "exit code: " << WEXITSTATUS(status) << std::endl;}else{std::cout << "子进程被信号所杀" << std::endl;std::cout << "signal:" << (status & 0x7F) << ", core dump:" << ((status >> 7) & 1) << std::endl;}return 0;
}
图4.3 代码4.1的运行结果

4.3 用core文件调试代码

Linux下的gcc/g++编译器默认采用Release版本发布可执行程序,若要对代码进行调试,就需要Debug版本的可执行程序,这就需要在使用gcc/g++编译源文件的时候添加-g选项。

代码4.2人为制造除0错误,这样就会有8号信号发给进程,8号信号默认会产生core文件。编译代码,生成Debug版本的可执行程序。使用gdb启动调试之后,在命令行中输入指令core-file [core文件名],就会显示出出现异常的行数,以及其他的错误信息。

gdb指令:core-file [core文件名] -- 引入core文件调试代码,定位出错位置,查看错误信息

代码4.2:出现异常并生成Core文件的测试代码

#include <iostream>
#include <unistd.h>int main()
{int a = 10;int b = a/0;sleep(1);std::cout << "run here" << std::endl;return 0;
}
图4.4 代码4.3的运行和调试结果

五. 总结

  • 进程信号用于向进程发送信息,通知进场某件事情的发生,进程咱叔不响应信号,等待合适的时机再进行响应。
  • 进程处理信号的方式和分为三种:a. 按照默认方式响应  b.按照用户自定义的方式响应  c.直接忽略信号,不做处理。
  • 如果进程暂时不响应信号,则需要对进行进行暂时保存。信号保存需要存储信号编号以及它是否产生,Linux中使用位图保存信号,而位图存于每个进程的PCB中。
  • 发送信号的本质,就是更改位图中对应的二进制位。
  • 信号可以通过四种方式产生:1.按键  2.系统接口调用  3.软件行为  4.硬件异常
  • 如果某种信号的默认处理方式会产生core文件,而且系统的Core dump功能处于开启状态,那么进程在接收到这些信号后会生成core文件,core文件用于对代码的调试,可以帮助快速定代码中出现异常的位置以及错误信息。

相关文章:

Liunx系统编程:进程信号的概念及产生方式

目录 一. 进程信号概述 1.1 生活中的信号 1.2 进程信号 1.3 信号的查看 二. 信号发送的本质 三. 信号产生的四种方式 3.1 按键产生信号 3.2 通过系统接口发送信号 3.2.1 kill -- 向指定进程发送信号 3.2.2 raise -- 当自身发送信号 3.2.3 abort -- 向自身发送进程终止…...

宝塔端口监听不到端口

场景&#xff1a; 两个服务器同时在安装nginx 出问题导致20011没有在监听&#xff0c;重新删除nginx 就行了 当时一直以为是安全组没有放过端口&#xff0c;其实是没有监听 排查问题 php -S 0.0.0.0:端口 如果可以访问说明链接可以到服务器只是nginx没监听 sudo netstat …...

机器学习入门的概念

导航 一、 人工智能&#xff0c;机器学习&#xff0c;深度学习和传统学习二、数学基础三、编程语言 如果你刚刚入门机器学习&#xff0c;会接触到了非常多的概念。比如人工智能&#xff0c;机器学习&#xff0c;深度学习&#xff0c;神机网络&#xff0c;强化学习&#xff0c;各…...

插入排序优化——超越归并排序的超级算法

插入排序及优化 插入排序算法算法讲解数据模拟代码 优化思路一、二分查找二、copy函数 优化后代码算法的用途题目&#xff1a;数星星&#xff08;POJ2352 star&#xff09;输入输出格式输入格式&#xff1a;输出格式 输入输出样例输入样例输出样例 题目讲解步骤如下AC 代码 插入…...

面试之快速学习STL-容器适配器

1. 容器适配器 简单的理解容器适配器&#xff0c;其就是将不适用的序列式容器&#xff08;包括 vector、deque 和 list&#xff09;变得适用。 注意&#xff1a;默认使用的基础容器不代表一定只能用它&#xff0c;比如queue可以用deque&#xff0c;list。 如果你希望你的qu…...

性能比较 - Spring Boot 应用程序中的线程池与虚拟线程 (Project Loom)

本文比较了 Spring Boot 应用程序中的不同请求处理方法&#xff1a;ThreadPool、WebFlux、协程和虚拟线程 (Project Loom)。 在本文中&#xff0c;我们将简要描述并粗略比较可在 Spring Boot 应用程序中使用的各种请求处理方法的性能。 高效的请求处理在开发高性能后端…...

rust学习-打印结构体中的vec

write! 宏 将格式化后的数据写入到一个缓冲区&#xff08;buffer&#xff09;&#xff0c;而不是直接打印到标准输出或文件中。 这个缓冲区可以是字符串&#xff0c;也可以是需要写入的文件的缓冲区。 write!(writer, format_string, expr1, expr2, ...);writer 参数是一个实…...

FPGA: RS译码仿真过程

FPGA: RS译码仿真过程 在上一篇中记录了在FPGA中利用RS编码IP核完成信道编码的仿真过程&#xff0c;这篇记录利用译码IP核进行RS解码的仿真过程&#xff0c;带有程序和结果。 1. 开始准备 在进行解码的过程时&#xff0c;同时利用上一篇中的MATLAB仿真程序和编码过程&#x…...

PostgreSQL 查询数据表、视图信息

--获得指定schema范围内的所有表和视图的列表&#xff0c;可指定一个排除表前缀模式with param as (select public,iit as schema_name,db2g% as exclude_pattern),base_info as (--获得所有基表select pg_namespace.nspname as schema_name, a.relname as tbl_name ,TBL as tb…...

手撕vector容器

一、vector容器的介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素&#xff0c;但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而且它的大小会被容器自动处理。 总结&#xff1a;vector是一个动态…...

PyMuPDF`库实现PDF旋转功能

本文介绍了一个简单的Python应用程序&#xff0c;用于将PDF文件转换为旋转90度的PDF文件。主要用于csdn网站中导出的博客pdf是横向的&#xff0c;看起来不是很方便&#xff0c;才想到用python编制一个将pdf从横向转为纵向的功能。 功能 该PDF转换工具具有以下功能&#xff1a…...

微人事 登录问题完善

重启服务端的时候&#xff0c;发现前端页面会操作不了&#xff0c;这样后端session会失效&#xff0c;我们就需要让页面重新跳转到登录页 springsecurity配置类后端配置 前端拦截器进行拦截跳转...

【业务功能篇64】安装docker容器,在docker上安装mysql

docker教程&#xff1a; https://www.runoob.com/docker/docker-tutorial.html卸载docker 较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序&#xff0c;请卸载它们以及相关的依赖项。 yum remove docker docker-client docker-client-latest docker-co…...

MyBatis的基本概念和核心组件

MyBatis的基本概念 MyBatis 是一款优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息&#xff0c;将接口和 Java 的 POJOs(Pla…...

sql update执行返回0,能否判断数据不存在

答案&#xff1a;不能。 update执行返回0的情况 1、没有找到需要更新的数据&#xff0c;就是这条记录不存在 例如&#xff1a;where后面的条件是id0&#xff0c;那这条记录肯定是不存在的&#xff0c;返回结果是0 2、更新时的数据和要更新的数据完全一致时 例如&#xff1a;更…...

数据分析 | 调用Optuna库实现基于TPE的贝叶斯优化 | 以随机森林回归为例

1. Optuna库的优势 对比bayes_opt和hyperoptOptuna不仅可以衔接到PyTorch等深度学习框架上&#xff0c;还可以与sklearn-optimize结合使用&#xff0c;这也是我最喜欢的地方&#xff0c;Optuna因此特性可以被使用于各种各样的优化场景。 2. 导入必要的库及加载数据 用的是sklea…...

stm32单片机开关输入控制蜂鸣器参考代码(附PROTEUS电路图)

说明&#xff1a;这个buzzer的额定电压需要改为3V&#xff0c;否则不会叫&#xff0c;源代码几乎是完全一样的 //gpio.c文件 /* USER CODE BEGIN Header */ /********************************************************************************* file gpio.c* brief Thi…...

打印X型的图案

int main() {int n0;int i0;int j0;scanf("%d",&n);for(i0;i<n;i){for(j0;j<n;j){if(ij){printf("*");}else if((ij)n-1){printf("*");}elseprintf(" ");}printf("\n");}return 0; }...

不含数字的webshell绕过

异或操作原理 1.首先我们得了解一下异或操作的原理 在php中&#xff0c;异或操作是两个二进制数相同时&#xff0c;异或(相同)为0&#xff0c;不同为1 举个例子 A的ASCII值是65&#xff0c;对应的二进制值是0100 0001 的ASCII值是96&#xff0c;对应的二进制值是 0110 000…...

Mac上传项目源代码到GitHub的修改更新

Mac上传项目源代码到GitHub的修改更新 最近在学习把代码上传到github&#xff0c;不得不说&#xff0c;真的还挺方便 这是一个关于怎样更新项目代码的教程。 首先&#xff0c;在本地终端命令行打开至项目文件下第一步&#xff1a;查看当前的git仓库状态&#xff0c;可以使用git…...

Android6:片段和导航

创建项目Secret Message strings.xml <resources><string name"app_name">Secret Message</string><string name"welcome_text">Welcome to the Secret Message app!Use this app to encrypt a secret message.Click on the Star…...

ClickHouse AST is too big 报错问题处理记录

ClickHouse AST is too big 报错问题处理记录 问题描述问题分析解决方案1、修改系统配置2、修改业务逻辑 问题描述 项目中统计报表的查询出现 AST is too big 问题&#xff0c;报错信息如下&#xff1a; 问题分析 报错信息显示 AST is too big。 AST 表示查询语法树中的最大…...

DPDK系列之二十七DIDO

一、DIDO介绍 随着计算机技术发展&#xff0c;特别是应用技术的快速发展。应用场景对计算机的处理速度几乎已经到了疯狂的地步。说句大白话&#xff0c;再快的CPU也嫌慢。没办法&#xff0c;CPU和IO等技术基本目前都处在了瓶颈之处&#xff0c;大幅度提高&#xff0c;短时间内…...

《游戏编程模式》学习笔记(七)状态模式 State Pattern

状态模式的定义 允许对象在当内部状态改变时改变其行为&#xff0c;就好像此对象改变了自己的类一样。 举个例子 在书的示例里要求你写一个人物控制器&#xff0c;实现跳跃功能 直觉上来说&#xff0c;我们代码会这么写&#xff1a; void Heroine::handleInput(Input input…...

博客系统之功能测试

博客系统共有&#xff1a;用户登录功能、发布博客功能、查看文章详情功能、查看文章列表功能、删除文章功能、退出功能 1.登录功能&#xff1a; 1.1测试对象&#xff1a;用户登录 1.2测试用例 方法&#xff1a;判定表 用例 编号 操作步骤预期结果实际结果截图1 1.用户名正确…...

CJS和 ES6 的语法区别

CommonJS 使用 module.exports 导出模块。ES6 使用 export 导出模块。 示例代码&#xff1a; CommonJS&#xff08;CJS&#xff09;模块的导出&#xff1a; // 导出模块 module.exports {foo: bar,baz: function() {return qux;} }; ES6 模块的导出&#xff1a; // 导出模…...

ArcGIS Pro如何制作不规则形状图例

在默认的情况下&#xff0c;ArcGIS Pro生成的图例是标准的点、直线和矩形的&#xff0c;对于湖泊等要素而言&#xff0c;这样的表示方式不够直观&#xff0c;我们可以将其优化一下&#xff0c;制作不规则的线和面来代替原有图例&#xff0c;这里为大家介绍一下制作方法&#xf…...

微软Win11 Dev预览版Build23526发布

近日&#xff0c;微软Win11 Dev预览版Build23526发布&#xff0c;修复了不少问题。牛比如斯Microsoft&#xff0c;也有这么多bug&#xff0c;所以你写再多bug也不作为奇啊。 主要更新问题 [开始菜单&#xff3d; 修复了在高对比度主题下&#xff0c;打开开始菜单中的“所有应…...

【NEW】视频云存储EasyCVR平台H.265转码配置增加分辨率设置

关于视频分析EasyCVR视频汇聚平台的转码功能&#xff0c;我们在此前的文章中也介绍过不少&#xff0c;感兴趣的用户可以翻阅往期的文章进行了解。 安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各…...

【数据结构】如何用队列实现栈?图文详解(LeetCode)

LeetCode链接&#xff1a;225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 本文默认读者已经掌握栈与队列的基本知识 或者先看我的另一篇博客&#xff1a;【数据结构】栈与队列_字节连结的博客-CSDN博客 做题思路 由于我们使用的是C语言&#xff0c;不能直接使用队…...

Linux 虚拟机Ubuntu22.04版本通过远程连接连接不上,输入ifconfig只能看到127.0.0.1的解决办法

之前给虚拟机配置静态IP之后&#xff0c;可以直接通过主机Vscode远程连接。但是前一段时间把主机的TCP/IPV4静态IP设置了一下之后&#xff0c;再连接虚拟机就连不上了&#xff0c;于是参考解决虚拟机不能上网ifconfig只显示127.0.0.1的问题&#xff0c;又可以连接上了&#xff…...

C语言刷题训练DAY.9

1.线段图案 解题思路&#xff1a; 这里非常简单&#xff0c;我们只需要用一个循环控制打印即可。 解题代码&#xff1a; #include<stdio.h> int main() {int n 0;while ((scanf("%d", &n)) ! EOF){int i 0;for (i 0; i < n; i){printf("*&…...

CTFHub php://input

1.首先看代码&#xff1a; 这里其实就应该想到的是php://伪协议&#xff1a; php://filter、php://input、php://filter用于读取源码 php://input用于执行php代码 2.其次&#xff0c;判断使用php://input伪协议 而执行php://input伪协议条件是allow_url_include是On 可以先利用…...

React Native expo项目修改应用程序名称

https://expo.dev/accounts/xutongbao/projects npm install --global eas-cli && \eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 app.config.js: export default {name: 学习,slug: learn-gpt,owner: x…...

unity 之Transform组件(汇总)

文章目录 理论指导结合例子 理论指导 当在Unity中处理3D场景中的游戏对象时&#xff0c;Transform 组件是至关重要的组件之一。它管理了游戏对象的位置、旋转和缩放&#xff0c;并提供了许多方法来操纵和操作这些属性。以下是关于Transform 组件的详细介绍&#xff1a; 位置&a…...

基于Opencv的虚拟拖拽项目

预备知识 勾股定理 跟随移动算法 手势识别图解 项目源代码 """ 演示一个简单的虚拟拖拽 步骤&#xff1a; 1、opencv 读取视频流 2、在视频图像上画一个方块 3、通过mediapipe库获取手指关节坐标 4、判断手指是否在方块上 5、是&#xff0c;方块跟着移动 6、…...

基于单片机DHT11温湿度NRF2401无线通信控制系统

一、系统方案 本设计采用STC89C5单片机作为主控制器&#xff0c;从机采用DHT11传感器采集温湿度、按键设置报警阀值&#xff0c;液晶1602显示&#xff0c;蜂鸣器报警&#xff0c;无线NRF2401模块。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统…...

AutoSAR配置与实践(基础篇)2.5 RTE对数据一致性的管理

传送门 点击返回 ->AUTOSAR配置与实践总目录 AutoSAR配置与实践&#xff08;基础篇&#xff09;2.5 RTE对数据一致性的管理 一、 数据一致性问题引入二、 数据一致性的管理2.1 RTE管理 (SWC间)2.2 中断保护 (SWC内)2.3 变量保护IRVS (SWC内)2.4 Task分配2.5 任务抢占控制 一…...

ASP.NET WEB API通过SugarSql连接MySQL数据库

注意&#xff1a;VS2022企业版可以&#xff0c;社区版可能存在问题。实体名称和字段和数据库中的要一致。 1、创建项目&#xff0c;安装SqlSugarCore、Pomelo.EntityFrameworkCore.MySql插件 2、文件结构 2、appsettings.json { “Logging”: { “LogLevel”: { “Default”: …...

08-微信小程序视图层

08-微信小程序视图层 文章目录 视图层 ViewWXML数据绑定列表渲染条件渲染模板引用importimport 的作用域include WXSS尺寸单位样式导入内联样式选择器全局样式与局部样式 WXS注意事项页面渲染数据处理 视图层 View 框架的视图层由 WXML 与 WXSS 编写&#xff0c;由组件来进行…...

[机器学习]特征工程:特征降维

特征降维 1、简介 特征降维是指通过减少特征空间中的维度&#xff0c;将高维数据映射到一个低维子空间的过程。 在机器学习和数据分析中&#xff0c;特征降维可以帮助减少数据的复杂性、降低计算成本、提高模型性能和可解释性&#xff0c;以及解决维度灾难等问题。特征降维通…...

12. Docker可视化工具

目录 1、前言 2、Docker UI 2.1、部署Docker UI 2.2、管理容器 3、Portainer 3.1、部署Portainer 3.2、管理容器 3.3、添加远程Docker 4、Shipyard 1、前言 Docker 提供了命令行工具来管理 Docker 的镜像和运行 Docker 的容器。我们也可以使用图形工具来管理 Docker。…...

css层叠关系

文章目录 cascading声明冲突应用重置样式表a元素的类选择器顺序问题 cascading cascading – 层叠 解决声明冲突的过程&#xff0c;浏览器会自动处理&#xff1b;就是计算样式的权重&#xff0c;权重大的就被选择&#xff1b; 声明冲突 是指多个选择器选中同一个标签&#x…...

【Unity实战篇 】| 如何在小游戏中快速接入一个新手引导教程

前言 【Unity实战篇 】 | 如何在小游戏中快速接入一个新手引导教程一、简单教程描述二、接入Tutorial Master 实现游戏引导2.1 导入Tutorial Master2插件2.2 添加TutorialMasterManager脚本对象2.3 配置Tutorial&#xff0c;用于管理第一段引导内容2.4 配置Stage&#xff0c;用…...

Lookup Singularity

1. 引言 Lookup Singularity概念 由Barry WhiteHat在2022年11月在zkResearch论坛 Lookup Singularity中首次提出&#xff1a; 其主要目的是&#xff1a;让SNARK前端生成仅需做lookup的电路。Barry预测这样有很多好处&#xff0c;特别是对于可审计性 以及 形式化验证&#xff…...

idea 本地版本控制 local history

idea 本地版本控制 local history 如何打开 1 自定义快捷键 settings->keymap->搜索框输入 show history -》Add Keyboard Shortcut -》设置为 CtrlAltL 2 右键文件-》local history -》show history 新建文件 版本1&#xff0c;creating class com.geekmice…这个是初…...

【Freertos基础入门】深入浅出freertos互斥量

文章目录 前言一、互斥量是什么&#xff1f;二、互斥量的使用场景三、互斥量的使用1.创建 2.删除互斥量3.give和take四、示例代码总结 前言 FreeRTOS是一款开源的实时操作系统&#xff0c;提供了许多基本的内核对象&#xff0c;其中包括互斥锁&#xff08;Mutex&#xff09;。…...

皮爷咖啡基于亚马逊云科技的数据架构,加速数据治理进程

皮爷咖啡&#xff08;Peet’s Coffee&#xff09;是美国精品咖啡品牌&#xff0c;于2017年进入中国&#xff0c;为中国消费者带来传统经典咖啡饮品&#xff0c;并特别呈现更加丰富的品质咖啡饮品体验。通过深入应用亚马逊云科技云原生数据库产品Amazon Redshift以及Amazon DMS等…...

C++ string类详解

⭐️ string string 是表示字符串的字符串类&#xff0c;该类的接口与常规容器的接口基本一致&#xff0c;还有一些额外的操作 string 的常规操作&#xff0c;在使用 string 类时&#xff0c;需要使用 #include <string> 以及 using namespace std;。 ✨ 帮助文档&…...

深入浅出Pytorch函数——torch.nn.init.ones_

分类目录&#xff1a;《深入浅出Pytorch函数》总目录 相关文章&#xff1a; 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...