【Linux】进程 信号的产生

🌻个人主页:路飞雪吖~

       🌠专栏:Linux


目录

一、掌握Linux信号的基本概念

 🌠前台进程 VS 后台进程

🌠 小贴士:

🪄⼀个系统函数 --- signal()

 🪄查看信号 --- man 7 signal

二、软硬件上理解 : OS如何进行信号处理

✨硬件上理解

🌠 信号 VS 硬件中断

✨软件:如何理解信号处理?

三、✨信号产生的方式

🚩1. 键盘产生

🚩2. 系统指令产生  发送信号  ---- 底层使用的是系统调用​编辑

🚩3. 系统调用 发送信号

🌠  发送信号的其他函数:

🪄 raise --- 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号) 

🪄 abort --- 自己给自己发了一个特定的终止自己的信号 --- 6号信号[SIGABRT]

🚩4. 由软件条件 产生信号

 🌠OS内定时器

四、✨ alarm() 设置重复闹钟 --- 简单快速理解系统闹钟

🚩5. 异常 (野指针、 除 0)

 🪄模拟 野指针

 🪄 模拟 除 0

🌠OS怎么知道我们的进程内部出错了?为什么会陷入死循环?

☄️ 除 0 :

☄️ 野指针

🌠core VS Term 

✨core 的使用 --- 调试

🌠 如果是子进程异常了呢? core 会不会出现?

🌠小结:

 五、信号处理

1. 默认处理动作

2. 忽略:本身就是一种信号捕捉的方法,动作就是忽略

 3. 自定义 捕捉一个信号


• 信号是内置的,进程认识信号,是程序员内置的特性;

• 信号的处理方法,在信号产生之前,就已经准备好了;

• 何时处理信号?先处理优先级很高的,可能并不是立即处理,在合适的时候处理;

• 怎么处理信号:a. 默认行为,b. 忽略信号,c. 自定义动作

信号产生 ---> 信号保存 ---> 信号处理

一、掌握Linux信号的基本概念

// MakefileBIN=sig
CC=g++
SRC=$(shell ls *.cc)
OBJ=$(SRC:.cc=.o)$(BIN):$(OBJ)$(CC) -o $@ $^ -std=c++11%.o:%.cc$(CC) -c $< -std=c++11.PHONY:clean
clean:rm -f $(BIN)
// Signal.cc#include <iostream>
#include <unistd.h>int main()
{while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}

 🌠前台进程 VS 后台进程

• 前台进程【./sig】:在命令行所输入的所有东西都不能执行。

  > Ctrl + c 终止前台进程:给的是shell,shell在前台 --> 信号发给进程。

• 后台进程【./sig &】:bash进程依旧可以进行命令行解释。

  > kill -9 [pid] 终止后台进程

  > fg [作业号] 

🌠 小贴士:

<1>

 【1 -- 31】:普通信号;

 【34 -- 64】:实时信号。

<2> 

ctrl + c --> 信号发给进程,被OS接受并解释成为2号【SIGINT】信号,发送给目标进程 -- 对2号信号的默认处理动作是 终止自己!。

🪄⼀个系统函数 --- signal()

#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{signal(SIGINT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}

#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{signal(SIGQUIT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}

ctrl + \ :3号信号,默认也是终止进程的。

 🪄查看信号 --- man 7 signal

#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{// signal 为什么不放在循环里面? 不需要,只需要设置一次就可以了// signal:如果没有产生2或者3号信号呢? handler不被调用!// signal(SIGINT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler// signal(SIGQUIT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handlerfor (int signo = 1; signo < 32; signo++)// 捕捉 1--31 号的信号,使得很多方法杀不掉进程,但是有一些信号捕捉不到{signal(signo, Handler);std::cout << "自定义捕捉信号:" << signo << std::endl;}while (true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}

二、软硬件上理解 : OS如何进行信号处理

✨硬件上理解

OS怎么知道键盘上面有数据?

