Linux系统编程——理解系统内核中的信号捕获
目录
一、sigaction()
使用
信号捕捉技巧
二、可重入函数
三、volatile关键字
四、SIGCHLD信号
在信号这一篇中我们已经学习到了一种信号捕捉的调用接口:signal(),为了深入理解操作系统内核中的信号捕获机制,我们今天再来看一个接口:sigaction()
一、sigaction()
sigaction
一个系统调用,用于修改和/或查询信号的处理方式。它提供了对信号处理函数更精细的控制,相较于旧的 signal
函数来说更为强大和灵活。
1、int signum:该参数需要传入指定的进程信号,表示要捕捉的信号
2、const struct sigaction *act:该参数与函数同名,是一个结构体指针如图
1、void (*sa_handler)(int);,此成员的含义其实就是 自定义处理信号的函数指针;
2、void (*sa_sigcation)(int, siginfo_t *, void *);此成员也是一个函数指针. 但是这个函数的意义是用来 处理实时信号的, 不考虑分析. (siginfo_t 是表示实时信号的结构体)
3、sigset_t sa_mask;, 此成员是一个信号集, 这个信号集有什么用呢?我们在使用时解释
4、int sa_flags;, 此成员包含着系统提供的一些选项, 本篇文章使用中都设置为0
5、void (*sa_restorer)(void);, 很明显 此成员也是一个函数指针. 但我们暂时不考虑他的意义.也就是说我们暂时将该接口的第二个参数简单理解为一个结构体指针,并且结构体里有一个成员是用来自定义处理信号的。即该参数的作用就是将指定信号的处理动作改为传入的struct sigaction的内容。
3、struct sigaction *oldact,第三个参数类似sigprocmask()接口中的第三个参数一样,都是输出型参数,且其作用是获取 此次修改信号 struct sigaction之前的原本的 struct sigaction ,如果传入的是空指针,则不获取。
使用
我们实现一个捕捉2号信号的程序,然后让他按照我们自定义的处理动作执行,且将自定义的处理动作设置为死循环,也就是让进程在收到2号信号后一直执行该信号的处理动作。
#include<iostream>
#include<unistd.h>
#include<signal.h>
using std::cout;
using std::endl;void handler(int signum)
{cout<<"I catch a signal::"<<signum<<endl;sigset_t pending;while(true){sigpending(&pending);int sig=1;for(sig=1;sig<=31;sig++){//利用死循环打印未决信号集if(sigismember(&pending,sig)){cout<<"1";}else{cout<<"0";}}cout<<endl;sleep(1);}
}
int main()
{struct sigaction act,oact;//实例化出两个结构体对象作为参数act.sa_handler=handler;//初始化自定义处理动作act.sa_flags=0;//先设置为0sigemptyset(&act.sa_mask);//初始化sigaction(2,&act,&oact);//捕捉2号信号while(true){cout<<"I am a process!My pid::"<<getpid()<<endl;sleep(1);}return 0;
}
可以看到在我们第一次发送了2号信号后,其打印出来的未决信号集全是0,这是因为在没有捕捉到2号信号前,该进程的未决信号集全为0,捕捉之后,第二个位置应该为1,然后开始处理自定义动作前将1又置为0,表示开始处理自定义动作,此时就一直处于死循环中,即一直在执行自定义动作。
当我们第二次发送2号信号时,它的未决信号集的对应位置就变为了1,后面再发送2号信号时,该信号都会被拦截下来导致后面的2号信号一直处于未决状态。但是发送其他信号进程又会处理其他信号。
那么我们要是想要在处理2号信号的同时还要将其他信号拦截呢?这时候就与 sa_mask 相关了,顾名思义了就是信号屏蔽字。
struct sigaction结构体的sa_mask 成员的意义是, 添加进程在处理捕捉到的信号时对其他信号的阻塞. 如果需要添加对其他信号的阻塞, 那么就可以继续在 sa_mask 中添加其他信号.
不过, 这样做有什么意义呢?
这样做可以 防止用户自定义处理信号时, 嵌套式的发送其他信号并捕捉处理.
如果 用户的自定义处理信号方法内部, 还会发送其他信号, 并且用户还对其进行了捕捉. 那么 信号的处理就无止尽了. 这种情况是不允许发生的.
所以 可以通过使用 sa_mask 来进行对其他信号的拦截阻塞.
信号捕捉技巧
为了避免捕捉不同的信号并做处理时,编写不同的处理函数太过于麻烦,我们可以考虑通过传入相同的函数指针实现对不同信号的不同处理。
当我们定义完指定信号的处理函数之后, 我们可以再定义一个 handlerAll(int signo)
函数, 并使用 switch 语句, 将不同的 signo 分别处理.
此时, 我们在使用 signal()
或者 sigaction()
捕捉信号时, 就只需要统一传入 handlerAll
的函数指针就可以了.
这是一种 解耦技巧
二、可重入函数
可重入函数(Reentrant Function)是指可以在多线程环境中安全使用的函数,即这个函数可以被多个线程同时调用而不会导致数据错误或不一致。
下面用个例子解释一下
一个进程中, 存在一个全局的单链表结构. 并且此时需要执行一个节点的头插操作:
此时需要让该结点的指向下一个节点的指针指向头节点,再将自己变为头节点
node1->next = head;
head = node1;
如果在刚执行完第一步之后, 进程因为硬件中断或者其他原因 陷入内核了
.
陷入内核之后需要回到用户态继续执行, 切换回用户态时 进程会检测未决信号, 如果此时刚好存在一个信号未决, 且此信号自定义处理.并且, 自定义处理函数中 也存在一个新节点头插操作:
此时又会执行node2节点的头插 ,执行完后node2节点暂时就成为了新的头节点
接着进程返回用户态去执行剩下的代码,即 head=node1 ,
导致的结果就是node2节点最终找不到了,这样就造成了 内存泄漏
是因为 单链表的头插函数, 被不同的控制流程调用了, 并且是在第一次调用还没返回时就再次进入该函数, 这个行为称为 重入
而 像例子中这个单链表头插函数, 访问的是一个全局链表, 所以可能因为重入而造成数据错乱, 这样的函数 被称为 不可重入函数, 即此函数不能重入, 重入可能会发生错误
反之, 如果一个函数即使重入, 也不会发生任何错误(一般之访问函数自己的局部变量、参数), 这样的函数就可以被称为 可重入函数. 因为每个函数自己的局部变量是独立于此次函数调用的, 再多少次调用相同的函数, 也不会对之前调用函数的局部变量有什么影响.
如果一个函数符合以下条件之一,则称为不可重入函数
- 调用了malloc和free,因为 malloc 也是用全局链表来管理堆的。
- 调用了标准I/O库函数, 标准I/O库的很多实现都以不可重入的方式使用全局数据结构
三、volatile关键字
在之前学习C/C++的时候,我们就已经接触过这个关键字了,它的作用是防止编译器对该关键字修饰的变量的优化,确保每次访问这个变量时都直接从内存中读取,而不是使用可能存在的寄存器中的缓存值。这是因为在某些情况下,变量的值可能会被外部因素改变,如硬件中断、多线程环境下的其他线程等。
接下来我们用一个例子分析一下该关键字
下面的程序是先定义一个全局变量flag,以该全局变量作为触发条件,当为0时,一直处于死循环状态。当为1时程序正常结束。在main()中不对flag做修改,只有在捕获到2号信号的时候,在自定义的处理函数中才会对flag做出修改。
#include <stdio.h>
#include <signal.h>int flags = 0;void handler(int signo) {printf("获取到一个信号,信号的编号是: %d\n", signo);flags = 1;printf("我修改了flags: 0->1\n");
}int main() {signal(2, handler);while (!flags);// 未接收到信号时, flags 为 0, !flags 恒为真, 所以会死循环printf("此进程正常退出\n");return 0;
}
可以看到在发送了2号信号后程序正常结束。
- 虽然 2信号的自定义处理函数 会对flags作出修改, 但是这个函数的执行是未知的. 即 不确定进程是否会收到2信号 进而执行此函数.
- 那么对编译器来说, 就有可能对 flags 做出优化.
- 我们知道, 进程再判断数据时, 是CPU在访问数据, 而CPU访问数据时 会将数据从内存中拿到寄存器中. 然后再根据寄存器中的数据进行处理.
- 在此例中, 就是 while(!flags); 判断时, CPU会从内存中拿出数据进行判断. 当flags从0变为1时, 是内存中的数据发生了变化, CPU也会从内存中拿到新的数据进行判断
- 而 此例中编译器可以确定一定会执行的代码中, flags是不会被修改的. 那么 编译器就可能针对flags做出优化:
- 由于编译器认为进程不会修改 flags, 那么在 while(!flags); 判断时, CPU读取到flags为0 并存放在寄存器中之后, 为了节省资源 在之后的判断中 CPU 不会再从内存中读取数据, 而是直接根据寄存器中存储的数据进行判断.
- 这就会造成 即使处理信号时将 flags 改为了1, 在进行 while(!flags);判断时, CPU依旧会只根据寄存器中存储的0 来进程判断, 这就会造成 进程不会正常退出
我们可以在编译是使用 -02 选项让编译器做出这样的优化
可以看到即使是flag改成了1,程序依然不会停止
接着我们使用关键字修饰这个变量 volatile int flag=0 ,再查看结果
可以看到已经没有优化了。
四、SIGCHLD信号
我们之前讲到过,在子进程退出的时候,是需要让父进程接收退出信息的,否则子进程会进入僵尸状态,所以我们介绍了有关于进程等待的函数,让父进程主动去询问子进程是否退出,事实上,子进程退出的时候是会通知父进程的,只不过父进程会忽略而已。
在子进程退出的时候,子进程会向父进程发送一个信号,即 SIGCHID
下面我们在父进程中捕捉这个信号看看情况:
#include<iostream>
#include<cstdlib>
#include<signal.h>
#include<unistd.h>
using std::cout;
using std::endl;
using std::cerr;
void handler(int signum)
{cout<<"Child process has exited,mypid::"<<getpid()<<"Signal num::"<<signum<<endl;
}
int main()
{signal(SIGCHLD,handler);pid_t id=fork();if(id<0){cerr<<"fork error!"<<endl;exit(1);}else if(id==0){while(true){cout<<"I am child process,mypid::"<<getpid()<<endl;sleep(2);}exit(0);}while(true){cout<<"I am parent process!my pid::"<<getpid()<<endl;sleep(2);}return 0;}
17号信号即 SIGCHID信号 ,且默认动作是Ign(忽略)
但是对于该信号,内容中说明 子进程暂停或终止,即在子进程暂停或终止的时候都会发送该信号给父进程。我们做个测试,首先需要知道暂停信号是19号,继续信号是18号信号
可以看到无论是暂停、继续、还是终止子进程的时候,其都会向父进程发送该信号。
那么我们知道了这个信号又有什么用处呢?
在介绍进程等待时 提到过,waitpid()接口会等待子进程退出, 而等待的动作是主动去询问子进程是否退出.
现在我们清楚了子进程在退出的时候会发送SIGCHID信号给父进程,那我们让父进程可以通过捕捉该信号去等待子进程。
#include<iostream>
#include<cassert>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
using std::cout;
using std::endl;
using std::cerr;
void Childprofree(int signo)
{assert(signo==SIGCHLD);pid_t id=waitpid(-1,nullptr,0);if(id>0){cout<<"Waiting success!pid::"<<getpid()<<"child process id::"<<id<<endl;}}int main()
{signal(SIGCHLD,Childprofree);pid_t id=fork();if(id<0){cout<<"Perror fork!"<<endl;exit(0);}else if(id==0){while(true){cout<<"I am child process!my pid::"<<getpid()<<endl;sleep(2);}exit(0);}while(true){cout<<"I am parent process!my pid::"<<getpid()<<endl;sleep(2);}return 0;
}
上面的程序只针对单进程的情况,如果是多个进程的情况下就会有一些问题
我们利用循环创建10个子进程,这10个子进程在打印完十次之后自动退出。
#include<iostream>
#include<cassert>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
using std::cout;
using std::endl;
using std::cerr;
void Childprofree(int signo)
{assert(signo==SIGCHLD);pid_t id=waitpid(-1,nullptr,0);if(id>0){cout<<"Waiting success!pid::"<<getpid()<<"child process id::"<<id<<endl;}}int main()
{signal(SIGCHLD,Childprofree);for(int i=0;i<10;i++){pid_t id=fork();if(id<0){cout<<"Perror fork!"<<endl;exit(0);}else if(id==0){int cnt=10;while(cnt){cout<<"I am child process!my pid::"<<getpid()<<"cnt::"<<cnt--<<endl;sleep(2);}cout<<"Child process being Z!"<<endl;exit(0);}}while(true){cout<<"I am parent process!my pid::"<<getpid()<<endl;sleep(2);}return 0;
}
我们在右边窗口使用 while :;do ps ajx|head -1&&ps ajx|grep mydwait|grep -v grep;sleep 2;done命令循环查看进程情况
运行结果如图
可以看到等到子进程推出的时候,按道理是所有的子进程都退出都被父进程回收掉,但是只有左边红色框里这几个进程退出了,右边红色框里还有几个没有被回收掉,一直处于僵尸状态。
在Linux中,每个进程都有一个信号集,用来表示该进程当前接收到的、尚未处理的信号。这个集合有一个重要特点:它是基于信号类型的,而不是基于信号的数量。这意味着对于同一类型的信号(例如多个
SIGCHLD
),操作系统不会为每个信号单独排队,而是只会记录该类型信号至少发生过一次。换句话说,如果多个SIGCHLD
信号几乎同时到达,操作系统会将它们合并成一个信号,并只传递给父进程一次。因此,如果大量的子进程几乎在同一时间结束,父进程可能只接收到一个
SIGCHLD
信号,而实际上有多个子进程已经终止。这就导致了父进程可能没有机会处理所有终止的子进程,从而留下僵尸进程。
那么怎么修改处理信号的方式呢?我们将回收子进程的部分设置为 利用死循环回收,在没有子进程的情况下跳出循环。
一旦有收到子进程的退出信号后,这个外部的函数就会进入死循环,不断等待释放需要退出的子进程,直到没有子进程需要释放了就退出。事实上这个改动就跟收到信号没有关系了,单纯利用循环不断等待需要被释放的子进程。
void freeChild(int signo) {assert(signo == SIGCHLD);while(true) {pid_t id = waitpid(-1, nullptr, 0);if (id > 0) {cout << "父进程等待子进程成功, child pid: " << id << endl;}else {cout << "等待结束" << endl;break;}}
}
可以看到最后所有的进程都退出了。
但是新的问题又出现了,一旦有子进程不退出的话,父进程就不会再运行了,因为我们设置的waitpid()的第三个参数为0是阻塞式等待,所以会一直处在外部的自定义处理函数中,不会回到main()函数
观察下面的情况,我们让一部分子进程循环5次后退出,一部分循环30次后退出
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using std::cout;
using std::endl;void freeChild(int signo) {assert(signo == SIGCHLD);while (true) {pid_t id = waitpid(-1, nullptr, 0);if (id > 0) {cout << "父进程等待子进程成功, child pid: " << id << endl;}else {cout << "等待结束" << endl;break;}}
}int main() {signal(SIGCHLD, freeChild);for (int i = 0; i < 10; i++) {pid_t id = fork();if (id == 0) {// 子进程int cnt = 0;if(i < 6)cnt = 5; // 前6个子进程 5就退出elsecnt = 30; // 后4个子进程 30退出while (cnt) {cout << "我是子进程, pid: " << getpid() << ", cnt: " << cnt-- << endl;sleep(1);}cout << "子进程退出, 进入僵尸状态" << endl;exit(0);}}// 父进程while (true) {cout << "我是父进程, pid: " << getpid() << endl;sleep(1);}return 0;
}
可以看到在所有进程退出之前父进程代码并没有运行,我们只需要将 waitpid()的第三个参数改为 WNOHANG 就可以了表示 非阻塞式等待。
下面是最终版代码
#include<iostream>
#include<cassert>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
using std::cout;
using std::endl;
using std::cerr;
void Childprofree(int signo)
{assert(signo==SIGCHLD);while(true){pid_t id=waitpid(-1,nullptr,WNOHANG);if(id>0){cout<<"Waiting success!pid::"<<getpid()<<"child process id::"<<id<<endl;}else{cout<<"Wating end!"<<endl;break;}}}int main()
{signal(SIGCHLD,Childprofree);for(int i=0;i<10;i++){pid_t id=fork();if(id<0){cout<<"Perror fork!"<<endl;exit(0);}else if(id==0){int cnt = 0;if(i < 6)cnt = 5; elsecnt = 30; while(cnt){cout<<"I am child process!my pid::"<<getpid()<<"cnt::"<<cnt--<<endl;sleep(2);}cout<<"Child process being Z!"<<endl;exit(0);}}while(true){cout<<"I am parent process!my pid::"<<getpid()<<endl;sleep(2);}return 0;
}
相关文章:
Linux系统编程——理解系统内核中的信号捕获
目录 一、sigaction() 使用 信号捕捉技巧 二、可重入函数 三、volatile关键字 四、SIGCHLD信号 在信号这一篇中我们已经学习到了一种信号捕捉的调用接口:signal(),为了深入理解操作系统内核中的信号捕获机制,我们今天再来看一个接口:si…...
ISP算法之BNR降噪(Bayer域)
概述 BNR(Bayer Noise Reduction)即Bayer域降噪算法。对于噪声的分类如下表所示: 高斯噪声( Gaussian) 高斯噪声也被称为热噪声,通常是由于电路系统中自由电子的热运动,这种噪声幅度分布服从高…...
HBuilder快捷键大全
目录 一、最常用快捷键 二、文件操作快捷键 三、选择操作快捷键 四、插入操作快捷键 五、编辑操作快捷键 六、删除操作快捷键 七、查找操作快捷键 八、标签规范快捷键 八、运行操作快捷键 九、转换操作快捷键 十、跳转操作快捷键 十一、附加功能快捷键 十二、快捷键…...
Diffusion_Policy项目测试报错记录
1、项目连接 paper:2303.04137 (arxiv.org) github: real-stanford/diffusion_policy: [RSS 2023] Diffusion Policy Visuomotor Policy Learning via Action Diffusion (github.com) 2、问题与解决办法 1) 运行 python train.py --..... 显示无法Err…...
Linux内核学习资源
老版本内核源码: https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/old-versions/ 内核源码分析开源项目: https://github.com/ultraji/linux-0.12/tree/master/srchttps://gitee.com/wslyx/linux-0.12https://github.com/0voice/linux_ke…...
NGINX的安装和配置(Linux环境)
目录 NGINX 安装方式1、 离线编译安装2、 在线仓库安装 NGINX 常用命令1、进程管理命令2、信息查看命令 NGINX 配置文件1、进程使用的配置2、配置文件格式3、配置文件层级 NGINX 全局配置1、全局配置常用指令2、连接相关配置 NGINX 配置MIME1、MIME 标准2、types 配置块3、defa…...
Leetcode打卡:考场就坐
执行结果:通过 题目: 855 考场就坐 在考场里,有 n 个座位排成一行,编号为 0 到 n - 1。 当学生进入考场后,他必须坐在离最近的人最远的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外&am…...
STM32——“SPI Flash”
引入 在给单片机写程序的时候,有时会用到显示屏,就拿市面上的0.96寸单色显示器来说,一张全屏的图片就占用8x1281024个字节,即1kb的空间,这对于单片机来说确实有点奢侈,于是我买了一个8Mb的SPI Flash&#x…...
Zookeeper基本命令解析
ZooKeeper -server host:port -client-configuration properties-file cmd args addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE addauth scheme auth 一、整体命令格式 ZooKeeper -serve…...
React-更新state的数据,dom没有重新渲染
{entourageList.map((person) > (<Row key{person.id}> {/* 使用唯一的 person.id */}<Col span{16}><Row gutter{16}><Col lg{9} md{18} sm{24}><FormItem label{姓名}>{getFieldDecorator(name-${person.id}, {initialValue: person.name,…...
消息队列技术的发展历史
消息队列技术的演进历程宛如一幅波澜壮阔的科技画卷,历经多个标志性阶段,各阶段紧密贴合不同的技术需求与市场风向,下面为您详细道来。 第一阶段:消息中间件的起源(1970 年代末期 - 1980 年代中期) 在计算…...
20.抽卡只有金,带保底(WPF) C#
这是一个界面应用化后的抽卡,目前只能抽金,只有基础概率加保底概率 适合界面化应用初学者。 这是展示图: 使用的是WPF不是winform,我也是第一次做WPF内容,就可以试一试,代码都在这里了,简单来…...
DALFox-一款XSS自动化扫描工具
声明!本文章所有的工具分享仅仅只是供大家学习交流为主,切勿用于非法用途,如有任何触犯法律的行为,均与本人及团队无关!!! 目录标题 一、介绍及使用启动及使用1. 单个扫描2. 多个扫描3. 文件扫描…...
软件测试之压力测试【详解】
压力测试 压力测试是一种软件测试,用于验证软件应用程序的稳定性和可靠性。压力测试的目标是在极其沉重的负载条件下测量软件的健壮性和错误处理能力,并确保软件在危急情况下不会崩溃。它甚至可以测试超出正常工作点的测试,并评估软件在极端…...
电视大全 1.3.8|汇聚多频道资源,秒切换流畅播放
电视大全TV版是一款功能丰富的TV端直播软件,由悠兔电视的同一开发者打造。最新版本更新了更多频道,包括央视、卫视和地方频道等,提供了多线路流畅播放体验,并支持节目回看、线路选择、开机自启等功能。该应用免登录且无购物频道&a…...
MaxKB基于大语言模型和 RAG的开源知识库问答系统的快速部署教程
1 部署要求 1.1 服务器配置 部署服务器要求: 操作系统:Ubuntu 22.04 / CentOS 7.6 64 位系统CPU/内存:4C/8GB 以上磁盘空间:100GB 1.2 端口要求 在线部署MaxKB需要开通的访问端口说明如下: 端口作用说明22SSH安装…...
大恒相机开发(3)—大恒相机工业检测的实际案例
大恒相机工业检测的实际案例 工业检测的实际案例图像采集性能优化技巧工业环境下的稳定性 工业检测的实际案例 以下是一些使用大恒相机进行工业检测的实际案例: 多特征光学成像系统: 在这个案例中,使用大恒相机构建了一个全方位、多特征的图…...
《Java 与 OpenAI 协同:开启智能编程新范式》
在当今科技飞速发展的时代,人工智能已成为推动各领域创新变革的核心力量。OpenAI 作为人工智能领域的领军者,其开发的一系列强大模型,如 GPT 系列,为自然语言处理等诸多任务带来了前所未有的突破。而 Java,作为一种广泛…...
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
在智能手机、平板和其他智能设备日益智能化的今天,视觉识别技术成为提升用户体验和智能交互的重要手段。HarmonyOS NEXT通过基础视觉服务(HMS Core Vision)提供了一套强大的视觉识别功能,其中多目标识别作为其关键技术之一&#x…...
MySQL体系架构
MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层。 网络连接层 客户端连接器(Connectors):提供与MySQL服务器建立的支持。目前几乎支持所有主 流的服务端编程技术,例如常见的 Java、C、Python、.…...
如何学习Trustzone
阅读官方文档 ARM 官方文档是学习 Trustzone 最权威的资料来源。例如,ARM Architecture Reference Manual 中详细介绍了 Trustzone 的架构原理、寄存器定义和操作模式等内容。这些文档虽然比较复杂,但能够提供最准确的技术细节,适合在学习过…...
【视觉SLAM:Panoptic FPN全景分割网络】
Panoptic FPN是一种全景分割(Panoptic Segmentation)的经典网络,最早由 Kirillov 等人在 2019 年提出。全景分割是一种统一的视觉任务,结合了实例分割(Instance Segmentation)和语义分割(Semant…...
【Linux】数据呈现
一、数据的输入与输出 1、标准文件描述符 Linux系统会将每个对象都当做文件来处理,包括输入和输出。它用文件描述符来标识每个文件对象。 文件描述符是一个非负整数,唯一会标识的是会话中打开的文件。每个进程一次最多可以打开9个文件描述符。bash sh…...
编译原理期末复习--伪代码部分
1.词法分析 token token nexttoken()c getchar();switch(c):case <:c getchar();switch(c):case : return RE;case >: return NE;default: rollback(); return LT;case :return EQ;case >:c getchar();switch(c):case : return GE;default: rollback(); return GT…...
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
要成为一名云原生相关的 Go 语言工程师,需要在 Go 语言、云原生技术栈以及相关的开发和运维工具上建立扎实的基础。下面是一个前字节员工总结的技术路线规划: 1. 掌握 Go 语言基础 深入理解 Go 语言:你需要熟练掌握 Go 的语法、数据结构、并…...
基于Springboot + vue实现的汽车资讯网站
🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…...
【YashanDB知识库】ycm-YashanDB列表有数据库显示故障排除步骤
本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802959.html?templateId1718516 数据库状态 正常 异常 1、查看告警列表 例如:告警显示实例无法连接,一般是数据库实例服务掉了,需要尽快联系系统…...
如何使用 FastAPI 框架创建 RESTful API ?
FastAPI是一个现代、快速(高性能)的Web框架,用于构建API,它基于Python 3.6的类型提示功能,可以自动生成交互式API文档(使用Swagger UI或ReDoc),并且支持异步编程。 二、创建第一个F…...
Java 继承
继承是所有 OOP 语言和 Java 语言不可缺少的组成部分。 继承是 Java 面向对象编程技术的一块基石,是面向对象的三大特征之一,也是实现软件复用的重要手段,继承可以理解为一个对象从另一个对象获取属性的过程。 如果类 A 是类 B 的父类&…...
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
此项目是基于人脸识别的考勤系统开发,包括如下模块: 1、人脸识别考勤系统GUI界面设计,包括: (1)Qt环境(window环境/linux环境) ; (2)Qt工程创建分析; &am…...
门户系统需要压测吗?以及门户系统如何压力测试?
一、门户系统为什么要进行压力测试? 首先一点要明确一下,统一门户上线以后,将是所有应用系统的入口,对应门户稳定性要求较高,门户实现了统一入口和统一认证,系统宕机将影响其他系统使用。一般部署架构要求…...
Linux扩展——shell编程
前置:Linux基础及命令复习 目录 shell概述Shell脚本入门案例 sh bash ./ . source 变量系统预定义变量 $HOME $PWD $SHELL等自定义变量 unset readonly补充:开启子Shell进程的常见方法 (...) $(...) ... <(...) >(...) 特殊变量 $n $# $* $ $&…...
golang, go sum文件保证下载的依赖模块是一致的
在 Go 编程语言中,go.sum 文件是 Go 模块管理的一部分,主要用于记录模块的校验信息(模块版本的校验和)。它的设计目标是确保模块的完整性和安全性,解决以下关键问题: 1. 确保模块版本的一致性 go.sum 文件…...
TANGO与LabVIEW控制系统集成
TANGO 是一个开源的设备控制和数据采集框架,主要用于管理实验室设备、自动化系统和工业设备。它为不同类型的硬件提供统一的控制接口,并支持设备之间的通信,广泛应用于粒子加速器、同步辐射光源、实验室自动化和工业控制等领域。 1. TANGO的核…...
【day14】异常处理与Object类深入解析
【day13】回顾 在深入探讨异常处理与Object类之前,让我们回顾一下【day13】中的关键内容: 权限修饰符: public:最广的访问范围,任何地方都可以访问。protected:在同包和子类中可以访问。默认(无…...
云技术基础知识(二):虚拟化与容器技术
内容预览 ≧∀≦ゞ 虚拟化与容器技术虚拟化技术一、虚拟化的核心概念二、虚拟化的主要类型1. 服务器虚拟化2. 操作系统虚拟化(容器化)3. 网络虚拟化4. 存储虚拟化 三、虚拟化的实现方法和工具1. 服务器虚拟化实现2. 操作系统虚拟化(容器化&am…...
【Java基础面试题033】Java泛型的作用是什么?
Java的基础语法可以看尚硅谷的这个PDF:尚硅谷JavaSE基础/《Java从入门到精通(JDK17版)》_尚硅谷电子书.pdf Autism_Btkrsr/Blog_md_to_pdf - 码云 - 开源中国 (gitee.com) 回答重点 Java泛型的作用是通过在编译时检查类型安全,允许程序员编写更通用和…...
Linux 基本使用和程序部署
1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种: 直接安装在物理机上。但是Linux桌面使用起来非常不友好,所以不建议。[不推荐]。使用虚拟机软件,将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bugÿ…...
react中使用ResizeObserver来观察元素的size变化
在 React 中使用 ResizeObserver 来观察元素的大小变化,可以通过创建一个自定义 Hook 来封装 ResizeObserver 的逻辑,并在组件中使用这个 Hook。以下是一个完整的示例,展示了如何在 React 中使用 ResizeObserver 来观察元素的大小变化。 自定…...
常见数据结构
1.数组 vector 2.链表 list 双向链表,不能通过下标找元素 3.栈 stack 4.队列 queue 优先队列priority_queue. 默认队头为最大值,可以用来任务调度,图算法,霍夫曼树 5.双端队列 deque 6.集合 set multiset s.begin() //返回…...
【服务器】linux服务器管理员查看用户使用内存情况
【服务器】linux服务器管理员查看用户使用硬盘内存情况 1、查看所有硬盘内存使用情况 df -h2、查看硬盘挂载目录下所有用户内存使用情况 du -sh /public/*3、查看某个用户所有文件夹占用硬盘内存情况 du -sh /public/zhangsan/*...
Java-冒泡排序、选择排序、二分查找算法
1. 冒泡排序 (1) 冒泡排序:每次从数组中找出最大值放在数值的后面去 public static void main(String[] args) {//冒泡排序int[] arr {5, 6 ,4, 9, 3, 1};for (int i 0; i < arr.length - 1; i) {for (int j 0; j < arr.length - i - 1; j) {if (arr[j] &…...
leetcode之hot100---240搜索二维矩阵II(C++)
思路一:通过遍历主对角线上元素判断查找方向 主对角线遍历: 遍历主对角线上的每个元素(matrix[i][i]),其中 i 的范围是 [0, min(m, n) - 1]。如果目标值小于当前主对角线元素,说明目标值可能在当前元素的左…...
牛客--求小球落地5次后所经历的路程和第5次反弹的高度,称砝码
求小球落地5次后所经历的路程和第5次反弹的高度 描述 假设有一个小球从 hh 米高度自由落下,我们不考虑真实的物理模型,而是简洁的假定,该小球每次落地后会反弹回原高度的一半;再落下,再反弹;……。 求小球…...
EasyPoi 使用$fe:模板语法生成Word动态行
1 Maven 依赖 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version> </dependency> 2 application.yml spring:main:allow-bean-definition-over…...
在 PowerShell 中优雅地显示 Python 虚拟环境
在使用 Python 进行开发时,虚拟环境管理是一个非常重要的部分。无论是使用 venv 还是 conda,我们都希望能够清晰地看到当前所处的虚拟环境。本文将介绍如何在 PowerShell 中配置提示符,使其能够优雅地显示不同类型的 Python 虚拟环境。 问题…...
GUI07-学工具栏,懂MVC
MVC模式,是天底下编写GUI程序最为经典、实效的一种软件架构模式。当一个人学完菜单栏、开始学习工具栏时,就是他的一生中,最适合开始认识 MVC 模式的好时机之一。这节将安排您学习: Model-View-Controller 模式如何创建工具栏以及…...
免费线上签字小程序,开启便捷电子签名
虽如今数字化飞速发展的时代,但线上签名小程序的开发制作却并非易事。需要攻克诸多技术难题,例如确保签名的真实性与唯一性,防止签名被伪造或篡改。 要精准地捕捉用户手写签名的笔迹特征,无论是笔画的粗细、轻重,还是…...
计算机的错误计算(一百八十九)
摘要 用大模型计算 tan(12.345) . 自变量取弧度。结果保留10位有效数字。不同于前面两节的大模型,本节调用了新的两个大模型。然而,很遗憾,它们给出的答案似乎仍然是“匹配”出来的,不是计算出来的。当然,均是错误的。…...
HDR视频技术之十一:HEVCH.265 的 HDR 编码方案
前文我们对 HEVC 的 HDR 编码优化技术做了介绍,侧重编码性能的提升。 本章主要阐述 HEVC 中 HDR/WCG 相关的整体编码方案, 包括不同应用场景下的 HEVC 扩展编码技术。 1 背景 HDR 信号一般意味着使用更多比特,一般的 HDR 信号倾向于使用 10…...