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

linux之进程信号(初识信号,信号的产生)

目录

  • 引入
  • 一、初识信号(信号预备知识)
    • 1.生活中的信号
    • 2.Linux中的信号
    • 3.信号+进程得出的初步结论
  • 二、信号的产生
    • 1.通过终端输入产生信号
    • 拓展: 硬件中断
    • 2.调用系统函数向进程发信号
    • 3.硬件异常产生信号
    • 4.软件条件产生信号
    • 拓展: 核心转储技术
    • 总结一下:

引入

一、初识信号(信号预备知识)

1.生活中的信号

  • 当你在打着永杰无间的时候,你的外卖到了,外卖员给你打电话去取,而你此时正在打游戏,你心里想着这把打完了去取,过了一会,你打完了这把游戏,你想起来你还有外卖没取,你就马上去取了
  • 信号弹
  • 我们根据红绿灯的颜色过马路
  • 上课铃声响了,我们取上课
  1. 你怎么认识这些信号?
    有人教过我,我就记住了 -> 1.识别信号 2.知道信号的处理方法

  2. 即使我们现在没有信号产生,我们也知道信号产生之后,我该干什么?

  3. 信号产生了之后,我们可能不会立即处理这个信号,我们可能有更重要的事情要做,但是我们必须要把信号产生这个信息保存下来,在合适的时候去处理

2.Linux中的信号

概念:Linux信号通常由操作系统或其他进程发送给目标进程,可以用于多种目的,例如中断进程、终止进程或请求进程执行某个特定操作。本质是一种通信机制。

用kill -l命令可以察看系统定义的信号列表

在这里插入图片描述
可以看到全是大写的,因为linux是用c语言写的,所以就是c语言中的,所以我们也可以使用数字也可以使用以上宏都是一个意思
一共有62个信号,因为32、33不存在,其中,1-31是普通信号,也是本文重点讲解的, 34-64属于实时信号,优先级比较高,立即处理,本文不做讲解。

3.信号+进程得出的初步结论

所以进程信号?

  1. 进程必须识别并能够处理信号 ,信号即使没有产生。也要具备处理信号的能力,怎么做到呢?信号的处理能力,是操作系统给进程内置的功能的一部分

  2. 进程即使没有收到信号,也能知道哪些信号该如何处理

  3. 当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个信号,会在合适的时候区里这个信号 处理动作:1.默认动作 2.忽略 3.自定义动作

  4. 一个进程必须当信号产生,到信号开始处理,就会有一定的时间窗口,这段窗口,进程需要保存哪些信号已经发送了的能力

所以信号要经历: 信号产生 -> 信号保存 -> 信号处理 三个阶段

ctrl + c 为啥能杀死我们的前台进程呢?

  • linux中,一次登录中,一般会配上一个bash,每一个登录,只允许一个进程是前台进程,可以允许多个进程是后台进程。
  • 当我们用键盘输入ctrl + c 快捷键时候,会被解释成2号信号然后发给前台进程
  • 启动进程的时候 加上 & 表示以后台进程的方式启动
  • 所以,当我们以后台进程状态运行时,我们用键盘输入ctrl + c 的时候进程收不到信号,也就不会终止进程,我们只能使用kill命令杀死该进程
    在这里插入图片描述

二、信号的产生

通过以上对linux信号的简单了解后,我们再来看一下信号是如何产生的,以下是信号产生的几种方式

1.通过终端输入产生信号

比如ctrl + c 就是2号信号SIGINT,我们如何来验证呢?
介绍一个函数

SIGNAL(2)                                                                      Linux Programmer's Manual                                                                      SIGNAL(2)NAMEsignal - ANSI C signal handlingSYNOPSIS#include <signal.h>typedef void (*sighandler_t)(int);  //函数指针sighandler_t signal(int signum, sighandler_t handler); //表示当我们收到signum信号后,处理动作为handler方法//,涉及信号处理,此处先简单讲解