当按下键盘 首先是被驱动程序识别到 按下的按键,OS如何知道键盘被按下了?--- 硬件中断 ,键盘一旦按下,在硬件上 键盘 和 CPU 是连接的,在控制信号,输入设备是直接和中央处理器连接的,输入设备首先会给中央处理器发送一个中断信号【硬件电路】,CPU中央处理器收到这个硬件电路之后,立马就会告诉OS,当前外设有一个外设准备好了,接下来让OS主动的去把外设的数据拷贝到内存里,此时OS再也不用主动轮询检测任何输入/输出设备了,只需要等待发生中断信号 --- 硬件和OS可以并行执行了!

 

🌠 信号 VS 硬件中断

• 信号是纯软件,模拟中断的行为;

• 硬件中断,纯硬件。

✨软件:如何理解信号处理?

• 键盘上的组合键 是先被OS系统识别到【OS是键盘真正的管理者,当系统在运行的时候,OS一直在检测键盘上有没有信息】再发给进程,当进程不能立即处理这个信号时,进程就会记录下这个信号【信号是从 1-31 连续的数字,进程是否收到1-31这个数字的信号 --- 在 task_struct 里通过位图记录 0000 0000 0000 0000 0000 0000 0000 0000,比特位的位置:信号的编号,比特位的内容:是否为0/1,是否收到对应的信号】发送信号的本质是什么?【写入信号】OS修改目标进程的PCB中的信号位图【0 -> 1】,操作系统有没有权利修改进程PCB的位图?有,OS是进程的管理者,所以 无论以什么方式发送信号,最终 都是转换到OS,让OS写入信号,因为OS是进程的唯一管理者。   

信号产生有很多种,但是信号发送只有OS;

• 判断进程是否收到信号:进程里面有信号处理的表结构 sighandler_t arr[32] 函数指针数组,根据PCB里面去查位图,根据位图就可以检测出那个比特位为1,拿到比特位为1,这个数字就可以作为该数组的下标【下标-1】,直接去调用函数指针上的方法。

三、✨信号产生的方式

🚩1. 键盘产生

当在可执行程序在前台进程产生之后,按住 ctrl+c ,键盘被按下,计算机的CPU识别到键盘又被按下的动作,唤醒OS,让OS去读取键盘山的 ctrl+c ,读到后将 ctrl + c 解释成 2号 信号【if(案件 == ctrl + c)】,OS会把 ctrl + c 转化成一段代码【向目标前台进程PCB写入2号信号,即把比特位 0 --> 1,OS信号发送完成】,发送完成之后,该进程在后续合适的时候调度运行,发现自己收到一个信号,当前进程默认就要执行自己对 2号 信号的处理动作。

🚩2. 系统指令产生  发送信号  ---- 底层使用的是系统调用

🚩3. 系统调用 发送信号

// MakefileBIN=mykill
CC=g++
SRC=$(shell ls *.cc)
OBJ=$(SRC:.cc=.o)$(BIN):$(OBJ)$(CC) -o $@ $^ -std=c++11%.o:%.cc$(CC) -c $< -std=c++11.PHONY:clean
clean:rm -f $(BIN)
// Signal.cc#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <signal.h>// 设置自己的killvoid Usage(std::string proc)
{std::cout << "Usage: " << proc << " signumber processid " << std::endl;
}// ./mykill  1信号    12345进程号
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}int signumber = std::stoi(argv[1]);// 转换为整数pid_t id = std::stoi(argv[2]);int n = ::kill(id, signumber);if(n < 0){perror("kill");exit(2);}exit(0);
}

信号发送 本质都是操作系统OS发的!!!

🌠  发送信号的其他函数:

🪄 raise --- 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号) 

#include <iostream>
#include <unistd.h>
#include <signal.h>int main()
{int cnt = 5;while(true){std::cout << "hahaha alive" << std::endl;cnt--;if(cnt <= 0)raise(9);sleep(1);}
}

🪄 abort --- 自己给自己发了一个特定的终止自己的信号 --- 6号信号[SIGABRT]

