进程信号生命周期详解
信号和信号量半毛钱关系都没有!
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
查看信号的机制,如默认处理动作man 7 signal
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump。
进程信号
kill命令可以给进程发送信号。kill -l
查看信号列表,共61个信号,其中信号1-31号:普通信号,34-64号:实时信号。我们只需了解普通信号。信号是给进程发的,kill -信号编号 pid
。
进程本身是被程序员编写的属性和逻辑的集合—程序员编码完成的,当进程收到信号时,不一定立即处理。
信号及相关概念
信号是进程之间事件异步通知的一种方式,属于软中断。
[yyq@VM-8-13-centos 2023_03_01_ProcessSignal]$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
ctrl+c
:是一个组合键,OS会识别为kill -2 pid
,是2号信号,用于终止前台进程。SIGINT的默认处理动作是终止进程。
-
实际执行信号的处理动作称为信号递达(Delivery)。
-
信号从产生到递达之间的状态,称为信号未决(Pending)。(信号产生了,但是没有被执行)
-
信号可以被进程选择性阻塞。
-
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号的生命周期
预备
首先进程需要认识信号,知道信号的属性和对应要做的动作
信号产生
发送信号的本质就是更改对应进程PCB中的信号位图。PCB的管理者是OS,故信号也是由OS发送,因为只有OS有权利修改PCB里的内容。
无论有多少种发送信号的方式,本质都是通过OS向目标进程发送信号,故OS必须提供发送信号/处理信号的相关系统调用。
当信号来临时,进程不一定马上处理这个信号。异步:各自做着不一样的事情,进程继续执行自己的代码,而不执行信号。同步:进程先执行信号,停下自己的代码。由于信号可以随时产生,所以进程对信号是异步的。
常见的信号产生方式
键盘输入
ctrl+c
==> SIGINT-终止前台进程 ;ctrl+\
==> SIGQUIT-终止进程
系统调用
kill() 给别人发任意信号、raise() 给自己发任意信号、abort() 给自己发SIGABRT信号
#include <sys/types.h>
#include <signal.h>
原型int kill(pid_t pid, int sig);
参数pid:pid>0,发送给指定进程; pid==0,发送给调用进程的进程组中的每个进程; pid==-1,发送给所有调用进程有权限发送信号的进程,除了进程1 (init); pid<-1, 发送给进程组中ID为-pid的所有进程sig:信号编号
返回值成功返回0;失败返回-1
#include <signal.h>
原型int raise(int sig);// 相当于kill(getpid(), sig);
参数sig:信号编号
返回值成功返回0;失败返回非0
#include <signal.h>
原型void abort(void);// 相当于kill(getpid(), SIGABRT);
大多数信号的默认处理动作都是终止进程,信号的意义:信号不同代表不同的事件,对事件发生之后的处理动作可以一样。
硬件异常
1、如除0异常的8号信号SIGFPE
那操作系统如何得知下面这个程序在执行除0操作?
答:通过硬件-cpu内的状态寄存器。状态寄存器有个溢出标记位,当除0溢出标记位就变成1,此次运算结果无意义,CPU发生运算异常,OS作为软硬件资源的管理者,就知道是当前的进程导致的硬件异常,于是OS修改该进程的信号标记位。
#include <iostream>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>void handler(int signo)
{std::cout << "捕捉到一个信号,编号是" << signo << std::endl;
} int main()
{signal(SIGFPE, handler);// 只是注册了该信号对应的动作int a = 10;a /= 0;while(true){std::cout << "我是一个进程:" << getpid() << std::endl; sleep(1);}return 0;
}
在执行这个程序后,会一直输出
捕捉到一个信号,编号是8
,不应该只输出1次,程序就被终止了么?
答:首先我们自定义了收到8号信号的动作,就不会执行默认动作(退出)。其次,1、收到信号不一定会引起进程退出,2、进程没有退出就有可能还会被调度,3、cpu内部对应的寄存器只有一个,但是状态寄存器中的内容属于当前进程的上下文,但是用户没有能力去修改寄存器中的内容,4、当进程被切换的时候,就有无数次状态寄存器被保存和恢复的过程,所以每一次恢复的时候就会让OS识别到状态寄存器的溢出标志位,所以一直给进程发8号信号。
2、如访问野指针 11号信号SIGSEGV
int* p = nullptr;
*p = 100;//段错误
那操作系统如何得知下面这个程序访问野指针了?
答:咱们在程序里写的指针,本质是就是虚拟地址,而虚拟地址空间的0号地址,经过页表和MMU(在cou里))的映射,发现当前进程不允许访问0号地址,MMU这个硬件因为越界/野指针,进而发生异常,OS识别到了MMU异常,就将对应进程的信号位图的11位置为1.
软件条件
1、管道读端关闭,写端一直写==>OS直接终止当前进程,13号信号SIGPIPE
2、定时器alarm()
函数 14号信号SIGALRM
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
可以通过alarm()
的使用,我们可以明显观察到IO速度很慢
int main()
{alarm(1); // 1s内计算机能够将数据累加到144250int cnt = 0;while(true){std::cout << cnt++ << std::endl;//这里要进行cnt次IO,速度很慢cnt++;}return 0;
}
---------------------------------------------
int cnt = 0;
void handler(int signo)
{std::cout << "cnt: " << cnt << std::endl;//只需要进行1次IO
}
int main()
{alarm(1); // 1s内计算机能够将数据累加到435882026signal(SIGALRM, handler);while(true){cnt++;}return 0;
}
闹钟信号只会通知1次即只有1次输出,不会像硬件异常那样疯狂输出
int cnt = 0;
void handler(int signo)
{std::cout << "cnt: " << cnt << std::endl;//只需要进行1次IOalarm(1);//这样就可以实现每隔1s打印一次,简单的sleep(1)
}
int main()
{alarm(1); // 1s内计算机能够将数据累加到435882026signal(SIGALRM, handler);while(true){cnt++;}return 0;
}
alarm(0)
表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。
任何一个进程都可以通过alarm系统调用在内核中设置闹钟,那么OS就要对其进行管理,先描述在组织。
struct alarm { uint64_t when; //未来的时间 即时间戳+闹钟设定的时间int type; //闹钟类型一次性或周期性 task_struct *p; //对应的进程 struct alarm* next; //下一个闹钟
};
比如用最小堆来管理,那么堆顶的闹钟就是将来第一个要超时的闹钟,OS就可以周期性的检查堆顶,当操作系统的当前时间>alarm.when,就给对应的进程发送信号。
信号保存
在信号到来到信号被处理的这段时间,称为时间窗口,必须要保存这个信号。会被保存在进程PCBtask_struct{ unsigned int signal}
这个结构体里,unsigned int
有32个比特位,用来表示1-31号信号【比特位的位图结构】。比特位的位置,代表信号编号,比特位的内容,代表是否收到该信号,对应的值为0/1,收到对应的信号就置为1。
保存和处理中间的情况:信号阻塞、信号未决
被阻塞的信号将保持在未决(pending)状态,直到进程解除对信号的阻塞(block),才执行递达动作。普通信号在递达之前产生多次只计一次,因为对应二进制位非0即1。
task_struct {unsigned int pending = 0; //0000 0000 0000 0000 0000 0000 0000 0000 // 从右向左第1个比特位表示1号信号unsigned int block = 0; //0000 0000 0000 0000 0000 0000 0000 0000
}if ((1 << (signo - 1)) & pcb->block)
{//被阻塞,那就不用递达了
}
else
{if((1 << (signo - 1)) & pcb->pending){//未被阻塞,且信号未决,那就递达}
}
①PCB进程控制块中有信号屏蔽状态字(block),信号未决状态字(pending),还有是否忽略标识
②信号屏蔽状态字(block):1代表阻塞,0代表不阻塞;信号未决状态字(pending):1代表未决,0代表信号递达
③向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,如果信号屏蔽状态字阻塞,信号未决状态字(pengding)相应位置1;
若阻塞解除,信号未决状态字(pending)相应位置0,表示信号可以递达了。
④block状态字,pending状态都是64bit,分别代表Linux系统中的64个信号。例如SIGINT是2号信号,对应block状态字中的第二位
⑤block状态字用户可以读写,pending状态字用户只能读,这是信号的设计机制。
注意:即使一个信号没有产生,它仍然可以先被设置为阻塞状态。进程能识别信号,是因为PCB结构中有相应的数据结构(block
位图、pending位图、handler_t表)
信号处理
typedef void(*handler_t)(int signo);//函数指针-表示操作方法
handler_thandler[32] = 0;//函数指针数组,存储32个信号对应的处理方法 数组的下标+1表示信号编号,数组下标对应的内容表示对应信号的处理方法
收到信号后一般会有3个动作。
默认动作
大多数信号的默认处理动作都是终止进程,信号的意义:信号不同代表不同的事件,对事件发生之后的处理动作可以一样。term,core,ign,stop,cont
进程终止时核心转储问题
当进程出现异常的时候,我们将进程对应时刻在内存中的有效数据转储到磁盘
First the signals described in the original POSIX.1-1990 standard.
//表中第一列是各信号的宏定义名称,第二列是各信号的编号,第三列是默认处理动作
Signal Value Action Comment
──────────────────────────────────────────────────────────────────────
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
-
Term表示终止当前进程,OS正常结束该进程
-
Core表示终止当前进程并且Core Dump(Core Dump 用于gdb调试)。在命令行输入
ulimit -a
查看core file size
核心转储功能,云服务器默认是0(关闭该功能),命令行输入ulimit -c 1024
,打开云服务器的核心转储功能并设置大小为1024字节,此时再执行会发生段错误的程序,就会输出Segmentation fault (core dumped)
,且当前目录下会生成一个文件core.引起core问题的进程ID
,给该文件在编译时带上-g
选项,在进行调试的时候,输入core-file core.pid
就可以把对应的核心转储文件打开,就可以看到详细的报错信息。 -
Ign表示忽略该信号
-
Stop表示停止当前进程
-
Cont表示继续执行先前停止的进程
捕捉(自定义动作)
用户自定义信号处理函数称为捕捉。9号信号无法捕捉。信号产生不一定被立即处理,而是在合适的时候(由内核态返回用户态)处理。
内核态:由用户/进程发起的,但实际执行系统调用的身份是内核
用户态:平时自己写的代码是运行在用户态的,而我们的代码难免访问2种资源【OS资源(getpid, waitpid),硬件资源(printf, write, read)】,用户为了访问内核和硬件资源,必须通过系统调用来完成对应的访问。
往往系统调用比较费时间,要进行角色转换。CPU内有寄存器,有个current寄存器指向当前正在运行进程的PCB的起始地址,CR3寄存器表征当前进程的运行级别(0表示内核态,3表示用户态)
合适的时候:从内核态返回用户态的时候,要返回的话,曾经肯定进入了内核态(系统调用、进程切换等),此时要从内核态切换回去,OS会先通过PCB看看每个信号的block表是否被阻塞,没被阻塞的再看对应的pending表是否未决,未决的信号让它递达。【block1,不管;block0&&pending0,不管;block0&&pending==1,查handler表对应的方法并执行】,默认和忽略这两个动作很方便,而自定义动作需要切换回用户态才能执行【不可以在内核态直接执行】,然后再回到内核态获取当前进程的上下文,才能回到用户态跳转的位置继续执行后续代码。
普通信号的自定义捕捉流程:用户态–>内核态【执行系统调用,以及信号检测】–>用户态【执行自定义动作】–>内核态【获取进程上下文】–>用户态。
忽略动作
函数接口
signal()函数 信号捕捉
#include <signal.h>
功能 信号处理器,即可以收到特定信号时,执行自定义动作原型typedef void (*sighandler_t)(int); // 函数指针,传入的int表示信号的编号sighandler_t signal(int signum, sighandler_t handler);//其实就是拿着signum去操作方法表里修改对应信号的操作方法
// 设置了收到2号信号的自定义动作
void handler(int signo)
{std::cout << "捕捉到2号信号" << signo << std::endl;
}// 这里是对signal函数的调用,而不是对handler的调用
// handler对应的方法一般不会执行,除非收到对应的信号
signal(2, handler);// 只是注册了该信号对应的动作
//要想触发该动作,需要按ctrl+c组合键 SIGINT的默认处理动作是终止进程
//2号的默认动作是退出进程,当我们给这个进程的2号信号设置了自定义动作,就会执行自定义动作
附:1-31号信号产生原因
- SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
- SIGINT:当用户按下了组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止里程
- SIGQUIT:当用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信 号。默认动作为终止进程
- SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
- SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件
- SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件
- SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件
- SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件
- SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可 以杀死任何进程的方法
- SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程
- SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件
- SIGUSR2:这是另外一个用户自定义信号 ,程序员可以在程序中定义并使用该信号。默认动作为终止进程
- SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
-
SIGALRM:定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程
-
SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时,缺省产生这个信号。默认动作为终止进程
-
SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程
-
SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号
-
SIGCONT:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为终止进程
-
SIGTTIN:后台进程读终端控制台。默认动作为暂停进程
-
SIGTSTP:停止进程的运行。按下组合键时发出这个信号。默认动作为暂停进程
-
SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程
-
SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号
-
SIGXFSZ:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程
-
SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程
-
SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程
-
SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程
-
SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号
-
SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略
-
SIGPWR:关机。默认动作为终止进程
-
SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件
31号SIGRTMIN~64号SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
相关文章:
进程信号生命周期详解
信号和信号量半毛钱关系都没有! 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2 查看信号的机制,如默认处理动作man 7 signal SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理…...
2023-03-03干活小计
今天见识了 归一化的重要性:归一化 不容易爆炸 深度了解了学习率:其实很多操作 最后的结果都是改变了lr 以房价预测为例:一个点一个点更新 比较 矩阵的更新: 为什么小批量梯度下降 优于随机梯度下降 优于批量梯度下降ÿ…...
操作系统结构
随着操作系统的不断增多和代码规模的不断扩大,提供合理的结构对提升操作系统的安全与可靠性来说变得尤为重要。 1.分层法 指将操作系统分为若干层,最低层位硬件,最高层为用户接口,每层只能调用紧邻它的低层的功能和服务(类似于计…...
[SSD科普] 固态硬盘物理接口SATA、M.2、PCIe常见疑问,如何选择?
前言犹记得当年Windows 7系统体验指数中,那5.9分磁盘分数,在其余四项的7.9分面前,似乎已经告诉我们机械硬盘注定被时代淘汰。势如破竹的SSD固态硬盘,彻底打破了温彻斯特结构的机械硬盘多年来在电脑硬件领域的统治。SSD数倍于HDD机…...
【Java学习笔记】3.Java 基础语法
Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象ÿ…...
Python基础学习6——if语句
基本概念 if语句为条件判断语句,用来判断if后面的语句是真是假。if的用途有很多,比如作为条件测试可以判断两数是否相等与不等、进行数值笔记等等。例子如下: Lego_price (599, 799, 898) if Lego_price[0] 599:print("Correct!&quo…...
有免费的PDF转Word吗?值得收藏的7个免费 PDF转Word工具请收好
PDF 和 DOC 是人们在工作中广泛使用的两种最流行的文档格式。PDF 是 Adobe 的便携式文档格式,DOC 是 Microsoft 的 Word 文档格式。PDF 是一种更安全可靠的文件格式,因为它很难编辑 PDF 文件,但是有一些称为 PDF 编辑器的工具可用于编辑 PDF …...
Thinkphp6使用RabbitMQ消息队列
Thinkphp6连接使用RabbitMQ(不止tp6,其他框架对应改下也一样),如何使用Docker部署RabbitMQ,在上一篇已经讲了->传送门<-。 部署环境 开始前先进入RabbitMQ的web管理界面,选择Queues菜单,点…...
小成本互联网创业怎么做?低成本创业的方法分享
多数人都会有想法创业,尤其是在互联网上面创业,很多人看到了商机,但是因为成本的原因又放弃了,实际上,小成本也可以互联网创业!那么,小成本互联网创业怎么做?低成本创业的方法在这里…...
六、栈、栈的相关问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、栈 1.栈概述 2.栈的实现 2.1 栈的API 2.2 栈的实现 二、栈的括号匹配问题 1.问题描述 2.代码实现 三、逆波兰表达式求值问题 1.问题描述 2.代码 总结 前言 提…...
Java安全停止线程
Thread 类虽提供了一个 stop() 方法(已经被废弃),但由于 stop() 方法强制终止一个正在执行的线程,可能会造成数据不一致的问题,所以在生产环境中最好不要使用。 场景: 由于一些操作需要轮询处理ÿ…...
12 readdir 函数
前言 在之前 ls 命令 中我们可以看到, ls 命令的执行也是依赖于 opendir, readdir, stat, lstat 等相关操作系统提供的相关系统调用来处理业务 因此 我们这里来进一步看一下 更细节的这些 系统调用 我们这里关注的是 readdir 这个函数, 入口系统调用是 getdents 如下调试…...
Windows环境搭建Android开发环境-Android Studio/Git/JDK
Windows环境搭建Android开发环境-Android Studio/Git/JDK 因为休假回来后公司的开发环境由Ubuntu变为了Windows,所以需要重新配置一下开发环境。 工作多年第一次使用Windows环境进行开发工作,作次记录下来。 一、 Git安装 1.1git 标题软件下载 网址&…...
全国爱耳日丨听力受损严重有哪些解决办法
——【科学爱耳护耳,实现主动健康】随着数码电子设备使用越来越方便、日常使用时间越来越长,听力障碍、患上耳道疾病一系列问题也接踵而至,在当下我们必须重视听力健康,采取更科学的听音方式,保护听力健康,…...
【抽水蓄能电站】基于粒子群优化算法的抽水蓄能电站的最佳调度方案研究(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...
【异常】因多租户字段缺少导致Error updating database. Column ‘tenant_id‘ cannot be null
一、报错内容 org.springframework.dao.DataIntegrityViolationException: ### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Column tenant_id cannot be null ### The error may exist in com/xxx/cloud/mall/admin/mapper/Goods…...
类和对象(上)
文章目录 面向对象的初步认知类的实例化this引用对象的构造及初始化封装static成员代码块内部类 对象的打印一、面向对象的初步认知 Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。在java中…...
Java经典面试题——谈谈 Java 反射机制,动态代理是基于什么原理?
典型回答 反射机制是 Java 语言提供的一种基本功能,赋予程序在运行时 自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法ÿ…...
19 客户端服务订阅机制的核心流程
Nacos客户端服务订阅机制的核心流程 说起Nacos的服务订阅机制,大家会觉得比较难理解,那我们就来详细分析一下,那我们先从Nacos订阅的概述说起 Nacos订阅概述 Nacos的订阅机制,如果用一句话来描述就是:Nacos客户端通…...
教师论文|科技专著管理系统
技术:Java、JSP等摘要:随着计算机和互联网技术的发展,社会的信息化程度越来越高,各行各业只有适应这种发展趋势,才能增强自己的适应能力和竞争能力,不断发展壮大。大学作为教育的基地,是社会进步…...
骨传导耳机是什么意思,骨传导耳机的好处具体有哪些
在这个全民都是手机的时代,各种蓝牙耳机,入耳式耳机,真无线耳机等各种款式琳琅满目。而骨传导耳机是一种全新的科技产物,顾名思义就是通过头骨振动将声音传至外耳内的耳机。由于无需入耳,不会对耳朵造成任何影响。那…...
elasticsearch—使用汇总
文档结构1、概念简介2、使用创景3、核心组件4、环境部署5、操作实践官方网站:https://www.elastic.co/cn/elasticsearch/ 官方手册:https://www.elastic.co/guide/en/elasticsearch/reference/8.6/getting-started.html 参考教程: Aÿ…...
聊一聊代码重构——我们为什么要代码重构
代码重构 事情的起因是在去年下半年,我们终于无法承受往年的历史包袱而决定开始进行代码重构。 在以前我们尝试过进行代码重构但是从来没有系统性的考虑过如何重构。在对代码重构的过程中很多经验都是来自《重构:改善既有代码的设计》这本书,…...
【Python学习笔记】第二十九节 Python2 和Python3发生了哪些变化
Python 版本分为两大流派,一个是 Python 2.x 版本,另外一个是 Python 3.x 版本,Python 官方同时提供了对这两个版本的支持和维护。2020 年 1 月 1 日,Python 官方终止了对 Python 2.7 版本(最后一个 Python 2.x 版本&a…...
[oeasy]python0099_雅达利大崩溃_IBM的开放架构_兼容机_oem
雅达利大崩溃 回忆上次内容 个人计算机浪潮已经来临 苹果公司迅速发展微软公司脱离mits准备做纯软件公司IBM用大型机思路制作的5100惨败 Commodore 64 既做计算机又做游戏机 计算机行业和游戏行业 跟随着底层技术不断迭代已经进入了战乱纷纷的年代最终又会如何呢?…...
学术论文投稿之同行评审过程中可能会遭遇哪些偏见?
同行评审过程的顺利进行,在很大程度上取决于学术界的积极参与和相互信任,以及需要参与各方都以负责任的态度行事。作为审稿专家,向作者提供公正、客观的评价是至关重要的。同行评审过程中,若有任何偏离客观性的行为,均…...
Python写一个自动发送直播弹幕的工具,非常简单
哈喽大家好,今天给大家用Python整一个可以在直播间自动发弹幕的工具,来为喜欢的主播疯狂扣6 ! 事情原由昨晚回家,表弟在看LOL直播,看得我气不打一处来,差点就想锤他。 身为程序员的表弟,看直…...
学生档案管理系统的设计与实现
技术:Java、JSP等摘要:本设计是为托普学院学生档案的管理实现电子化而设计的,系统开发采用J2EE技术,数据库采用了SQL Server 2005,因而系统具有很好的扩展性、可移植性,实现了教学资源的信息化管理。主要功…...
JavaEE学习笔记-SpringBoot快速上手、部分注解解释
SpringBoot快速上手 一、快速创建SpringBoot应用1.1利用IDEA提供的Spring Initializr创建Spring Boot应用1.2Spring Boot生成的项目结构介绍1.3初步测试后端是否OK(建立一个controll类)二、热部署2.1 添加依赖2.2 Setting处项目自动化设置2.3 具体项目设置2.4 待选步骤三、注…...
【Python学习笔记】第二十六节 Python PyMySQL
一、什么是 PyMySQL?PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库。可以用它来连接Python和MySQL。如果你追求速度,这是一个很好的选择,因为它比mysql-connector-python快。PyMySQL 遵循 Python 数据库 API v2.0 规范&#x…...
服务器免费/中山seo关键词
目录1. Chrome DevTools功能简介2. 使用Elements调试DOM2.1 查看编辑HTML和DOM2.2 在Console中访问节点2.3 在DOM中断点调试3. 调试样式及CSS3.1 查看和编辑CSS3.2 在元素中动态添加类与伪类3.3 快速调试CSS数值及颜色图形动画等4. 使用 Console 和 Sources 调试 JavaScript4.1…...
制作网站的方法有哪些内容/企业网站建设要多少钱
问题描述:使用HtmlAgilityPack分析页面结构,抓取到要遍历的节点列表后,foreach每个节点,数据取到的都一样,代码如下 HtmlNodeCollection carInfList htmlDoc.DocumentNode.SelectNodes("//div[idbrandtab-1]/div…...
求职网站网页模板/海外推广营销系统
精彩回顾如何实现H5可视化编辑器的实时预览和真机扫码预览功能在线IDE开发入门之从零实现一个在线代码编辑器基于ReactKoa实现一个h5页面可视化编辑器-DooringTS核心知识点总结及项目实战案例分析前言本文是基于上一篇文章介绍H5编辑器 后台管理系统实战的第二篇文章…...
昌平区事业单位公共知识培训网站/百度关键词优化软件怎么样
B/S结构,即Browser/Server(浏览器/服务器)结构,是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在…...
具有价值的微网站建设/今日热点新闻事件2022
原文是财经记者写的,有文学描述思维,所以容易形成兴冲冲读完过瘾了叫好了但就没有下文了。为了防止这个弊病,我是学理工科的,来解构的,给大家把骨头剔出来。一、郁亮CEO(登山与企业运作的相通性)…...
珠海手机网站建设/站长之家关键词挖掘工具
在以下类中添加 description 字段后, class Colors(models.Model):colors models.CharField(u颜色, max_length10)description models.CharField(u描述, max_length10)def __str__(self):return self.colors执行以下初始化数据库的步骤,报错 C:\Pychar…...