实验一:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;
//num:收到了哪个信号
void handler(int num)
{cout << num << " handler ..." << endl;//exit(1);
}
int main()
{signal(SIGINT, handler);//只需设置一次,以后都有效while(1){cout << "i am a process, mypid: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
可以看到输入的ctrl + c被转换成2号信号


其实还有一些快捷键也可以表示为信号,比如ctrl + \ 表示3号信号即SIGQUIT
实验二:
code:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;
void handler(int num)
{cout << "signal: " <<  num << " handler ..." << endl;//exit(1);
}
int main()
{signal(SIGINT, handler);signal(3, handler);while(1){cout << "i am a process, mypid: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
我们可以发现,2号和3号信号的处理动作都是同一个函数,这也就是为handler函数要有一个参数为int,就是为了标识是哪个信号正在处理。


但是有些信号不会使用signal定义的处理动作,比如19号信号SIGSTOP
实验三:
code:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;
void handler(int num)
{cout << "signal: " <<  num << " handler ..." << endl;//exit(1);
}
int main()
{signal(SIGINT, handler);//signal(3, handler);signal(19, handler);while(1){cout << "i am a process, mypid: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
可以看到ctrl + z 并没有调用自定义的signal方法,仍然使进程停止了,这是为什么呢?想知后续请继续往下看!


实验四:
对所有信号都实验signal捕捉

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;
void handler(int num)
{cout << "signal: " <<  num << " handler ..." << endl;//exit(1);
}
int main()
{//signal(SIGINT, handler);//signal(3, handler);//signal(19, handler);for(int i = 1; i <= 31; ++i){signal(i, handler);}while(1){cout << "i am a process, mypid: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
可以看到1-8都被捕捉了,而9号信号没有。我们继续重启进程,继续向后发信号。
在这里插入图片描述
在这里插入图片描述
可以看到10-18都被捕捉了,19不会,也就是之前做的实验三。
继续重启进程,发信号,可以发现都被捕捉了(这里不做演示了)。

所以只有9号和19号信号不会被signal捕捉,这是为什么呢?
可以发现这俩一个是杀死进程,一个是停止进程,都跟进程的执行有关,你觉得操作系统为啥不让所有信号被捕捉呢?很简单,操作系统又不是傻子,万一有个病毒软件什么的,它把所有信号都捕捉了,那它不就永远都杀不掉了嘛。


拓展: 硬件中断

看了上面的,你有什么疑问嘛?
键盘数据是如何传入给内核的?,ctrl + c 又是如何变成信号交给进程呢?
接下来就要谈谈硬件了

首先,进程是由操作系统管理的,所以键盘被按下,肯定是操作系统先知道?那么操作系统又是如何知道键盘上有数据了????
在这里插入图片描述

  1. 操作系统会在启动的时候加载进内存
  2. linux下一切皆文件,每个文件都有自己的缓冲区,即struct file结构体
  3. os怎么知道键盘上有数据?os会对外设进行轮询监测嘛?os系统没这么蠢,系统中这么多设备,操作系统这样的化效率太低了
  4. cpu和键盘通过主板连接在一起,cpu上有很多针脚,和各种设备间接或直接连接着,当键盘中有数据产生的时候,键盘会给cpu发送硬件中断,将cpu特定的针脚发送高低电频,即充放电,然后cpu感受到了。
  5. cpu中的寄存器如何存储数据呢?也就是上述充放电的过程
  6. os内核中有一个中断向量表,就和文件描述符表向上,中断向量表中存储各个设备的读写方法
  7. 当键盘中有数据产生,给cpu发送硬件中断,cpu让操作系统执行中断相量表中的方法去读取键盘中的数据
  8. 当键盘中的数据是一般数据的时候,会被读入缓冲区中,当是快捷键比如ctrl + c 这样的,会被解释成信号由操作系统发送给进程

那么以上所讲硬件中断和我们讲的信号有什么联系嘛?
我们学习的信号,就是利用软件的方式,对进程模拟硬件中断

再谈缓冲区

  1. 平常我们输入命令的时候,我们可以看到我们自己的输入,其实是操作系统将键盘输入缓冲区中的数据拷贝到显示器输出缓冲区中去了,我们就可以看到我们输入的内容了,而linux中输入密码的时候我们看不到密码,也就是os没有把此时的输入缓冲区中的内容拷贝到输出缓冲区中去
  2. 当我们以后台进程的方式启动进程时,我们即使隔一段时间才输入一个完整的命令,也可以正常执行,因为我们我们看到的很长时间才输入完整的命令实在显示器缓冲区中,而输入缓冲区中的数据时连续完整的,所以可以正常执行

在这里插入图片描述

2.调用系统函数向进程发信号

在这里插入图片描述
可以看到,kill系统调用的作用是发送一个信号给指定进程,就不就是和我们命令行中的kill命令一样的嘛,其实我们命令行中的kill命令底层就是调用的kill系统调用。
那么我们直接来做一个kill命令

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstdio>
#include <signal.h>
using namespace std;
static void Usage(const string& cmd)
{cout << "\n\r " << cmd << " signo process_pid " << endl << endl;
}
int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}int sig = stoi(argv[1]);pid_t id = stoi(argv[2]);int ret = kill(id, sig);if(ret == -1){perror("kill");exit(2);}return 0;
}

在这里插入图片描述


在这里插入图片描述
raise作用:发送一个信号给调用者,实际上就是kill的封装,就是kill(getpid(), sig).


在这里插入图片描述
abort作用: 给调用者发送6号信号(SIGABRT,实际也是对kill的封装,但是他有一些特性,直接看实验

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstdio>
#include <signal.h>
using namespace std;
// static void Usage(const string& cmd)
// {
//     cout << "\n\r " << cmd << " signo process " << endl << endl;
// }
void handler(int signum)
{cout << "get a sig : " << signum << endl;
}
int main(int argc, char* argv[])
{// if(argc != 3)// {//     Usage(argv[0]);//     exit(1);// }// int sig = stoi(argv[1]);// pid_t id = stoi(argv[2]);// int ret = kill(id, sig);// if(ret == -1)// {//     perror("kill");//     exit(2);// }signal(6, handler);for(int i = 0; i < 3; ++i){cout << "i am a process pid: " << getpid() << endl;sleep(1);}abort();return 0;
}

在这里插入图片描述
可以看到,即使signal捕捉了6号信号,进程也执行了handler方法,但进程最终还是终止了,所以abort内部采用一定的手段使进程强制停止了。

3.硬件异常产生信号

1.除0错误
code:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstdio>
#include <signal.h>
using namespace std;void handler(int signum)
{cout << "get a sig : " << signum << endl;
}
int main()
{// signal(SIGFPE, handler);cout << "zero before" << endl;sleep(1);int a = 10;cout << 10 / 0 << endl;;cout << "zero after" << endl;return 0;
}

实验现象:
在这里插入图片描述
放开注释后,自定义捕捉SIGFPE信号

实验现象:
在这里插入图片描述
可以看到我们捕捉了8号信号(SIGFPE),所以发生除0错误后,会收到8号信号,但是我们一直死循环重复捕捉,这是为什么呢?

  • 我们从硬件角度来分析,cpu内有一套寄存器,保存着进程的上下文数据,其中一些特殊的寄存器比如eip/pc记录着进程执行的代码情况。
  • 最重要的是有一个状态寄存器,他就是一个位图结构,用来记录进程的运行状态。每一个比特位代表不同的状态。
  • 当发生除0错误的时候,状态寄存器中溢出标记位由0变1,发生异常。
  • 而操作系统是硬件的管理者,它发现cpu中状态寄存器发生异常,就会给进程发送特定的信号,而进程本身无法处理这个异常,即使捕捉了信号, 操作系统就会不断的发信号。

2.野指针异常
code:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cstdio>
#include <signal.h>
using namespace std;void handler(int signum)
{cout << "get a sig : " << signum << endl;
}int main()
{//signal(SIGSEGV, handler);cout << "before" << endl;sleep(1);int *p = nullptr;*p = 30;cout << "after" << endl;return 0;
}

实验现象:
在这里插入图片描述
放开注释,实验现象:
在这里插入图片描述
可以看到,发生野指针的时候,也会不断收到信号。
我们再从底层的角度解释一下:

  • 发生野指针的本质是访问无效的空间,由进程地址空间可以知道,我们语言上访问的空间其实是虚拟地址,实际上我们要通过页表进行虚拟地址到物理地址的转化,而这一过程os系统不会参与,会有页表和mmu(memory manager unit)即内存管理单元(cpu中硬件),当转化失败的时候mmu会报错。
  • os系统是硬件的管理者,它识别到mmu的报错,而进程无法修正错误,即使捕捉了信号,就不断给进程发信号

总结一下:
当我们出现异常的时候,操作系统为啥选择给进程发信号,然后去执行特定的代码,而不是直接干掉进程呢?
其实向我们发信号是为了让进程知道进程出现了什么异常,而且让进程有一定的缓存时间,去处理日志信息等,如果操作系统直接把进程干掉了,就可能处理不了这些情况了。

补充:
上述过程一个进程导致cpu中的寄存器发生了异常,也就是硬件发生了异常,为啥其他进程还能正常运行呢?

  • cpu中的寄存器只有一套,存储的是进程的上下文信息,当发生进程切换的时候,进程会带走它的上下文数据,然后其他进程将它之前访问的上下文数据放进寄存器,上一个进程带来的寄存器异常已经被带走了,而新来的进程上下文数据并没有异常,所以不会影响其他进程的运行。
  • 所以信号的存在不是让我们解决问题,操作系统都解决不了,你让进程自己解决码?
  • 信号的存在是为了让我们知道程序异常的原因,并且给我们一定的缓冲时间,用来打印日志信息等等。

4.软件条件产生信号

比如我们写管道的时候,当读端关闭的时候,写端还有继续写入,此时操作系统就会向写端进程发送SIGPIPE信号,然后终止进程。
在这里插入图片描述
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
返回值代表上次设定闹钟的剩余时间,比如你定一个闹钟30分钟后响,20分钟后你就行了,此时你查看时间闹钟还有10分钟才响。

实验:如果我们自定义处理闹钟的动作,进行一些日志打印等操作,然后再在捕捉函数内定一个闹钟,不就做到了不断做日志打印的业务了嘛?
code:

#include <signal.h>
using namespace std;
void work()
{cout << "print a log..." << endl;
}
void handler(int signum)
{cout << "get a sig : " << signum << endl;work();alarm(5);
}
int main()
{signal(SIGALRM, handler);alarm(5);while(1){cout << "i am a process pid : " << getpid() << endl;sleep(1);}return 0;
}

实验现象:
在这里插入图片描述

返回值验证:可以看到上面实验现象每次闹钟剩余时间都是0,因为我们没有利用其他手段让闹钟提前苏醒,我们使用kill命令来验证
实验现象:
在这里插入图片描述
补充:

  • 操作系统内部有很多进程,每个进程都可以设置闹钟,那么操作系统内部就有很多闹钟了,操作系统就要管理它们,管理的本质是先描述再组织,用特定的结构体来描述闹钟,比如 struct alarm.
  • struct alarm中需要存储设置这个alarm的进程,以及什么时候响,一般用当前时间戳+设计多久响来表示
  • 那么操作系统用什么组织alarm呢?很明显可以用我们常见的数据结构小堆来管理,堆顶存最早响的闹钟,os只需要查看堆顶的时间,如果超时了,给struct alarm中存的进程pid发送信号,然后将堆顶的数据删除,继续查看堆顶,重复以上动作,直到堆顶没有超时。

拓展: 核心转储技术

在这里插入图片描述
之前讲进程退出状态的时候,正常退出时次第8为为退出码,低8位为0,
收到信号退出的时候,次第0位无意义,低7位表示退出信号,其中第8个比特位之前没有讲,其实是core dump(核心转储)标识。

什么是核心转储?
将进程异常退出时的状态保存下来形成一个core.pid文件,放在当前路径下(磁盘)可以配合gdb进行时候调试,一会做实验。

一般在项目上线后是关闭的,为什么?
一般服务即使出现异常挂掉了,会有一定的手段自动重启,如果一些程序员写的代码太水,一上线就挂掉,不断自动重启,不断形成core dump文件,把磁盘写满,挂的就是操作系统了,那问题就严重了。

并不是所以的信号都会使进程挂掉后形成核心转储,通过man 7 signal可以查看
在这里插入图片描述
其中term表示只终止进程,core表示终止进程并形成core dump核心转储文件。
ign表示忽略动作,stop表示停止,这俩现在不详细讲。

实验验证:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <signal.h>
using namespace std;int main()
{pid_t id = fork();if(id == 0){int cnt = 500;while(cnt){cout << "i am child process, pid: " << getpid() << "cnt: " << cnt-- << endl;sleep(1);}exit(0);}int status = 0;pid_t rid = waitpid(id, &status, 0);cout << " child quit info, rid: " << rid << " exit code: " << ((status >> 8) & 0xff) << " exit signal: " << (status & 0x7f) << " core dump: " << (status >> 7 & 1) << endl;return 0;
}

默认情况下core dump是关闭的,ulimit -a可以查看
在这里插入图片描述
此时实验现象:
在这里插入图片描述
在这里插入图片描述
可以看到此时无论发送term信号还是core信号都不会形成core dump。

当我们把核心转储打开
在这里插入图片描述
开始重做实验:
在这里插入图片描述
在这里插入图片描述
可以看到当我们使用core信号时,形成了核心转储文件,并且很大。

那么我们如何将他和gdb配合使用呢?
code:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <signal.h>
using namespace std;int main()
{int a = 10;a /= 0;cout << "a: " << a << endl;return 0;
}

在这里插入图片描述
当我们直接运行的时候,可以看到报错多了一段core dumped,并形成了core.pid文件
在这里插入图片描述
直接开始gdb
在这里插入图片描述
可以看到直接定位到了异常的位置

总结一下:

  1. 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?
    OS是进程的管理者

  2. 信号的处理是否是立即处理的?
    普通在合适的时候进行处理,但是对于实时信号来说,必须立马处理。

  3. 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?
    可以发现普通信号一共有31个,一个int有32个比特位,不觉得很巧嘛?每一个比特位代表着一个信号,0和1表示着是否收到该信号,其实信号就是用类似这样的结构保存的,也就是位图,但是可以发现只能存0和1那就只能表示是否收到该信号,而不能知道收到该信号的数量的,实际上内核也是这样的。
    而实时信号需要立即处理,所以发几个信号就要处理几个,所以就不能用位图结构来存储了,而是用一个队列(先到先处理)。

  4. 一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?
    必须知道。

  5. 如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?
    与其说os向进程发信号,不如说向进程的pcb发信号,信号在pcb中用位图方式存储,那么os向进程发信号就是修改进程pcb中的位图,将对应的信号比特位由0置1.

相关文章:

linux之进程信号(初识信号,信号的产生)

目录 引入一、初识信号(信号预备知识)1.生活中的信号2.Linux中的信号3.信号进程得出的初步结论 二、信号的产生1.通过终端输入产生信号拓展: 硬件中断2.调用系统函数向进程发信号3.硬件异常产生信号4.软件条件产生信号拓展: 核心转储技术总结一下&#xff1a; 引入 一、初识信…...

为深度学习创建PyTorch张量 - 最佳选项

为深度学习创建PyTorch张量 - 最佳选项 正如我们所看到的&#xff0c;PyTorch张量是torch.Tensor​ PyTorch类的实例。张量的抽象概念与PyTorch张量之间的区别在于&#xff0c;PyTorch张量为我们提供了一个可以在代码中操作的具体实现。 在上一篇文章中&#xff0c;我们看到了…...

MySQL 与 Redis 数据一致性 2

1. 强一致还是最终一致?2. 先写 MySQL 还是先写Redis?case 1 3. 缓存(Redis)更新还是清除?更新策略更新策略会有数据不一致问题?数据不一致的概率与影响如果使用监听binlog更新数据还会出现数据不一致问题?binlog的消费问题 使用消息队列行不行?其他方案总结: 数据不一致…...

Git | git reset命令详解

关注&#xff1a;CodingTechWork 引言 Git 是一款非常流行的分布式版本控制工具&#xff0c;它帮助开发者有效地管理代码历史&#xff0c;支持多种功能来帮助团队协作、追踪修改和维护代码质量。git reset是 Git 中最强大、最复杂的命令之一&#xff0c;它的主要作用是重置当前…...

Linux高并发服务器开发 第十四天(dup/duo2/fcntl 进程 pcb进程控制块 环境变量)

目录 1.dup 和 dup2 1.1dup 1.2dup2 2.fcntl 3.进程 3.1进程和程序 3.2并发 3.3cpu 3.5pcb进程控制块 3.6进程状态 4.环境变量 1.dup 和 dup2 1.1dup - 将 文件描述符 &#xff0c;复制产生“新文件描述符” 并返回。新、旧文件描述符&#xff0c;指向同一文件。 …...

[MySQL | 二、基本数据类型]

基本数据类型 一、数值类型举例表结构1. 整数类型zerofill属性 与 int(n) 中 n 的关系 2.bit类型3. 小数类型float类型decimal类型 二、字符串类型1. char2. varchar如何选择定长或变长字符串&#xff1f; 3. 日期时间类型(date datetime timestamp)4. enum枚举类型5. set多选类…...

第G1周:生成对抗网络(GAN)入门

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊]** 本人往期文章可查阅&#xff1a; 深度学习总结 基础任务 1.了解什么是生成对抗网络2.生成对抗网络结构是怎么样的3.学习本文代码&am…...

ROS2 准备工作(虚拟机安装,Ubuntu安装,ROS2系统安装)

准备工作 虚拟机安装 大家可以自行去安装VMware链接&#xff1a;https://pan.baidu.com/s/1KcN1I9FN--Sp1bUsjKqWVA?pwd6666 提取码&#xff1a;6666(提供者&#xff1a;零基础编程入门教程) 教程&#xff1a;【【2025最新版】VMware虚拟机安装教程&#xff0c;手把手教你免…...

FreeType 介绍及 C# 示例

FreeType 是一个开源的字体渲染引擎&#xff0c;用于将字体文件&#xff08;如 TrueType、OpenType、Type 1 等&#xff09;转换为位图或矢量图形。它广泛应用于操作系统、图形库、游戏引擎等领域&#xff0c;支持高质量的字体渲染和复杂的文本布局。 FreeType 的核心功能 字体…...

BertTokenizerFast 和 BertTokenizer 的区别

BertTokenizerFast 和 BertTokenizer 都是用于对文本进行标记化的工具&#xff0c;主要用于处理和输入文本数据以供 BERT 模型使用。它们都属于 HuggingFace 的 transformers 库。 主要区别 底层实现&#xff1a; BertTokenizer: 这是一个使用纯 Python 实现的标记器&#xff…...

OpenGL中Shader LOD失效

1&#xff09;OpenGL中Shader LOD失效 2&#xff09;DoTween的GC优化 3&#xff09;开发微信小程序游戏有没有类似Debug真机图形的方法 4&#xff09;射线和Mesh三角面碰撞检测的算法 这是第418篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题&#xff0c;涵盖了U…...

[操作系统] 深入理解约翰·冯·诺伊曼体系

约翰冯诺依曼&#xff08;John von Neumann&#xff0c;1903年12月28日—1957年2月8日&#xff09;&#xff0c;原名诺伊曼亚诺什拉约什&#xff08;Neumann Jnos Lajos&#xff09;&#xff0c;出生于匈牙利的美国籍犹太人数学家&#xff0c;20世纪最重要的数学家之一&#xf…...

计算机网络(五)运输层

5.1、运输层概述 概念 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。 当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时…...

网络分析仪测试S参数

S参数的测试 一&#xff1a;S参数的定义 S参数&#xff08;Scattering Parameters&#xff0c;散射参数&#xff09;是一个表征器件在射频信号激励下的电气行为的工具&#xff0c;它以输入信号、输出信号为元素的矩阵来表现DUT的“传输”和“散射”效应&#xff0c;输入、输出…...

什么是数据仓库?

什么是数据仓库&#xff1f; 数据仓库&#xff08;Data Warehouse&#xff0c;简称DW&#xff09;是一种面向分析和决策的数据存储系统&#xff0c;它将企业中分散的、异构的数据按照一定的主题和模型进行集成和存储&#xff0c;为数据分析、报表生成以及商业智能&#xff08;…...

.NET8.0多线程编码结合异步编码示例

1、创建一个.NET8.0控制台项目来演示多线程的应用 2、快速创建一个线程 3、多次运行程序&#xff0c;可以得到输出结果 这就是多线程的特点 - 当多个线程并行执行时&#xff0c;它们的具体执行顺序是不确定的&#xff0c;除非我们使用同步机制&#xff08;如 lock、信号量等&am…...

使用 Charles 调试 Flutter 应用中的 Dio 网络请求

为了成功使用 Charles 抓取并调试 Flutter 应用程序通过 Dio 发起的网络请求&#xff0c;需遵循特定配置步骤来确保应用程序能够识别 Charles 的 SSL 证书&#xff0c;并正确设置代理服务器。 配置 Charles 以支持 HTTPS 请求捕获 Charles 默认会拦截 HTTP 流量&#xff1b;…...

老centos7 升级docker.io为docker-ce 脚本

旧的centos7 之前安装的是docker.io 由于一些原因,像docker compose 等版本变化,以及docker.io源受限等,我们要更新到docker-ce 并使用国内阿里云的源怎么处理?下面直接上脚本,upgrade-docker.sh #!/bin/bashset -e# 创建临时目录 TEMP_DIR"./tmp" mkdir -p "…...

Go Ebiten小游戏开发:贪吃蛇

贪吃蛇是一款经典的小游戏&#xff0c;玩法简单却充满乐趣。本文将介绍如何使用 Go 语言和 Ebiten 游戏引擎开发一个简单的贪吃蛇游戏。通过这个项目&#xff0c;你可以学习到游戏开发的基本流程、Ebiten 的使用方法以及如何用 Go 实现游戏逻辑。 项目简介 贪吃蛇的核心玩法是…...

c语言----------内存管理

内存管理 目录 一。作用域1.1 局部变量1.2 静态(static)局部变量1.3 全局变量1.4 静态(static)全局变量1.5 extern全局变量声明1.6 全局函数和静态函数1.7 总结 二。内存布局2.1 内存分区2.2 存储类型总结2.3内存操作函数1) memset()2) memcpy()3) memmove()4) memcmp() 2.4 堆…...