int main()
{int cnt = 5;while(true){std::cout << "hahaha alive" << std::endl;cnt--;if(cnt <= 0)// abort();sleep(1);}
}

🚩4. 由软件条件 产生信号

 SIGPIPE 是⼀种由软件条件产生的信号。

🪄 alarm 函数 --- 调⽤ alarm 函数可以设定⼀个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发 (14号)SIGALRM 信号,该信号的默认处理动作是终⽌当前进程。

int main()
{// 统计我的服务器 1s 可以将计数器累加多少!alarm(1);// 我自己,会在 1s 之后收到一个SIGALARM信号int number = 0;while(true){printf("count: %d\n", number);}}

int number = 0;
void die(int signumber)
{printf("get a sig : %d, count: %d\n", signumber, number);exit(0);
}int main()
{// 统计我的服务器 1s 可以将计数器累加多少!alarm(1);// 我自己,会在 1s 之后收到一个SIGALARM信号signal(SIGALRM, die);while(true){number++;}}

这两个的写法 计数器累加的次数为什么会差别这么大呢?

【printf("count: %d\n", number);】里面的 IO 影响了计算的速度!,【while(true) number++;】纯CPU计算。

 🌠OS内定时器

• 在操作系统内,要对 进程管理、文件管理、多线程、内存管理 进行管理,同时也要做定时管理,OS在开机之后,是会维护时间的,所以设置的闹钟,在OS底层就是设置了一个定时器。 

• 许多进程都可以设置闹钟,OS里可以同时存在很多被设置的闹钟,所以OS就要管理定时器【先描述,再组织】[struct timer{}] ,OS在每次检测超时的时候,会把定时器所对应的节点,按顺序进行升序排序,变成有序的列表,当有超时的时候,只需要从前往后遍历,遇到第一个没有超时的,之前的全部是超时的,遍历的同时,把超时的【struct timer】执行对应的 【func_t f;函数,给目标进程发送SIGALRM信号】在操作系统内给相应的进程发信号。

• 用堆理解定时器的排序:维护成一个最小堆,用超时时间作为键值,所以要想知道定时器有没有超时,只需要查堆顶就可以了,若堆顶没有超时,则所有的节点都没有超时;若堆顶超时,就把堆顶pop出来【堆再重新构建】,去执行相应的操作,重复操作,直到不再超时。

• 所以当我们在调用alarm函数时,就相当于在OS内给我们获取当前时间【currenttime】和 当前的超时时间【seconds】,然后在内核中设置一个节点,放在堆里面,OS就会在超时之后,直接向目标进程发送SIGALRM信号,并且把该节点释放掉。

• 闹钟的返回值 是什么?闹钟剩余时间

int main()
{alarm(10);sleep(4);int n = alarm(0);// 0:取消闹钟std::cout << "n: " << n << std::endl;
}

当软件条件就绪时【超时、闹钟、定时器】,OS就可以向目标进程发送信号。

四、✨ alarm() 设置重复闹钟 --- 简单快速理解系统闹钟

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <signal.h>
#include <functional>
#include <vector>// 闹钟
using func_t = std::function<void()>;int gcount = 0;
std::vector<func_t> gfuncs;// 把信号 更换 成为 硬件中断
void handler(int signo)
{for(auto &f : gfuncs){f();}std::cout << "gcount : " << gcount << std::endl;alarm(1);// 上面的闹钟响了之后,继续执行
}int main()
{gfuncs.push_back([](){ std::cout << "我是一个内核刷新操作" << std::endl;});gfuncs.push_back([](){ std::cout << "我是一个检测进程时间片的操作,如果时间片到了,我会切换进程" << std::endl;});gfuncs.push_back([](){ std::cout << "我是一个内存管理操作,定期清理操作系统内部的内存碎片" << std::endl;});alarm(1);// 一次性的闹钟,超时alarm会自动被取消signal(SIGALRM, handler);while(true)  // gcount++;{pause();std::cout << "我醒来了..." << std::endl;gcount++;}
}

• 操作系统其实就是一个死循环,当OS启动之后,会接收外部固定的事件源【时钟中断,集成在CPU内部】,每隔很短的时间,向OS触发硬件中断,让OS去执行中断方法。

• OS的调度、切换、内存管理、系统调用,全都是依靠中断来完成的。所以OS不应该叫OS,应该叫一个中断程序。

🚩5. 异常 (野指针、 除 0)

 🪄模拟 野指针

void handler(int signo)
{std::cout << "get a signo: " << signo << std::endl;// 我捕捉了 11 号信号,没执行默认动作,也没有退出进程
}int main()
{signal(11, handler);int *p = nullptr;*p = 100;while(true);
}

 🪄 模拟 除 0

void handler(int signo)
{std::cout << "get a signo: " << signo << std::endl;// 我捕捉了 8 号信号,没执行默认动作,也没有退出进程
}int main()
{signal(8, handler);int a = 10;a /= 0;while(true);
}

C/C++中,常见的异常,进程崩溃了,原因是 OS给目标进程发送对应错误的信号,进而导致该进程退出。

🌠OS怎么知道我们的进程内部出错了?为什么会陷入死循环?
☄️ 除 0 :

状态寄存器里会有各种描述状态计算的结果,其中有一个叫做 溢出标记位,正常情况下计算完毕,溢出标记位为0,说明整个计算结果没有溢出,结果可靠,此时把结果写回内存里,就完成对应的复制。若对应的溢出标记位为1,就证明CPU内部计算结果出错了。

当出现溢出,即CPU内部结果出错了,OS就会知道【CPU[硬件]的中断有 内部中断 和 外部中断,CPU内部一旦出现溢出的错误,CPU内部硬件就会触发内部中断,就会告诉OS,OS是硬件的管理者,OS在调度、切换,就是在合理的使用CPU资源】,是哪个进程引起的硬件错误,OS就会通过信号杀掉进程!即当计算出现硬件错误,OS要识别到,就会给目标进程发送对应的信号,杀掉进程!

为什么会陷入死循环【不退出进程】?

这个进程一直没有退出,还要被调度,进程就会被切换、执行等等,【a /= 0】CPU里面的状态寄存器的 Eflags的溢出标记位一直都是1,CPU里面的数据一直都是这个进程的上下文内容不会更改,所以就会一直被捕捉,被调度,就会出现死循环,并且一直在触发8号信号。

☄️ 野指针

CR3 寄存器:保存页表的起始地址,负责虚拟地址和物理地址转化。

MMU【硬件】被集成在CPU内部:完成虚拟地址和物理地址转化。

ELP:读取可执行程序的起始地址。

当传递一个 0 地址,在MMU转化出来 0,我们没有权利访问最低的地址0,MMU这个硬件经过 转化或权限 方面上, 发现根本不能转化出来这个地址,使得MMU【硬件】报错,即CPU内部出错【与上面的类似】,OS就会知道,OS就会向目标进程去识别,就会杀掉这个进程。也要类似 状态寄存器的东西,转化错误,所以就会死循环。

OS怎么知道我们的进程内部出错了?程序内部的错误,其实都会表现在硬件错误上,OS就会知道是哪个地方出错,进而给对应的目标进程发送信号。

🌠core VS Term 

Term :正常终止进程【不需要进行debug】。

Core:也是终止进程,但会多做一些处理。核心转储,在当前目录下形成文件【pid.core】,OS在进程崩溃的时候,将进程在内存中的部分信息保存起来【保存在磁盘上,持久化起来】,方便后续调试!这个文件【pid.core】一般都会被云服务器关掉。

把core功能关闭的原因:在云服务器上,若野指针、除 0 出现错误等等,一般都会在后端立即重启这个服务。若不关闭,当某个错误,一直重复 重启后挂掉,磁盘上就会出现大量的core文件,甚至磁盘爆满,就会影响系统 或 某些应用服务直接就挂掉,即 会因为系统问题,导致磁盘爆满

✨core 的使用 --- 调试

🌠 如果是子进程异常了呢? core 会不会出现?

子进程会不会出现core取决于:退出信号是否终止动作 && 服务器是否开启 core 功能。

🌠小结:

• 键盘、系统指令、系统调用、软件条件、异常 的信号产生,最终都要有OS来执行,因为OS是进程的管理者,统一由OS来向目标进程发信号;

• 信号的处理方式:不是立即处理,而是在合适的时候处理;

• 信号如果不是被立即处理,信号就会暂时被进程记录下来,保存在进程对应的PCB中的信号位图;

• 一个进程在没有收到信号的时候,知道自己应该对合法信号作何处理:默认、忽略、自定义;

• OS向目标进程发送信号:实则是OS向目标进程写信号。

 五、信号处理

1. 默认处理动作

void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{signal(2, SIG_DFL);// default:默认while(true){pause();}
}

2. 忽略:本身就是一种信号捕捉的方法,动作就是忽略

void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{signal(2, SIG_IGN);// 忽略(不做处理),本身就是一种信号捕捉的方法,动作就是忽略while(true){pause();}
}

 3. 自定义 捕捉一个信号

void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{// 信号捕捉:// 1. 默认// 2. 忽略// 3. 自定义::signal(2, handler);// 自定义while(true){pause();}
}

如若对你有帮助,记得关注、收藏、点赞哦~ 您的支持是我最大的动力🌹🌹🌹🌹!!!

若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢 ヾ(≧▽≦*)o  \( •̀ ω •́ )/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/81383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 网络编程入门

目录 一、前言 二、网络通信基础12&#xff1a;TCP 与 UDP 协议解析 2.1 TCP 协议&#xff1a;可靠的面向连接通信 2.2 UDP 协7议&#xff1a;无连接的快速通信 2.3 Sock12et&#xff1a;网络通信的基石 三、TCP 编程实15战&#xff1a;从单工通信到双向聊天 3.1 TCP 客…

Django压缩包形式下载文件

通过web将minio上的文件以压缩包-文件夹-文件的形式下载到本地 import os from bx_mes import settings from io import BytesIO import zipfile from django.http import StreamingHttpResponse class FileRemote(GenericAPIView):def post(self,request):# 压缩包名folder_n…

Enhancing Relation Extractionvia Supervised Rationale Verifcation and Feedback

Enhancing Relation Extraction via Supervised Rationale Verification and Feedback| Proceedings of the AAAI Conference on Artificial Intelligencehttps://ojs.aaai.org/index.php/AAAI/article/view/34631 1. 概述 关系抽取(RE)任务旨在抽取文本中实体之间的语义关

【RAG】ragflow源码亮点:文档embedding向量化加权融合

引言&#xff1a; 最近在看ragflow源码&#xff0c;其中有一个较为巧妙地设计&#xff1a;分别将 文字 、 标题 行向量化 之后&#xff0c;直接根据权重&#xff0c;进行加法运算&#xff0c;得到向量融合&#xff0c;增强了文本向量化的表示能力&#xff0c;这里开始讨论一下…

限流系列:sentinel

目录 滑动窗口算法 Sentinel 数据模型 示例 大致流程 ​​​​​​​entry ​​​​​​​entryWithPriority ​​​​​​​FlowSlot.entry ​​​​​​​checkFlow ​​​​​​​canPass ​​​​​​​avgUsedTokens ​​​​​​​passQps ​​​​​​​pa…

Java 访问者模式深度重构:从静态类型到动态行为的响应式设计实践

一、访问者模式的本质与核心价值 在软件开发的漫长演进中&#xff0c;设计模式始终是架构师手中的利刃。当我们面对复杂对象结构上的多种操作需求时&#xff0c;访问者模式&#xff08;Visitor Pattern&#xff09;犹如一把精密的手术刀&#xff0c;能够优雅地分离数据结构与作…

UE 5 C++设置物体位置和旋转,初始化虚幻引擎样条线、加载引用虚幻编辑器中的蓝图、设置虚幻编辑器中Actor大小

一、设置物体位置和旋转 UE.cpp文件中代码&#xff1a; Mesh->SetWorldLocationAndRotation(FVector(50.0f, 50.0f, 50.0f),FRotator(0,-90,0)); vs代码编辑器中旋转信息顺序&#xff08;yzx&#xff09;&#xff1a; Pitch、 Yaw、 Roll UE编辑器中旋转信息顺序&#xf…

【文本分类】KG-HTC 知识图谱提升分类准确率

最近看到一篇论文“KG-HTC: Integrating Knowledge Graphs into LLMs for Effective Zero-shot Hierarchical Text Classification”&#xff0c;介绍了文本分类的技巧&#xff0c;这篇文航主要利用了知识图谱大模型的思路&#xff0c;实验效果不错&#xff0c;里面的一些论述也…

三大微调技术对比:Prompt/Prefix/P-Tuning

Prompt Tuning、Prefix Tuning和P - Tuning的区别 概念方面: Prompt Tuning:在输入序列前添加可训练的额外Token以适配下游任务,预训练语言模型参数不变。比如在文本分类中,在句子前加特定Token如“(OPINION)”,让模型理解是对观点进行分类的任务。Prefix Tuning:在每层T…

14.「实用」扣子(coze)教程 | Excel文档自动批量AI文档生成实战,中级开篇

随着AI编程工具及其能力的不断发展&#xff0c;编程将变得越来越简单。 在这个大趋势下&#xff0c;大师兄判断未来的编程将真正成为像office工具一样的办公必备技能。每个人通过 &#xff08;专业知识/资源编程&#xff09;将自己变成一个复合型的人才&#xff0c;大大提高生…

量子-经典协同计算新路径:NISQ 时代混合算法对后量子密码学的适应性探索

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨浪味仙 行业动向&#xff1a;3700字丨10分钟阅读 5 月 20 日&#xff0c;由北京量子院、清华大学、数学工程与先进计算国家重点实验室、南洋理工大学、量子信息前沿科学中心…

CentOS中安装Docker Compose

在CentOS中安装Docker Compose的步骤如下&#xff1a; 步骤 1&#xff1a;确保Docker已安装 Docker Compose依赖Docker环境&#xff0c;请先安装Docker&#xff1a; # 添加Docker官方仓库 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://downlo…

电商小程序店铺详情页:头部无限分类与筛选功能实现

电商小程序店铺详情页:头部无限分类与筛选功能实现 一、场景需求与技术选型二、头部无限分类导航三、筛选功能实现:Picker多列选择组件一、场景需求与技术选型 在电商小程序生态中,店铺详情页作为用户浏览商品的核心流量入口,其交互效率与功能完整性直接影响商品转化率。传…

Graph Neural Network(GNN)

我们首先要了解什么是图,图是由节点和边组成的,边的不一样也导致节点的不同(参考化学有机分子中的碳原子) gnn可以处理classification的问题,也就是分类的问题 也可以处理generation的问题 借一部日剧来说明,这个日剧是讲主角寻找杀害他父亲的凶手的,剧中的人物有姓名和特征 …

FallbackHome的启动流程(android11)

首次开机开机动画播完进入Launcher桌面时黑屏进入Launcher,有黑屏不太美观&#xff0c;在重启以后会在进入桌面后会显示android正在启动等一会进入Launcher,这就是系统FallBackHome机制 接下来我们跟着代码看下首次启动系统如何进入FallbackHome的 在SystemServer的startOthe…

【EdgeYOLO】《EdgeYOLO: An Edge-Real-Time Object Detector》

Liu S, Zha J, Sun J, et al. EdgeYOLO: An edge-real-time object detector[C]//2023 42nd Chinese Control Conference (CCC). IEEE, 2023: 7507-7512. CCC-2023 源码&#xff1a;https://github.com/LSH9832/edgeyolo 论文&#xff1a;https://arxiv.org/pdf/2302.07483 …

宫格导航--纯血鸿蒙组件库AUI

摘要&#xff1a; 宫格导航(A_GirdNav)&#xff1a;可设置导航数据&#xff0c;建议导航项超过16个&#xff0c;可设置“更多”图标指向的页面路由。最多显示两行&#xff0c;手机每行最多显示4个图标&#xff0c;折叠屏每行最多6个图标&#xff0c;平板每行最多8个图标。多余图…

调试的按钮

在Debug的时候&#xff0c;会有一些按钮&#xff0c;我们需要知道它们各自的作用。 注&#xff1a;调试器本身并没有一个直接的、可以撤销已执行代码效果的“返回上一步&#xff08;Undo Last Step&#xff09;”或“逆向执行&#xff08;Reverse Debugging&#xff09;”按钮…

人工智能如何协助老师做课题

第一步&#xff1a;在腾讯元宝对话框中输入如何协助老师做课题&#xff0c;通过提问&#xff0c;我们了解了老师做课题的步骤和建议。 第二步&#xff1a;开题报告提问&#xff0c;腾讯元宝对话框中&#xff0c;输入“大单元视域下小学数学教学实践研究课题开题报告。”......…

OpenGL Chan视频学习-5 Vertex Attributes and Layouts in OpenGL

bilibili视频链接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 一、知识点整理 1.1.OpenGL管线工作流程 为显卡提供绘制的所有数据&#xff0c;并将数据存储在GPU内存使用着色器&…