【hello Linux】进程信号
目录
1. 进程信号的引出及整体概况
2. 信号的产生
1. 键盘产生
2. 进程异常
3. 系统调用
4. 软件条件
3. 信号的保存
1. 信号相关的常见概念
2. sigset_t
3. 信号集操作函数
4. sigprocmask:对block位图的操作
5. sigpending:对pending位图的操作
6. 捕捉信号
4. 信号的处理
Linux🌷
1. 进程信号的引出及整体概况
在生活中关于信号的场景有很多。比如:早上响起的闹钟、过马路时的红绿灯、烽火台的烽火、跑步时的信号枪......这些信号都是在生活中的,是给人看的;
虽然这些场景还未到来时,但我们立马便能想到我们接下来应该做什么,其本质也就是:对于信号的处理动作,远远早于信号的产生,这是我们在长期以来积累的经验,又或者是通过学习知道的;
在计算机中也存在着信号,这个信号是OS给进程发送的,目的是为了让进程在合适的时候执行对应的动作。进程也是在没有收到信号时就能够识别信号并知道如何处理它,这是曾经编写OS的工程师在写进程源代码的时候就设置好的;
总结一下:进程具有识别信号并处理信号的能力,这是远远早于信号的产生的。
在生活中,我们收到某种”信号“的时候,并不一定是立即处理的:因为信号随时可能产生,我们在此时可能有更重要的事要做。也就是说:信号的产生和信号的处理是异步的。进程收到某种信号的时候,并不是立即处理的,而是在进程收到信号之后,先将信号保存起来,以供在合适的时候再进行处理。信号本质上也是数据,信号在发送之后,由OS将信号数据写往进程的task_struct中。信号产生的方式不止一种,无论信号时如何产生的,本质在底层都是通过OS发送的!
接下来将从(信号的产生——>信号的保存——>信号的处理)三个方面书写本篇的博客;
请大家耐心看下去,定会收获不少;
开始正文了!!!🔮
2. 信号的产生
1. 键盘产生
我们写了一个每隔1秒钟往显示器输出 ”hello linux!" 的死循环代码;
程序运行起来后,往键盘中输入 ctrl c 发现程序终止,猜想是该进程收到什么信号的原因;
信号捕捉函数:
#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
我们可以在程序中,通过使用如上函数对信号进行捕捉;
其本质是修改进程对信号的默认处理动作;
signum:捕捉到的信号;
handler:对于信号的自定义处理方法;
对于信号捕捉函数,只有到收到信号时,才会执行 handler 方法;
#include <stdio.h>
#include <signal.h>
#include <unistd.h> //自定义处理方法
void handler(int signo)
{ printf("get a signal: signal no:%d\n",signo);
} int main()
{ //信号捕捉函数 int i; for(i=1;i<31;i++) { signal(i,handler); } while(1) { printf("hello linux! pid:%d\n",getpid()); sleep(1); } return 0;
}
我们使用如上代码对 1~31 号信号 进行捕捉,发现从键盘输入 ctrl c 后确实产生了信号,并且是2号信号,同时我们也发现 9 号信号是不可被捕捉的;
查看系统定义的信号列表
kill -l
这些信号其实都是利用宏定义出来的,我们既可以使用信号前面的数字,又可以使用信号宏;
编号 34 以上的是实时信号,在此只讨论编号 34 一下的信号;
这些信号各自在什么条件下产生,默认处理动作是什么,我们都可以使用如下命令进行查看:
man 7 signal
在此只截出文件中的一小部分;
注意:这些信号的默认处理动作都是终止进程,且只能用来终止前台进程;
使用 kill 命令发送信号:
kill [-信号名] [进程ID]
2. 进程异常
我们写了一个算术异常的代码,经过运行,发现程序崩溃,猜测是否是因为收到信号的原因;
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h> void handler(int signo)
{ printf("get a signal! signal no:%d pid:%d\n",signo,getpid() ); sleep(1);
} int main()
{ //信号捕捉 int i; for(i=1;i<31;i++) { signal(i,handler); } //算术异常 int a=10; a/=0; return 0;
}
上述代码对信号捕捉,发现确实是因为收到 8 号信号的原因;
这是因为CPU对上述代码中的异常算术进行了计算,导致CPU出现一些标志位的错误,OS作为硬件资源的管理者,要对硬件的 “健康” 负责,因此向该进程发送信号,达到终止进程的目的;
core dump:
在学习父进程 waitpid 等子进程时,有这样一张图;
我们学习了进程退出时的退出码((status>>8)&0xff),进程退出时的信号(status&0x7f);
一直没有说 core dump 标志位是干什么的;
core dump:在程序异常终止时,如果有必要OS会将 core dump 标志位设为1,并将进程在内存中的数据转储到磁盘中,方便我们后期调试;
Linux云服务器,core dump 技术默认是关闭的,可以使用 ulimit -a 查看;
设置 core dump 大小:ulimit -c 10240
设置 core dump 前后对比:
并且我们发现多了 core 文件,这个文件是二进制的,是OS把内存数据直接转载到磁盘中的,是可供我们调试的;
注意:我们要调试程序肯定是在 gdb 下调试的,那编译时便要在后面带 -g 选项;
我们来验证一下如果 core dump 了,那么 core dump 标志位会被置为1;
程序异常时:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main()
{if(fork()==0){printf("I am a child...\n");int a=10;a/=0; }int status=0;waitpid(-1,&status,0);printf("exit code:%d, exit signo:%d, core dump flag:%d\n",(status>>8)&0xff,status&0x7f,(status>>7)&0x1);return 0;
}
运行后发现 core dump 标志位果然被置为1了;
程序正常时:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main()
{if(fork()==0){printf("I am a child...\n");int a=10;a/=1; }int status=0;waitpid(-1,&status,0);printf("exit code:%d, exit signo:%d, core dump flag:%d\n",(status>>8)&0xff,status&0x7f,(status>>7)&0x1);return 0;
}
core dump 标志位为0;
注意:并不是所有的 singnal 都会产生 core dump(在此不进行验证了);
core 文件的使用:
3. 系统调用
kill:可以给指定的进程发送指定的信号;
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);//成功返回0,出错返回-1
raise:给当前进程发送指定信号(自己给自己发);
#include <signal.h>int raise(int sig);//成功返回0,出错返回-1
abort:使当前进程接收到信号而异常终止;
#include <stdlib.h>void abort(void);//abort函数总会成功,所以没有返回值
三个函数的使用:
注意:如下代码并不能直接运行 ,只是示例下三个函数的使用方法;
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h> static void Usage(const char * proc)
{ printf("Usage:\n\t %s signo who\n",proc);
}
int mainint(int argc,char *argv[])
{ //使用手册 if(argc!=3) { Usage(argv[0]); return 1; } int signo = atoi(argv[1]); int who = atoi(argv[2]); //kill kill(who,signo); //raise raise(signo); //abort abort(); printf("signo:%d, who:%d\n",signo,who); return 0;
}
4. 软件条件
软件条件:通过某种软件(OS),来触发信号的发送,系统层面设置定时器,或者某种操作而导致条件不就绪等这样的场景下,触发的信号发送;
在进程间通信时:当读端不光不读,而且还关闭了读fd,写端一直在写,最终写进程会受到sigpipe(13),就是一种典型的软件条件触发的信号发送;
今天主要介绍 alarm 函数和 SLGALRM 信号;
设置闹钟:
#include <unistd.h>unsigned int alarm(unsigned int seconds);
功能:设置一个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发 SIGALRM 信号,该信号的默认处理动作是终止当前进程;
参数:设置的秒数;
返回值:为0,或者是以前设定的闹钟时间还余下的秒数;
代码练习:
如下代码:设置了一个3秒的闹钟,对返回值进行输出 ,并且捕捉信号;
#include <stdio.h>
#include <signal.h>
#include <unistd.h> void handler(int signo)
{ printf("get a signal! signo:%d\n",signo);
}
int main()
{ //设置闹钟-3s int ret = alarm(3); //对信号进行自定义捕捉 int i; for(i=1;i<31;i++) { signal(i,handler); } while(1) { printf("hello linux! ret:%d\n",ret); sleep(1); } return 0;
}
运行代码之后发现:alarm函数的返回值为0, 在3秒后收到了一个 14 号信号;
查看发现:14号信号就是SIGALRM;
取消闹钟:
alarm(0);//返回值为上个闹钟剩余的秒数
代码:1s之后我们取消闹钟,并对返回值进行输出打印;
#include <stdio.h>
#include <signal.h>
#include <unistd.h> int main()
{ //设置闹钟 int ret = alarm(10); while(1) { sleep(1); //取消闹钟 int res = alarm(0); printf("返回值 ret:%d , 剩余值 res:%d\n", ret, res); } return 0;
}
接下来我们统计一下 1s CPU的运算次数;
#include <stdio.h>
#include <signal.h>
#include <unistd.h> int count=0; void handler(int signo)
{ printf("count:%d\n",count);
} int main()
{ //设置一个1s的闹钟 alarm(1); //对闹钟后的信号进行捕捉 signal(14,handler); while(1) { count++; } return 0;
}
我们将代码修改一下,边计算count的值,边进行输出:
#include <stdio.h>
#include <signal.h>
#include <unistd.h> int count=0; int main()
{ //设置一个1s的闹钟 alarm(1); //对闹钟后的信号进行捕捉 while(1) { count++; printf("count:%d\n",count); } return 0;
}
我们发现这样统计的CPU在1s内的运算次数,比上个代码慢4个0不止,根本原因就是该程序涉及到外设的输出,其大大影响了CPU的速度;
3. 信号的保存
产生的信号,并不一定是立即处理的,OS发送给进程的信号,进程可以先保存在task_struct中;
我们在此讨论的是1~31号信号,task_struct 是用一个 uint32_t sigs 的位图结构对信号进行保存;
比如:
00000000 00000000 00000000 00000000
比特位的位置:代表的就是哪一个信号;
比特位的内容:代表是否收到了信号;
信号在内核中的表示示意图:
对于信号的保存,并以是一张位图完成的,而是三张位图的共同的结果;
block表(信号屏蔽字):代表该信号是否被堵塞;
pending表:代表是否收到了该信号;
handler表:该表其实是一个函数指针数组,保存着对于信号处理方法函数的地址;
对于一个信号,只有block为0(未堵塞) pending为1(收到信号) 才会处理handler的方法,也就是对信号的处理;
对于信号的处理有三种方法:
1. 默认 SIG_DFL:其实是一个宏定义,表示((sighandler_t)0);
2. 忽略 SIG_IGN:其实是一个宏定义,表示((sighandler_t)1);
3. 自定义函数(signal自定义捕捉);
在此便对信号的发送有了深一步的理解:
产生信号后,OS给进程发送信号,其本质就是OS向指定进程的task_struct中pending位图写入比特位1,即完成信号的写入;
1. 信号相关的常见概念
- 实际执行信号的处理动作称为信号递达(Delivery)
- 信号从产生到递达之间的状态,称为信号未决(Pending)。
- 进程可以选择阻塞 (Block )某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
2. sigset_t
3. 信号集操作函数
#include <signal.h>int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
- 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
- 函数sigfifillset初始化set所指向的信号集,使其中所有信号的对应bit置1,表示该信号集的有效信号包括系统支持的所有信号。
- 注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfifillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
4. sigprocmask:对block位图的操作
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
//返回值:若成功则为0,若出错则为-1
//set:输入型,返回老的信号屏蔽字——block位图,如果不想要的话设为NULL
//oset:输出型,修改block
5. sigpending:对pending位图的操作
#include <signal.h>int sigpending(sigset_t *set);
//读取当前进程的未决信号集,通过set参数传出。
//调用成功则返回0,出错则返回-1。
//不对pending位图做修改,只是单纯的获取进程的pending位图,只能OS来修改
下面用刚学的几个函数做个实验:
代码1:
其功能是将2号和9号信号进行屏蔽;
#include <stdio.h>
#include <signal.h>
#include <unistd.h> int main()
{ sigset_t iset, oset; //清空老的和新的信号 sigemptyset(&iset); sigemptyset(&oset); //将2号信号添加 sigaddset(&iset,2); //将9号信号添加 sigaddset(&iset,9); //1.设置当前进程的屏蔽字 //2.获取当前进程老的屏蔽字 sigprocmask(SIG_SETMASK, &iset, &oset); while(1) { printf("I am a process! pid:%d\n",getpid()); sleep(1); } return 0;
}
我们向该进程发送2号信号发现没有反应,发送9号信号发现进程终止;
这也说明了9号信号不仅不可被自定义捕捉,也不可被屏蔽;
代码2:
将2号信号进行堵塞,并输出堵塞之前和堵塞之后的pending位图;
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void show_pending(sigset_t *set)
{printf("pid:%d curr process pending:",getpid());int i;for(i=1;i<=31;i++){if(sigismember(set,i)){printf("1");}else {printf("0");} }printf("\n");
}int main()
{sigset_t iset, oset;//清空老的和新的信号sigemptyset(&iset);sigemptyset(&oset);//将2号信号添加sigaddset(&iset,2);//1.设置当前进程的屏蔽字//2.获取当前进程老的屏蔽字sigprocmask(SIG_SETMASK, &iset, &oset);sigset_t pending;while(1){//清空pending sigemptyset(&pending);//pending输出型参数sigpending(&pending);show_pending(&pending);sleep(1);}return 0;
}
2号信号被堵塞,所以pending中的2号信号位置一直为1;
代码3:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void handler(int signo)
{printf("%d号信号被传递了,已经处理完成!\n",signo);
}
void show_pending(sigset_t *set)
{printf("pid:%d curr process pending:",getpid());int i;for(i=1;i<=31;i++){if(sigismember(set,i)){printf("1");}else{printf("0");}}printf("\n");
}int main()
{sigset_t iset, oset;//清空老的和新的信号sigemptyset(&iset);sigemptyset(&oset);//将2号信号添加sigaddset(&iset,2);//1.设置当前进程的屏蔽字//2.获取当前进程老的屏蔽字sigprocmask(SIG_SETMASK, &iset, &oset);sigset_t pending; int count=0;while(1){//清空pending sigemptyset(&pending);//pending输出型参数sigpending(&pending);show_pending(&pending);sleep(1);count++;if(count==10){sigprocmask(SIG_SETMASK, &oset, NULL);//2号信号的默认动作是终止进程,看不到现象printf("恢复2号信号,可以被传递了!\n");}//对2号信号进行捕捉signal(2,handler);}return 0;
}
6. 捕捉信号
signal函数可以完成信号的自定义捕捉,sigaction函数也可以
#include <signal.h>int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction:是一个结构体,在此我们使用它的 (*sa_handler)(int) 和 sa_mask ;
- signo:指定信号的编号;
- act:输入型参数,若act指针非空,则根据act修改该信号的处理动作;
- oact:输出型参数,若oact指针非 空,则通过oact传出该信号原来的处理动作,不想要的话置为NULL;
(*sa_handler)(int) 的使用:
代码: 完成对2号信号的自定义捕捉;
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h> void handler(int signo)
{ printf("get a signal! signo: %d\n",signo);
}
int main()
{ struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = handler; //本质是修改当前进程的handler函数指针数组特定内容 sigaction(2, &act, NULL); while(1) { printf("I am a process! pid:%d\n",getpid()); sleep(1); } return 0;
}
//忽略处理
act.sa_handler = SIG_IGN; //默认处理
act.sa_handler = SIG_DEL;
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h> void handler(int signo)
{ printf("get a signal! signo:%d\n",signo);
}
int main()
{ struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = handler; sigemptyset(&act.sa_mask); //将3号信号捎带屏蔽 sigaddset(&act.sa_mask, 3); sigaction(2, &act, NULL); while(1) { printf("I am a process! signo:%d\n",getpid()); sleep(1); } return 0;
}
4. 信号的处理
我们之前总说信号的产生和信号的处理并不是同步的,而是在信号产生后在合适的时机进行信号的处理;
这个合适的时机:进程从内核态切换回用户态的时候,进行信号检测与信号的处理;
先来科普一些知识:
OS也是软件,在开机的时候,便是将OS的代码和数据加载到内存中。每个进程有自己的一张页表,OS也有:系统级页表,这张页表是供所有进程共享的。进程的程序地址有3GB给用户使用,还有1GB是给OS使用的。当进程切换至内核态时,便能访问那1GB空间,每个进程都是如此,进程之间无论如何切换,我们能保证我们一定能够找到同一个OS,因为我们每个进程都有3~4的地址空间,使用同一张内核页表。
CPU中有CR3寄存器,当CR3寄存器为0时:代表OS,当CR3为1时:代表用户;
用户态使用的是用户级页表,只能访问用户数据和代码;
内核态使用的是内核级页表,只能访问内核级数据和代码;
所谓的系统调用:就是进程的身份转化成为内核,然后根据内核页表找到系统函数,执行函数;
在大部分情况下,实际上我们OS都是可以在进程的上下文中直接运行的,但为了保护OS的安全,只允许OS执行内核代码;
上述皆是较为感性的认识;
下面的是较为理性的认识:
信号的处理过程:
用一张图更好的记忆下:
坚持打卡!😀
相关文章:
【hello Linux】进程信号
目录 1. 进程信号的引出及整体概况 2. 信号的产生 1. 键盘产生 2. 进程异常 3. 系统调用 4. 软件条件 3. 信号的保存 1. 信号相关的常见概念 2. sigset_t 3. 信号集操作函数 4. sigprocmask:对block位图的操作 5. sigpending:对pending位图的操作 6. 捕捉…...
【SpringBoot】获取HttpServletRequest的三种方式
方法一: Controller中增加request参数 RestController public class DemoController { RequestMapping("/demo")public void demo(HttpServletRequest request) { System.out.println(request.getParameter("hello"));} }线程安全缺点: 每个方法都…...
k8s DCGM GPU采集指标项说明
dcgm-exporter 采集指标项 指标解释dcgm_fan_speed_percentGPU风扇转速占比(%)dcgm_sm_clockGPU sm 时钟(MHz)dcgm_memory_clockGPU 内存时钟(MHz)dcgm_gpu_tempGPU 运行的温度(℃)dcgm_power_usageGPU 的功率(w)dcgm_pcie_tx_throughputGPU PCIeTX 传输的字节总数 (kb)dcgm_pc…...
从线程安全到锁粒度,使用Redis分布式锁的注意事项
关于 Redis 的分布式锁 在分布式的场景下,多个服务器之间的资源竞争和访问频繁性,为了数据的安全和性能的优化,我们需要引入分布式锁的概念,这把锁可以加在上层业务需要的共享数据/资源上,能够同步协调多个服务器的访…...
CopyOnWriteArrayList 的底层原理与多线程注意事项
文章目录 CopyOnWriteArrayList 的底层原理与多线程注意事项1. CopyOnWriteArrayList 底层原理1.1 概念说明1.2 实现原理1.3 优点1.4 缺点 2. CopyOnWriteArrayList 多线程注意事项与实例2.1 注意事项2.2 示例2.2.1 示例代码 3. 总结 CopyOnWriteArrayList 的底层原理与多线程注…...
互斥锁深度理解与使用
大家好,我是易安! 我们知道一个或者多个操作在CPU执行的过程中不被中断的特性,称为“原子性”。理解这个特性有助于你分析并发编程Bug出现的原因,例如利用它可以分析出long型变量在32位机器上读写可能出现的诡异Bug,明明已经把变量…...
Elasticsearch --- 数据聚合、自动补全
一、数据聚合 聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析、运算。例如: 什么品牌的手机最受欢迎? 这些手机的平均价格、最高价格、最低价格? 这些手机每月的销售情况如何? 实现这…...
Haproxy搭建web群集
一.常见的web集群调度器 1、目前常见的web集群调度器分为软件和硬件 2、软件通常使用开源的LVS、Haproxy、Nginx LVS 性能最好,但搭建复杂。Nginx并发量,性能低于Haproxy 3、硬件一般使用比较多的是F5,也有很多人使用国内的一些产品&a…...
Packet Tracer - 配置和验证小型网络
Packet Tracer - 配置和验证小型网络 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 RTA G0/0 10.10.10.1 255.255.255.0 不适用 G0/1 10.10.20.1 255.255.255.0 不适用 SW1 VLAN1 10.10.10.2 255.255.255.0 10.10.10.1 SW2 VLAN1 10.10.20.2 255.25…...
Baumer工业相机堡盟工业相机如何通过BGAPI SDK获取相机设备的各种固件信息如DeviceID或者SerialNumber等(C++)
项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具…...
java 的参数传递
一、疑惑引入 首先,我们从一个例子来引出这个问题: public static void main(String[] args) throws IOException {List<String> mockList Lists.newArrayList("a", "b");System.out.println("1: " mockList);L…...
【面试长文】HashMap的数据结构和底层原理以及在JDK1.6、1.7和JDK8中的演变差异
文章目录 HashMap的数据结构和底层原理以及在JDK1.6、1.7和JDK8中的演变差异HashMap的数据结构和原理JDK1.6、1.7和1.8中的HashMap源码演变JDK1.6JDK1.7JDK1.8 总结自己实现一个简单的HashMapHashMap的时间复杂度分析HashMap的空间复杂度分析HashMap的应用场景HashMap的弊端及解…...
【25】linux进阶——网络文件系统NFS
大家好,这里是天亮之前ict,本人网络工程大三在读小学生,拥有锐捷的ie和红帽的ce认证。每天更新一个linux进阶的小知识,希望能提高自己的技术的同时,也可以帮助到大家 另外其它专栏请关注: 锐捷数通实验&…...
JAVA入坑之JAVADOC(Java API 文档生成器)与快速生成
目录 一、JAVADOC(Java API 文档生成器) 1.1概述 1.2Javadoc标签 1.3Javadoc命令 1.4用idea自带工具生成API帮助文档 二、IDEA如何生成get和set方法 三、常见快捷方式 3.1快速生成main函数 3.2快速生成println()语句 3.3快速生成for循环 3.4“…...
React | React组件化开发
✨ 个人主页:CoderHing 🖥️ React .js专栏:React .js React组件化开发 🙋♂️ 个人简介:一个不甘平庸的平凡人🍬 💫 系列专栏:吊打面试官系列 16天学会Vue 11天学会React Node…...
云计算的优势与未来发展趋势
一、前言二、云计算的基础概念2.1 云计算的定义2.2 云计算的发展历程2.3 云计算的基本架构2.4 云计算的主要服务模式 三、企业采用云计算的优势3.1 降低成本3.2 提高效率和灵活性3.3 提升信息系统的安全性和可靠性3.4 拥有更加丰富的应用和服务 四、行业应用案例4.1 金融行业4.…...
shell编程lesson01
命令行和脚本关系 命令行:单一shell命令,命令行中编写与执行; 脚本:众多shell命令组合成一个完成特定功能的程序,在脚本文件中进行编写维护。 脚本是一个文件,一个包含有一组命令的文件。 编写一个shel…...
看看人家的MyBatis批量插入数据优化,从120s到2.5s,那叫一个优雅!
粗略的实验 最后 最近在压测一批接口的时候,我发现接口处理速度比我们预期的要慢。这让我感到有点奇怪,因为我们之前已经对这些接口进行了优化。但是,当我们进行排查时,发现问题出在数据库批量保存这块。 我们的项目使用了 myb…...
软件和信息服务业专题讲座
软件和信息服务业专题讲座 单选题(共 10 题,每题 3 分) 1、根据本讲,我国要加强物联网应用领域()开发和应用。 A、大数据 2、根据本讲,要充分发挥软件对城市管理和惠民服务的(&am…...
由 ChatGPT 团队开发,堪称辅助神器!IntelliJ IDEA 神级插件
什么是Bito? 为什么要使用Bito? 如何安装Bito插件 如何使用Bito插件 什么是Bito? Bito是一款由ChatGPT团队开发的IntelliJ IDEA编辑器插件,旨在提高开发人员的工作效率。此插件强大之处在于它不仅可以帮助开发人员更快地提交…...
spass modeler
课时1:SPSS Modeler 简介 本课时一共分为五个模块,分别是Modeler概述、工具安装、窗口说明以及功能介绍和应用案例。相信通过本课时内容的学习,大家将会对SPSS Modeler有个基础的了解. 在学习本节课内容之前,先来看看本节课我们究…...
kafka的push、pull分别有什么优缺点
文章目录 kafka的push、pull分别有什么优缺点Push 模式优点缺点 Pull 模式优点缺点 实践操作 kafka的push、pull分别有什么优缺点 Kafka 是由 Apache 软件基金会开发的一个开源流处理平台,广泛应用于各大互联网公司的消息系统中。在 Kafka 中,生产者使用…...
【Canvas入门】从零开始在Canvas上绘制简单的动画
这篇文章是观看HTML5 Canvas Tutorials for Beginners教程做的记录,所以代码和最后的效果比较相似,教程的内容主要关于这四个部分: 创建并设置尺寸添加元素让元素动起来与元素交互 设置Canvas的大小 获取到canvas并设置尺寸为当前窗口的大…...
【技术整合】各技术解决方案与对应解决的问题
文章目录 基本实现性能安全 本文将框架分为三大类: 基本实现:包括某个供能或者提供web、移动端、桌面端、或者上述端上的某种功能性能:提升高可用、高并发的框架安全:包括网络安全、权限与容灾等 基本实现 .NET CORE、.NET web基…...
公网远程访问公司内网象过河ERP系统「内网穿透」
文章目录 概述1.查看象过河服务端端口2.内网穿透3. 异地公网连接4. 固定公网地址4.1 保留一个固定TCP地址4.2 配置固定TCP地址 5. 使用固定地址连接 概述 ERP系统对于企业来说重要性不言而喻,不管是财务、生产、销售还是采购,都需要用到ERP系统来协助。…...
Win11的两个实用技巧系列之修改c盘大小方法、功能快捷键大全
Win11 c盘无法更改大小什么原因?Win11修改c盘大小方法 有不少朋友反应Win11 c盘无法更改大小是怎么回事?本文就为大家带来了详细的更改方法,需要的朋友一起看看吧 Win11 c卷无法更改大小什么原因?有用户电脑的系统盘空间太小了,…...
离散数学下--- 代数系统
代数系统 定义: 代数系统是用代数运算构造数学模型的方法。 • 通过构造手段生成,所以也称代数结构 • 代数运算:在集合上建立满足一定规则的运算系统 (一)二元运算 二元运算的定义 二元运算需要满足的两个条件&a…...
java基础入门-04
Java基础入门-04 11、集合&学生管理系统11.1.ArrayList集合和数组的优势对比:11.1.1 ArrayList类概述11.1.2 ArrayList类常用方法11.1.2.1 构造方法11.1.2.2 成员方法11.1.2.3 示例代码 11.1.3 ArrayList存储字符串并遍历11.1.3.1 案例需求11.1.3.2 代码实现 11…...
《面试1v1》java反射
我是 javapub,一名 Markdown 程序员从👨💻,八股文种子选手。 面试官: 你好,请问你对 Java 反射有了解吗? 候选人: 是的,我了解一些。 面试官: 那你能简单…...
【C语言】struct结构体
文章目录 一. 结构体简述二. 结构体的声明和定义1、简单地声明一个结构体和定义结构体变量2、声明结构体的同时也定义结构体变量3、匿名结构体4、配合typedef,声明结构体的同时为结构体取别名5、在声明匿名结构体时,使用typedef给这个匿名结构体取别名 三…...
怎样创建网站赚钱/线下广告投放渠道都有哪些
1. softmax 函数求导 求导之前我们先了解softmax 函数,softmax一般是用来作为网络的输出层,直接输出概率信息,定义如下: 那么我们对softmax 函数 进行求导,为了简洁把求和里面的一大堆用 简写: ①当 i j …...
zblog好还是wordpress/品牌营销策划有限公司
最后一步MAPPING 是指向本地代码的存放路径...
客厅装修风格/乐陵seo优化
小试牛刀 WiFi 远控 wendu 废话 少说 直接上代码 DH11三根线 信号线 接2 WiFi 模块 r-------t t--------r en&vcc------3.3v剩下的 共地的啦 double Fahrenheit(double celsius) { return 1.8 * celsius 32; } //摄氏温度度转化为华氏温度 double Kelvin(doubl…...
克隆网站首页做单页站几个文件夹/韩国今日特大新闻
目录 文档用途 详细信息 文档用途 用于排查应用连接不上数据库的错误原因 详细信息 检查网络,使用ping命令检查应用服务器与数据库服务器之间是否联通。写法:ping加ip地址 ping 192.192.192.192 检查端口,使用telnet命令检查端口是…...
西安大雁塔景点介绍/今日头条搜索优化
关闭页面的js方法 /** 关闭页面 */function closeWin() {if (navigator.userAgent.indexOf("Firefox") ! -1 || navigator.userAgent.indexOf("Chrome") ! -1) {//火狐或者谷歌浏览器 并非完全关闭 而是当前页面为“about:blank”window.location.href &q…...
网站搭建 保定/搜索百度网址网页
作说明及PC端控制软件https://share.weiyun.com/5HFmWGl或https://pan.baidu.com/s/1SXz5BsYJiAF-eUpFG_2l-g 提取码: kixh或https://drive.google.com/open?id1-JViWLBOIzaHTdwdONX2RP8S4EgWxoNDDIY的矢量网络分析仪,参考了日本edy555的相关设计,修改了部分电路&a…...