在一个sql select中作多个sum并分组

有表如下&#xff1b; 单独的对某一个列作sum并分组&#xff0c;结果如下&#xff1b; 对于表的第7、8行&#xff0c;num1都有值&#xff0c;num2都是null&#xff0c;对num2列作sum、按id分组&#xff0c;结果在id为4的行会显示一个null&#xff1b; 同时对2个列作sum&#x…...

如何修复Android上未安装的应用程序

在Android设备上安装应用程序通常是一个简单的过程。然而&#xff0c;“ Android上未安装应用程序”是一种常见的智能手机错误消息&#xff0c;由于一个或多个原因而经常遇到。发现由于即将出现故障而无法充分利用手机&#xff0c;这当然会非常令人沮丧&#xff0c;但幸运的是&…...

#CSS混合模式:解决渐变背景下的文字可见性问题

在现代网页设计中&#xff0c;渐变背景的使用越来越普遍。然而&#xff0c;当我们在渐变背景上放置文字时&#xff0c;常常会遇到一个问题&#xff1a;文字在某些背景颜色下可能变得难以阅读。今天&#xff0c;我们将探讨一个优雅的解决方案&#xff1a;使用CSS混合模式。 问题…...

微信小程序原生与 H5 交互方式

在微信小程序中&#xff0c;原生与 H5 页面&#xff08;即 WebView 页面&#xff09;之间的交互通常有以下几种方式&#xff1a; 1. 使用 postMessage 进行通信 微信小程序的 WebView 页面和原生小程序页面可以通过 postMessage 来进行数据传递。 WebView 页面向原生小程序发…...

