[Linux]进程信号
[Linux]进程信号
文章目录
- [Linux]进程信号
- 进程信号的定义
- 信号的特点
- 信号的生命过程
- 发送信号的原理
- 进程处理信号的方式分类
- 使用指令查看Linux系统定义的信号
- 信号产生
- 使用终端按键产生信号
- 使用指令向进程发送信号
- 调用系统调用向进程发送信号
- 由软件条件产生信号
- 硬件异常产生信号
- 核心转储
- 进程信号和核心转储的关系
- 使用指令操作系统核心转储信息
- 核心转储文件的使用
- 核心转储和退出信号的关系
- 阻塞信号
- 信号在内核的表示(信号保存)
- 信号集操作
- 信号集数据类型
- 信号集操作函数
- sigprocmask函数
- sigpending函数
- 捕捉信号(信号处理)
- 用户态和内核态
- 信号处理的时机
- 信号处理的过程
- signal函数
- sigaction函数
- volatile关键字
- SIGCHLD信号
进程信号的定义
进程之间事件异步通知的一种方式。它是一种软件中断,用于向进程发送通知和指令,以便对其进行控制或传递信息。进程信号由整数值来标识,每个值对应一个特定的信号。不同的信号对应不同的状况。
信号的特点
- 信号产生前,进程就知道如何处理 。
- 信号一旦产生,进程能够识别信号。
- 进程接收到信号后,不一定会立即处理,进程在收到信号后会先记录下来。
- 信号的产生对于进程是异步的。
信号的生命过程
信号的生命过程分为三个阶段:
- 信号产生
- 信号保存
- 信号处理

发送信号的原理
进程信号有许多个,操作系统为了管理进程接收的信号,需要使用一定的结构描述信号,操作系统采用了位图结构来记录不同的信号,该位图结构记录在进程的pcb中,当操作系统向进程发送信号时,就向进程pcb中描述信号的位图结构写入数据。
进程处理信号的方式分类
- 忽略此信号。
- 执行该信号的默认处理动作。
- 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉 (Catch)一个信号。
使用指令查看Linux系统定义的信号
使用kill-l可以查看Linux系统定义的信号列表:

使用man 7 signal可以查看Linux系统定义的信号详细说明:

信号产生
使用终端按键产生信号
在Linux系统中输入crtl + c可以给进程发送2号信号,2号信号默认的处理动作是终止进程。给进程发送2号信号的示例如下:

使用指令向进程发送信号
Linux系统中提供了kill -信号数 进程pid指令用于向指定进程发送信号:

调用系统调用向进程发送信号
kill接口
Linux系统中提供了kill系统调用用于向指定进程发送信号:
//kill所在的头文件和声明
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);
- pid参数 – 要发送信号的进程编号。
- sig参数 – 要给进程发送的信号。
- 成功返回0,失败返回-1,错误码被设置。
raise接口
Linux系统中提供了raise系统调用用于向进程自身发送信号:
//raise所在的头文件和声明
#include <signal.h>int raise(int sig);
- sig参数 – 向进程发送的信号。
- 成功返回0,失败返回失败原因对应的非0值。
abort接口
Linux系统下C语言库中提供了abort库函数用于向进程自身发送信号 SIGABRT:
//abort所在的头文件和声明
#include <stdlib.h>void abort(void);
abort是C语言库中提供的接口。abort向调用进程自身发送6号信号SIGABRT。
由软件条件产生信号
Linux系统了调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
//alarm所在的头文件和声明
#include <unistd.h>unsigned int alarm(unsigned int seconds);
- 调用
alarm函数操作系统会创建闹钟并建立对应数据结构组织起来。
硬件异常产生信号
硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除 以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
核心转储
Linux操作系统可以在进程异常时,将核心代码部分和相关的内存数据全部导出到磁盘外设中,这一功能被称作核心转储。
进程信号和核心转储的关系

不同的进程异常情况,进程会收到不同的进程信号,使用man 7 signal指令查看进程信号的信息,其中Action列中为Core的信号发送给进程后,进程就会进行核心转储。
使用指令操作系统核心转储信息
查看核心转储信息
使用ulimit -a指令可以查看到系统核心转储文件的信息:

云服务中默认设定核心转储文件的大小上限为0,也就是不进行核心转储。
修改核心转储信息
使用ulimit -c指令可以修改核心转储文件的大小上限:

核心转储文件的使用
核心转储文件的主要作用是当程序发生崩溃或异常终止时,通过分析核心转储文件,可以了解程序崩溃的原因、定位错误的位置以及查找潜在的缺陷。
运行一个进程用于,然后给它发送8号信号,让他产生核心转储文件:

使用gdb调试可执行程序,然后使用core-file指令打开核心转储文件,就能从调试中看到进程异常原因:

核心转储和退出信号的关系
如果某一进程产生异常生成了核心转储文件,Linux系统下进程使用系统接口等待回收该进程,获得的退出信息中倒数第八位会被置为1:

也就是core dump标志被置为1。
阻塞信号
- 实际执行信号的处理动作称为**信号递达(**Delivery) 。
- 信号从产生到递达之间的状态,称为信号未决(Pending)。
- 进程可以选择阻塞 (Block )某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
- 注意,**阻塞和忽略是不同的,**只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号在内核的表示(信号保存)
信号在内核中的表示示意图如下:

操作系统在内核数据结构task_struct中为信号维护了三张表:
- block: 位图结构,比特位的位置,表示一种信号,比特位的内容,对应的信号是否被阻塞。
- pending: 位图结构,比特位的位置,表示一种信号,比特位的内容,对应的信号是否接收到了。
- handler: 函数指针数组,数组的下标,表示信号的编号,数组的特定下标的内容,表示信号的递达动作。(用户自定义递达动作就是通过修改该数组实现的)
信号集操作
信号集数据类型
-
每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。 因此,未决和阻塞标志可以用相同的数据类型
sigset_t来存储,sigset_t称为信号集。 -
sigset_t类型可以表示每个信号 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。 -
sigeset_t类型的底层实现也是一种位图结构。
信号集操作函数
sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,这个类型内部如何存储这些bit则依赖于系统实现,用户不需要关心具体实现,只要能够按需求操作信号集即可,因此Linux系统提供了如下函数来操作信号集:
//信号集操作函数所在的头文件及函数声明
#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清零,表示该信号集不包含任何有效信号。 -
函数
sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。 -
注意,在使用
sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。 -
初始化
sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。 -
前四个函数都是成功返回0,出错返回-1。
-
sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。
sigprocmask函数
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集–block表)。
//sigprocmask所在的头文件及函数声明
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
- 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
- 如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。
- 如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。
- 返回值:若成功则为0,若出错则为-1
假设当前的信号屏蔽字为mask,下表说明了how参数的可选值:
| SIG_BLOCK | set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set |
|---|---|
| SIG_UNBLOCK | set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set |
| SIG_SETMASK | 设置当前信号屏蔽字为set所指向的值,相当于mask=set |
sigpending函数
调用函数sigpending可以读取当前进程的未决信号集,通过set参数传出:
//sigpending所在的头文件及函数声明
#include <signal.h>int sigpending(sigset_t *set);
- 调用成功则返回0,出错则返回-1。
捕捉信号(信号处理)
用户态和内核态
Linux系统中进程地址空间分为两个部分,一个部分是用户空间,另一个部分是内核空间,两个部分都有自身对应的页表,一个是用户级页表,另一个是内核级页表,操作系统计算机开机时会将自身的代码和数据加载到内存中,这部分代码和数据通过同一张内核级页表映射到每个进程的地址空间中的内核部分,示意图如下:
由于操作系统的代码和数据通过同一张内核级页表映射到每个进程的地址空间中的内核部分,因此在进程运行时,只需要像跳转到动态库一样,跳转到操作系统的代码和数据,使得进程代码和操作系统代码的切换可以在一张进程地址空间中跳转完成,但操作系统为了不让用户通过进程地址空间中的内核部分的随意访问操作系统的代码和数据,操作系统设置了两种状态:**用户态和内核态,当处于用户态时,进程只能访问用户进程的代码和数据,当处于内核态时,进程只能访问操作系统的代码和数据。**为了记录当前处于用户态还是内核态,将标志信息记录在CPU的寄存器中。
小总结一下:
- 所有进程地址空间的用户空间中的内容是不一样的,每一个进程都有自己的用户级页表,看到属于自己的代码和数据。
- 所有进程地址空间的内核空间中的内容是一样的,每一个进程都会看到同一个内核级页表,看到同一个操作系统。
- 操作系统实际是通过进程的地址空间完成运行。
- 使用系统调用的本质就是跳转到进程地址空间的内核空间部分运行。
- 为了保护操作系统中的代码和数据,操作系统设置了用户态和内核态。
信号处理的时机
当进程从内核态切换回用户态的时候(信号记录在内核中,只能在内核态访问),进程会在操作系统的指导下,进程信号的检测和处理。
用户态切换至内核态的情况如下:
- 发生系统调用:当用户程序需要访问受保护的系统资源或请求操作系统提供的服务时,它会发起系统调用。操作系统会接收到系统调用请求,然后执行相应的系统代码来处理该请求,并返回结果给用户程序。
- 异常或中断事件:当发生硬件故障、软件错误或外部中断等事件时,操作系统需要对其进行处理。操作系统会通过中断处理程序或异常处理程序来响应这些事件,并执行必要的系统代码来处理它们。
- 定时器事件:操作系统通常会使用定时器来进行时间管理和调度。当定时器触发时,操作系统会响应该事件,执行系统代码以更新任务调度和执行状态。
补充说明: 计算机存在一个计时器硬件,当计时器记录进程运行到一定时间后,操作系统会执行对应的中断方法,检查时间片,如果时间片到了就会调用操作系统中的调度函数,完成进程的切换。
信号处理的过程
在进程运行执行对应代码时,遇到需要切换至内核态的情况,产生中断跳转至内核态,进入内核态后完成对应中断处理后,开始检测信号,进行,执行信号对应的处理动作,若是默认处理或者忽略处理后就会直接进入用户态跳转回用户态中断前代码,如果是自定义处理动作,会进入用户态跳转执行对应的自定义处理动作,然后调用sigreturn回到内核态,再调用sys_sigreturn进入用户天跳转回用户态中断前代码。执行自定义处理动作的过程示意图如下:

- 在检测信号后,会将要处理的信号在block表对应位置置为1,将pending表中对应位置置为0,然后再执行处理动作。
signal函数
调用函数signal可以将对应信号的处理动作改为自定义处理动作:
//signal所在的头文件及函数声明
#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
- signum参数 – 要修改的信号。
- handler参数 – 自定义处理函数的函数指针。传入
SIG_DFL为默认处理动作,SIG_IGN为忽略动作。 - 返回值是一个函数指针,如果成功设置信号处理程序,则返回先前的信号处理程序的函数指针;如果出现错误,则返回
SIG_ERR。 SIGKILL和SIGSTOP信号不能被修改为自定义处理动作。
编写如下代码进行测试:
#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int signo)
{cout << "get the signal: " << signo << endl;
}int main()
{signal(SIGINT, handler);while(true){cout << "i am running, pid: " << getpid() << endl;sleep(1);}return 0;
}
编译代码运行查看结果:

sigaction函数
sigaction函数可以读取和修改与指定信号相关联的处理动作。
//sigaction所在的头文件及函数声明
#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- signo参数 – 指定信号的编号。
- 若act指针非空,则根据act修改该信号的处理动作。
- 若oact指针非空,则通过oact传出该信号原来的处理动作。
- 调用成功则返回0,出错则返回- 1。
sigaction函数需要使用struct sigaction数据类型作为参数,其结构定义如下:
struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);
};
其主要字段如下:
- sa_handler: 赋值为一个函数指针表示用自定义函数捕捉信号
- sa_mask: 在进行信号处理时需要额外屏蔽的信号
- sa_flags: 默认为0即可。
sigaction函数使用结构体作为参数修改信号处理动作,相比signal功能更为强大。
volatile关键字
在C语言中,volatile 是一个关键字,用于告诉编译器某个变量是易变的(volatile)并且不应该进行优化。
为了体会volatile关键字的作用,创建如下文件及文件内容:
makefile:
mysignal:mysignal.ccg++ -o $@ $^ -std=c++11 -O2//注意此处采用O2优化级别.PHONY:clean
clean:rm -rf mysignal
mysignal.cc:
#include <iostream>
#include <signal.h>using namespace std;int quit = 0;void handler(int signo)
{cout << "get the signal: " << signo << endl;quit = 1;cout << "quit: " << quit << endl;
}int main()
{signal(2, handler);while(!quit);cout << "process quit" << endl;return 0;
}
编译代码并查看运行结果:

从现象中可以看出,在键盘中输入ctrl+c传入二号信号后,执行了自定义的处理动作quit改为了1,但是循环依旧继续,程序并未终止。这是因为编译器的优化,在无优化的情况下,从汇编来看应该是在每次循环时将quit数据从内存加载到CPU寄存器中进行判断,但是由于main函数中没有quit修改的代码,编译器以为quit不会修改,直接将汇编优化成了只从内存中加载一次quit,然后只进行判断,信号处理后修改的是内存的数据,因此不会使得循环停止。
使用volatile关键字修改代码:
#include <iostream>
#include <signal.h>using namespace std;volatile int quit = 0;void handler(int signo)
{cout << "get the signal: " << signo << endl;quit = 1;cout << "quit: " << quit << endl;
}int main()
{signal(2, handler);while(!quit);cout << "process quit" << endl;return 0;
}
编译代码并查看运行结果:

volatile关键字让quit不再优化,每次判断都从内存加载quit到CPU中,因此现象如上图。
SIGCHLD信号
子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号 的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可,具体自定义代码如下:
void handler(int signo)
{while(true){pid_t id = waitpid(-1, nullptr, WNOHANG);if (id > 0) printf("waitpid: %d, my pid: %d\n", id, getpid());else break;}
}
说明:
waitpid传入-1让其接收任意一个僵尸状态的子进程。- 循环调用
waitpid是为了解决如果一个进程回收时,其他进程进入僵尸状态。 - 采用
WNOHANG非阻塞是为了解决暂时没有子进程需要回收,进入阻塞状态。
除了以上自定义处理外,Linux系统还提供了另一种方法解决僵尸状态的子进程:父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。(此方法对于Linux可用,但不保证在其它UNIX系统上都可用。)
相关文章:
[Linux]进程信号
[Linux]进程信号 文章目录 [Linux]进程信号进程信号的定义信号的特点信号的生命过程发送信号的原理进程处理信号的方式分类使用指令查看Linux系统定义的信号信号产生使用终端按键产生信号使用指令向进程发送信号调用系统调用向进程发送信号由软件条件产生信号硬件异常产生信号 …...
PostgreSQL 数据类型
文章目录 PostgreSQL数据类型说明PostgreSQL数据类型使用单引号和双引号数据类型转换布尔类型数值类型整型浮点型序列数值的常见操作 字符串类型日期类型枚举类型IP类型JSON&JSONB类型复合类型数组类型 PostgreSQL数据类型说明 PGSQL支持的类型特别丰富,大多数…...
智慧港口4G+UWB+GPS/北斗RTK人员定位系统解决方案
港口人员定位系统能够帮助企业实现对港口作业人员的全面监控和管理,不仅可以保障人员的人身安全,还可以提高人员的作业效率,为港口的可持续发展提供有力保障。接下来为大家分享智慧港口人员定位系统解决方案。 方案背景 1、港口作业人员多&a…...
实时时钟和日历电路芯片MS85163/MS85163M
MS85163/MS85163M 是一款 CMOS 实时时钟 (RTC) 和 日历电路,针对低功耗进行了优化,内置了可编程的时钟输出、中断输出和低电压检测器。所有寄存器地址和数据都通过两线双向I 2 C 总线进行串行传输,最大总线传输速度为 400kbit/s 。采用SOP8…...
【Java从入门到精通】这也许就是Java火热的原因吧!
前言:Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类:JavaSE(Standard Edition,标准版):支持面向桌面、嵌入式和移动设备的应用程序开发;JavaEE&…...
zTasker—简洁易用强大的定时热键一体自动化工具,效率倍增器
软件名称 zTasker 应用平台 PC Windows7及以上 一句简介 市面上定时类软件很多,但无一例外功能都很单一,要完成不同的任务,需要不同的软件 市面上的热键软件,要么功能少,要么像是AutoHotKey这样对于一般用户太专业…...
惊艳时装界!AIGC风暴来袭,从设计到生产的全新体验
时尚是一个不断演进的领域,充满创新和独创性,但现在,创新迈入了一个崭新的境界——人工智能生成内容(AIGC)。这个革命性的技术,改变了时装设计的游戏规则。在过去的几年里,人工智能已经深刻地改…...
element -ui table表格内容无限滚动 使用插件vue-seamless-scroll
使用插件 一、安装组件依赖 npm install vue-seamless-scroll 二、引入组件 import vueSeamlessScroll from "vue-seamless-scroll"; components: { vueSeamlessScroll }, <div class"table-list "><vue-seamless-scroll :class-option"…...
如何在windows环境下编译T
一, 安装MYSYS2 1. 去https://www.msys2.org下载 msys2-x86_64-xxxxx.exe; 2. 按照msys2.org主页提示的步骤安装; 3.安装完默认起来的是 UCRT的, 可以根据环境的需要选择, 我选择的 MSYS2 MINGW64 4. 搭建编译环境, 安装对应的软…...
USB接口针式打印机
1 针式打印机原理 - 针式打印机16针是纵向排列,每次打印垂直的16bit,然后右移一bit,继续下列打印;字节的MSB表示最上面的点,字节LSB表示最下面的点 - 由于汉字字模的点阵是横向排列的,而提供给打印头的信息…...
外贸建站教程步骤有哪些?独立站怎么搭建?
推荐的外贸建站教程?制作国际贸易网站的流程? 对于那些希望将产品或服务推向全球市场的企业来说,建立一个专业、具有吸引力的网站是至关重要的。下面115SHOP将介绍外贸建站教程的关键步骤,帮助您更好地了解如何在国际市场上建立您…...
useGetState自定义hooks解决useState 异步回调获取不到最新值
setState 的两种传参方式 1、直接传入新值 setState(options); const [state, setState] useState(0); setState(state 1); 2、传入回调函数 setState(callBack); const [state, setState] useState(0); setState((prevState) > prevState 1); // prevState 是改变之…...
input子系统框架、外设驱动开发
一、input子系统基本框架 Linux内核为了两个目的: 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发统一输入类外设产生的数据格式(struct input_event),更加方…...
Google Chrome 浏览器以全屏模式打开
目录 前言以全屏模式打开禁止弹出无法更新的提示窗禁止翻译网页Chrome设置禁止翻译网页可能1可能2可能3 网页添加指令禁止Chrome翻译网页 禁用脚本气泡浏览器解决办法html解决办法方法1:鼠标滑过超链接时,使状态栏不出现超链接方法2:方法3&am…...
安装torch113、cuda116并运行demo【Transformer】
文章目录 01. 导读02. 显卡驱动版本03. 创建环境、下载安装必要包04. 运行参考代码: 01. 导读 安装torch113、cuda116并运行demo【Transformer】 02. 显卡驱动版本 C:\Users\Administrator>nvidia-smi -l 10 Wed Sep 13 23:35:08 2023 ----------------------…...
基于scRNA-seq的GRN分析三阴性乳腺癌的肿瘤异质性
三阴性乳腺癌即TNBC是一种肿瘤异质性高的乳腺癌亚型。最近的研究表明,TNBC患者可能包含具有不同分子亚型的细胞。此外,基于scRNA-seq数据构建的GRN已经证明了对关键调控因子研究的重要性。作者使用scRNA-seq对TNBC患者的GRN进行了全面分析。从scRNA-seq数…...
Python:二进制文件实现等间隔取相同数据量并合并
举例:每3byte为一页,每3页为一wl。将所有wl的第一页/第二页/第三页分别合并为一个文件。 data b\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03\x04\x05\x06\x07\x08\x0…...
python使用openvc库进行图像数据增强
以下是使用Python和OpenCV库实现图像数据增强的简单示例代码,其中包括常用的数据增强操作: import cv2 import numpy as np import os# 水平翻转 def horizontal_flip(image):return cv2.flip(image, 1)# 垂直翻转 def vertical_flip(image):return cv2…...
如何利用Api接口获取手机当前的网络位置信息
在移动互联网时代,手机定位已经成为了一个日常化的需求,无论是导航、社交还是打车等服务都需要获取手机的位置信息。而获取手机位置信息最基础的一步就是获取手机当前的网络位置信息,本文将介绍如何利用API接口获取手机当前的网络位置信息。 …...
vue-elementPlus自动按需导入和主题定制
elementPlus自动按需导入 装包 -> 配置 1. 装包(主包和两个插件包) $ npm install element-plus --save npm install -D unplugin-vue-components unplugin-auto-import 2. 配置 在vite.config.js文件中配置,配置完重启(n…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