kotlin中的flow使用,Flow跟生命周期结合

kotlin的Flow可以连续异步发出多个数据。 1. 普通flow,冷流类似于一个函数&#xff0c;当开始收集时才开始运行 val coldStream flow {for (i in 1..5) {delay(100L)emit(i)}} val collect1 buildString {coldStream.collect { append(it).append(", ") } }.remo…...

讲一下ZooKeeper的持久化机制?

大家好&#xff0c;我是锋哥。今天分享关于【讲一下ZooKeeper的持久化机制&#xff1f;】面试题。希望对大家有帮助&#xff1b; 讲一下ZooKeeper的持久化机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 是一个开源的分布式协调服务&…...

mybatis里面实现动态升降序

问题 最近有一个需求&#xff0c;需要前端告诉后端按照某个字段进行排序。这里主要侧重mybatis的xml实现&#xff0c;其他Spring集成就忽略了。 mapper xml实现 <if test"sortField ! null and sortField ! ">ORDER BY<choose><when test"sor…...

探索网络安全:浅析文件上传漏洞

前言 在数字化时代&#xff0c;网络安全已成为我们每个人都需要关注的重要议题。无论是个人隐私保护&#xff0c;还是企业数据安全&#xff0c;网络威胁无处不在。了解网络安全的基本知识和防护措施&#xff0c;对我们每个人来说都至关重要。 网络安全 网络安全并非只是对网…...

【C++】B2112 石头剪子布

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述游戏规则&#xff1a;输入格式&#xff1a;输出格式&#xff1a;输入输出样例&#xff1a;解题分析与实现 &#x1f4af;我的做法实现逻辑优点与不足 &#x1f4af…...

java根据模板导出word,并在word中插入echarts相关统计图片以及表格

引入依赖创建word模板创建ftl模板文件保存的ftl可能会出现占位符分割的问题&#xff0c;需要处理将ftl文件中的图片的Base64删除&#xff0c;并使用占位符代替插入表格&#xff0c;并指定表格的位置在图片下方 Echarts转图片根据模板生成word文档DocUtil导出word文档 生成的wor…...

Linux网络知识——路由表

路由表 1 定义与作用 Linux路由表是一个内核数据结构&#xff0c;用于描述Linux主机与其他网络设备之间的路径&#xff0c;以及如何将数据包从源地址路由到目标地址。路由表的主要作用是指导数据包在网络中的传输路径&#xff0c;确保数据包能够准确、高效地到达目标地址。 …...

ImageSharp图形库学习

一、引言 在当今数字化时代&#xff0c;无论是 Web 应用、桌面程序&#xff0c;还是移动应用&#xff0c;图像处理都扮演着至关重要的角色。从电商平台展示商品图片&#xff0c;到社交媒体分享照片&#xff0c;再到各种软件的图标设计&#xff0c;图像处理无处不在。博主们在内…...

Android string.xml中特殊字符转义

项目中要在string.xml 中显示特殊符号 空格&#xff1a; &#xff08;普通的英文半角空格但不换行&#xff09; 窄空格&#xff1a;  &#xff08;中文全角空格 &#xff08;一个中文宽度&#xff09;&#xff09; &#xff08;半个中文宽度&#xff0c;但两个空格比一个中文…...

Rust 游戏开发框架指南

Rust 游戏开发框架指南 主流游戏引擎 1. Bevy 最受欢迎的 Rust 游戏引擎之一&#xff0c;基于 ECS&#xff08;实体组件系统&#xff09;架构。 特点&#xff1a; &#x1f680; 高性能 ECS 系统&#x1f4e6; 热重载支持&#x1f3a8; 现代渲染器&#x1f50a; 内置音频系…...

SpringBoot3+Vue3开发台球计时系统

项目介绍 台球计时系统可以帮助我们自动计算开台时间&#xff08;从开始到结束的时间段&#xff09;、自动计算开台费用、结账后生成订单记录进行留存、也可以导出订单记录。 主要功能包含&#xff1a;球桌管理、开台、结账、查看占用明细、查看球台订单、订单管理、查看订单…...

基于springboot的租房网站系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 项目包含&#xff1a; 完整源码数据库功能演示视频万字文档PPT 项目编码&#xff1…...

静态综合路由实验

实验拓扑 实验要求 1.除R5的环回地址外&#xff0c;整个其他所有网段基于192.168.1.0/24进行合理的IP地址划分 2.R1-R4每个路由器存在两个环回接口&#xff0c;用于模拟pc网段&#xff1b;地址也在192.168.1.0/24这个网络范围内 3.R1-R4上不能直接编写到达5.5.5.0/24的静态路由…...

国产编辑器EverEdit - 扩展脚本:新建同类型文件(避免编程学习者反复新建保存练习文件)

1 扩展脚本&#xff1a;在当前文件目录下新建同类型文件 1.1 应用场景 用户在进行编程语言学习时&#xff0c;比如&#xff1a;Python&#xff0c;经常做完一个小练习后&#xff0c;又需要新建一个文件&#xff0c;在新建文件的时候&#xff0c;不但要选择文件类型&#xff0c…...

Ubuntu Server 24.04 配置静态IP

Ubuntu Server 24.04 配置静态IP 提示&#xff1a;基于Ubuntu Server 24.04进行配置 文章目录 Ubuntu Server 24.04 配置静态IP一、查看网卡信息二、修改网卡信息三、使网卡配置生效四、测试 一、查看网卡信息 使用命令 ip a lo 为本地回环地址 ens33 真实网卡地址 shanfengubu…...

​​​​​​​​​​​​​​★3.3 事件处理

★3.3.1 ※MouseArea Item <-- MouseArea 属性 acceptedButtons : Qt::MouseButtons containsMouse : bool 【书】只读属性。表明当前鼠标光标是否在MouseArea上&#xff0c;默认只有鼠标的一个按钮处于按下状态时才可以被检测到。 containsPress : bool curs…...

linux系统监视(centos 7)

一.系统监视 1.安装iostat&#xff0c;sar&#xff0c;sysstat&#xff08;默认没有&#xff0c;安装过可以跳跃&#xff09; iostat 和 sar&#xff1a; 同样&#xff0c;iostat 和 sar 是 sysstat 软件包的一部分。使用以下命令安装&#xff1a;sudo yum install sysstat解释…...

Java面试总结(1)

问题1 自我介绍&#xff1a; 面试官您好&#xff0c;我叫xxx&#xff0c;是来自xxxx大学软件工程专业的一名应届生&#xff0c;我这次想应聘的是java开发实习生&#xff0c;在校期间&#xff0c;我热爱编程&#xff0c;能够使用java&#xff0c;C,python的编程语言&#xff0c…...

晨辉面试抽签和评分管理系统之六:面试答题倒计时

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…...

关于H5复制ios没有效果

问题场景&#xff1a;今天遇到这样一个问题&#xff0c;需要从后端接口获取到的值进行复制&#xff0c;且不能提现调用获取值&#xff0c;因为是一个数据列表&#xff0c;每个列表元素需要当场点击调用接口获取值进行复制&#xff0c;本来以为很简单的一个需求&#xff0c;当做…...

Windows 蓝牙驱动开发-安装蓝牙设备

蓝牙配置文件驱动程序有两种安装类型&#xff1a; 客户端安装&#xff0c;在此类安装中&#xff0c;远程设备播发其服务&#xff0c;并且计算机与之连接。 示例包括&#xff1a;鼠标、键盘和打印机&#xff1b;服务器端安装&#xff0c;在此类安装中&#xff0c;计算机播发服务…...

你喜欢用什么编辑器?

电脑工作者和程序员所使用的文本编辑器通常需要具备高效率、易用性以及对代码友好等特点&#xff0c;包括语法高亮、自动完成、多文件同时编辑、查找替换、版本控制集成等功能。以下是几个广受开发者欢迎且实用性较强的文本编辑器&#xff1a; Visual Studio Code&#xff08;V…...

32_Redis分片集群原理

1.Redis集群分片 1.1 Redis集群分片介绍 Redis集群没有使用一致性hash,而是引入了哈希槽的概念。Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。 用于将密钥映射到散列插槽的基本算法如下: HASH_SLOT = CRC16(key) mod 16384 集群的每…...

小米vela系统(基于开源nuttx内核)——openvela开源项目

前言 在 2024 年 12 月 27 日的小米「人车家全生态」合作伙伴大会上&#xff0c;小米宣布全面开源 Vela 操作系统。同时&#xff0c;OpenVela 项目正式上线 GitHub 和 Gitee&#xff0c;采用的是比较宽松的 Apache 2.0 协议&#xff0c;这意味着全球的开发者都可以参与到 Vela…...

【STM32-学习笔记-7-】USART串口通信

文章目录 USART串口通信Ⅰ、硬件电路Ⅱ、常见的电平标准Ⅲ、串口参数及时序Ⅳ、STM32的USART简介数据帧起始位侦测数据采样波特率发生器 Ⅴ、USART函数介绍Ⅵ、USART_InitTypeDef结构体参数1、USART_BaudRate2、USART_WordLength3、USART_StopBits4、USART_Parity5、USART_Mode…...

实现类似Excel的筛选

以下是在 DataGridView 中实现类似 Excel 下拉筛选功能的解决方案&#xff1a; 解决思路 为 DataGridView 的列添加 DataGridViewComboBoxColumn 类型的列&#xff0c;用于显示下拉筛选列表。为 DataGridView 的 ColumnHeaderMouseClick 事件添加处理程序&#xff0c;当用户点…...