【Linux学习】信号——预备知识 | 信号产生 | 核心转储
🐱作者:一只大喵咪1201
🐱专栏:《Linux学习》
🔥格言:你只管努力,剩下的交给时间!
信号
- 🔔信号
- 🎵预备知识
- 🎵信号处理方法的注册
- 🔔信号的产生
- 🎵通过终端按键产生信号
- 🎵调用系统调用向进程发信号
- 🎵硬件异常产生信号
- 🎵由软件条件产生信号
- 🔔核心转储
🔔信号
从生活中入手,例如发令枪,闹钟,红绿灯等等,这些都是信号。信号必须都是动态的,像路标就不能称之为信号。
以红绿灯为例,一看到红绿灯我们就知道红灯行,绿灯停,我们不仅能认识它是一个红绿灯,而且还知道应该产生什么样的行为,这样才算是能够识别红绿灯。
- 识别 = 认识 + 行为产生
对于红绿等这个信号,我们需要有如下几个共识:
- 我们之所以能识别红绿灯,是因为我们受到过教育(手段),让我们在大脑中记住了不同颜色对应的行为(属性)。
- 当绿灯亮了以后,不一定要立刻过马路,比如有其他的车闯红灯,需要进行避让,所以说我们不一定要立刻产生相应的行为。
- 红灯亮了以后,正好来了一个电话,在接电话这个期间我们会记住此时是红灯,不会将这个状态忘记。
- 红绿灯默认的行为是红灯行,绿灯停,但是也可以产生其他行为,还可以忽略。
现在将生活中红绿灯的例子迁移到进程中:
- 共识:信号是发给进程的。
- 进程之所以能够识别信号,是因为程序员将对应的信号种类和逻辑已经写好了的。
- 当信号发给进程后,进程不一定要立刻去处理,可能有更加紧急的任务,会在合适的时候去处理。
- 进程收到信号到处理信号之前会有一个窗口期,这个期间要将收到的信号进行保存。
- 处理信号的方式有三种:默认动作,自定义动作,忽略。
我们学习信号是学习它的整个生命周期,按照时间轴,分为信号产生,信号保存,信号处理。但是在这之前先需要学习一些预备知识。
🎵预备知识
进程能够识别的信号是已经写好的,它有62个:
- 红色框中的是普通信号,编号从1-31。
- 绿色框中的是实时信号,编号从34-64。
这其中没有32号和33号信号,所以一共有62个信号。而且这里我们只学习普通信号,对实时信号暂不做研究。
- 在使用这些信号时,可以用信号名,也可以用信号编号,它是一样的,都是宏定义后的结果。
根据我们对Linux的了解,信号存放在哪里呢?既然信号是给进程的,而进程是通过内核数据结构来管理的,所以我们可以推断出,信号放在进程的task_struct结构体中。
既然它是在PCB中,而且数量是31个,task_struct中必定不会设置31个变量来存放信号,数组还有可能,但是信号的状态只分为有和没有两种,所以再次推断,31个信号放在一个32位的整形变量中,每个比特位代表一个信号。
本喵写一段伪代码来示意一下:
struct task_struct
{//进程属性unsigned int signal;//.......
}
就像在学习基础IO和进程间通信的时候,那些flags标志中的不同的比特位代表着不同的意义,这31个信号量也是这种方式:
具体的保存细节后面本喵再详细讲解。
问题来了,内核数据结构的修改,这个工作是由谁来完成的?毫无疑问是操作系统,因为task_struct就是它维护的,而且是存在于内存中的,只有操作系统才有权力去修改它,用户是无法直接操作的,因为操作系统不相信任何人。
所以说,无论哪个信号,最后的本质都是由操作系统发生给进程的,这里的发送本质就是在修改task_struct中存放信号那个变量的比特位。
- 信号发送的本质就是在修改PCB中的信号位图。
无论未来我们学习了多少中发送信号的方式,本质都是通过操作系统向目标进程发送信号。
所以操作系统一定会提供相关的系统调用,比如我们之前使用过的各自信号:
kill -9 pid值 //停止某个进程
kill -19 pid值 //暂停某个进程
kill -18 pid值 //继续某个进程
它们的底层一定是在调用相关的系统调用,来让操作系统修改PCB中的信号位图。
🎵信号处理方法的注册
- 所谓的注册,就是告诉操作系统,当某个进程接收到某个信号后的处理方式。
既然是告诉操作系统,那么肯定会用到系统调用,该系统调用的名字是signal():
- int signal:要注册的信号编号
- sighandler_t handler:自定义的函数指针
可以将信号的处理方式写成一个函数,然后将函数名传递个signal,此时当进程接收到signum指定的信号编号时,就会执行我们定义的函数。
void handler(int signo)
{cout<<"进程接收到的一个信号,编号:"<<signo<<endl;
}
函数的返回类型必须是void类型,形参必须是signo类型,名字可以随意。
补充:
我们之前,在shell的命令行中,会按crtl + c 来结束正在运行的进程。这种键我们称为热键。
ctrl + c本质是上一个组合键,操作系统识别到这种组合键后,会将它解释为2号信号SIGINT。
现在将我们自己写的函数注册为2号信号的处理方式。
运行起来后发现,按上ctrl+c后,进程不会结束了。
- 2号信号SIGINT的默认处理方式就是结束进程。
- 我们自定义的处理方式中并没有结束进程,所以进程在收到2号进程后打印了一句话。
- 所有信号的默认处理方式都是结束进程,只是不同的信号代表的意义不一样。
小实验:
我们将31个信号都自定义处理方式,并且不退出进程,那么这个进程是不是就无法退出了?
此时31个信号的自定义处理方式中都没有进程退出。
可以看到,给这个进程这么多信号,都没有结束,而且自定义处理方式也执行了,打印出了接收到的信号编号,但是进程还是在执行。
那这样是不是意味着这个进程无法结束了?不是的,操作系统就防着你呢,不会让这种恶意的东西存在的。
- 9号信号SIGKILL是不能够自定义处理方式的,所以该信号能够将这个进程结束掉。
kill -9 pid值//杀死指定进程
该指令可以结束任何进程,这就是操作系统留的后手。
🔔信号的产生
有了上面的预备知识以后,就可以正式来研究信号了。
🎵通过终端按键产生信号
也就是在键盘上按一些热键,来给进程发送相应的信号,ctrl+c在上面本喵就讲解过了,它产生的是2号信号SIGINT。
还有常用按键ctrl+\,它产生的是3号信号SIGQUIT。
可以看到通过键盘产生了2号和3号信号。
🎵调用系统调用向进程发信号
系统调用kill():
- pid_t pid:要给发信号的pid
- int sig:要发送的信号编号
- 返回值:发送成功返回0,失败返回-1
该系统调用是一个进程给另一个进程发送指定信号,可以向任意进程发送任意信号。
信号接收端:
这是一个一直在运行的程序,用来接收从其他进程发送过来的信号。
信号发送端:
这是一个带有命令行参数的程序,输入的选项中的pid值和信号编号,在程序中在调用kill系统调用向指定pid的进程发送指定信号。
效果:
左边在执行mysginal的时候,输入对应的信号编号和pid,右边正在运行的进程就会接收到指定的信号而停止运行。
系统调用raise():
- int sig:要发送的信号
- 返回值:发送成功返回0,失败返回-1
该系统调用是由进程自己调用,也就是进程自己可以给自己发送任意信号。
通过命令行参数指定信号,在程序执行5秒钟后给自己发送该信号。
无论命令行输入哪个信号的编号,在5秒钟后,该进程都会给自己发送输入的信号,让进程结束。
系统调用abort():
- 没有参数,没有返回值
该系统调用只能给自己发送指定的信号,该信号是SIGABRT,信号编号是6。
5秒钟后给自己发送SIGABRT信号。
在运行5秒钟后,该进程接收到了6号信号SIGABRT。
虽然有3个系统调用来产生信号,但是归根到底都是在使用kill系统调用。
- kill()可以给任意进程发送任意信号。
- raise()可以给自己发送任意信号。
//raise本质
kill(getpid(),signo);
- abort()可以给自己发送SIGABRT信号。
//abort本质
kill(getpid(),SIGABRT);
🎵硬件异常产生信号
除0操作导致的硬件异常:
在这段代码中,有除0操作,我们知道,除0得到的是无穷大的数,所以在编程的时候是不允许出现的。
- 在运行的时候,直接出错,没有再执行下去,是因为接收到了信号。
- 接收到的信号是SIGFPE信号,编号为8号。
这其实就是一种硬件异常产生的信号。
- CPU中有很多的寄存器,例如eax,ebx,eip等等。
CPU会从内存中将代码中的变量拿到寄存器中进行运算,如果有必要,还会将运算的结果放回到内存中。
- 还有一个状态寄存器,如果CPU在运算的时候发现了除0操作,就会将状态寄存器的溢出标志位置一。
此时就意味着硬件产生了异常。而操作系统是一个进行软硬件资源管理的软件,CPU的中状态寄存器的溢出标志位置一后,操作系统可以第一时间拿到。
- 除0导致硬件异常以后,操作系统会给对应的进程发送SIGFPE信号。
当进程接收到SIGFPE信号以后,默认的处理方式就是结束进程。
现在我们对这个SIGFPE信号注册一个自定义处理方式:
只打印接收到的信号编号,进程不退出。
在进程运行起来后,怎么就开始鞭尸了呢?也就是这个信号被操作系统不停的发送给这个进程。
- 进程收到信号后进程不退出,随着CPU时间片的轮转就会再次被调到。
- CPU中只有一份寄存器,但是寄存器中的内容属于当前进程的上下文。
- 当进程被切换的时候,就有无数次的状态寄存器被保存和恢复的过程。
- 而除0操作导致的溢出标志位置一的数据还会被恢复到CPU中。
- 所以每一次恢复的时候,操作系统就会识别到,并且给对应进程发送SIGFPE信号。
所以就会导致上面不停调用自定义处理函数,不停打印接收到的信号编号。
解引用空指针导致的硬件异常:
上面代码中存在对空指针的解引用操作,空指针的本质是(void*)0,而0地址处是不允许我们用户进行访问的,这部分属于内核空间。
- 运行的时候直接出错,没有再运行下去,也是因为接收到了信号。
- 接收到的信号是SIGSEGV,编号是11。
这同样是一种硬件异常产生的信号。
- 我们之前一直谈论的页表时间上是页表+MMU,而MMU是在CPU中的,未来简便,我们就只说页表。
- 进程地址空间和物理内存之间的映射关系实际上是有MMU去完成映射的。
- 当对空指针解引用的时候,MMU会拒绝这种操作,从而产生异常标志。
- 操作系统拿到MMU产生的异常以后就会给对应的进程发送SIGSEGV信号。
当进程接收到编号为11的SIGSEGV信号以后,默认的处理动作就是结束进程。
将这个信号注册自定义处理方式,同样打印接收到的信号编号,但是不结束进程,可以看到,和除0操作一样,也是在鞭尸,不停的打印。
- 硬件异常所产生的信号,如果不结束这个进程,我们是没有能力去处理这个进程的。
- 随着时间片的轮转,这个导致硬件异常的进程还会不停的调到,所以操作系统会不停的向进程发送信号。
- 硬件异常产生的信号并不会显示发送,而是由操作系统自动发送的。
🎵由软件条件产生信号
读端关闭触发的信号:
比如在学习匿名管道的时候,当读端关闭的时候,写端所在进程就会收到编号为13的SIGPIPE信号结束进程。
读端5秒钟之后关闭,写端进程注册自定义处理方式,打印写端接收到的信号编号,但是不结束进程。
在读端关闭以后,写端的自定义处理方式中就接收到了系统发给的SIGPIPE信号,编号为13。
- 读端是否关闭是软件中的条件。
- 当条件达成以后,产生信号。
闹钟触发的信号:
闹钟就是系统中的定时器,使用的时候同样需要通过系统调用实现:
- 参数:要定的时长。
- 返回值:距离定的时间还差多少。
定时1秒钟,在循环中进行疯狂加1,设置自定义处理方式,打印定时到后收到的信号编号,并且统计这一秒中内进行了多少次加1操作。
当定时1秒钟时间到了以后,自定义处理方式中打印出接收到的信号编号是14号的SIGALRM信号,并且统计出了1秒钟进行加1操作的次数。
- 自定义处理方式中没有退出进程,所以在执行完处理方式以后,进程继续运行。
- 也就是继续进行加1操作,但是不会再收到信号了,因为定时到的条件只达成一次,所以信号也只产生一次。
如果想每隔一秒条件达成一次,产生一次SIGALRM信号,可以在这样处理:
- 在自定义处理方式中定时1s。
- 当1秒定时条件达成以后,产生信号,执行处理方法后会开始新一轮的定时。
- 软件中某个条件达成以后,操作系统就会产生相应的信号,比如上面的SIGPIPE信号和SIGALRM信号。
闹钟的管理:
操作系统中会有很多个进程,我们可以创建一个闹钟,那么其他进程也可以创建闹钟,这样就会存在很多个闹钟,那么这些闹钟是怎么管理的呢?先描述再组织。
首先需要创建一个闹钟的结构体,伪代码:
struct alarm
{unit64_t when;//定时时长int type;//闹钟类型,一次性还是周期性task_struct* p;//所属进程的地址struct_alarm* next;//下一个闹钟的地址//其他属性
}
大概就是这样的一个结构体来描述闹钟,必须由的肯定是定时时长,所属进程。
接下来就是组织了,用某一种数据结构来管理这些闹钟对象,为了方便管理,可以选择优先级队列prority_queuq来管理。
- 将定时时间最小的闹钟放在前面,时间长的放在后面。
- 操作系统每次只需要检测队首的定时时间是否达到就可以。
- 达到了就向对应进程发送SIGALRM信号,并且从队列中取出,以待再次检测。
操作系统会周期性的检测链表中的这些闹钟,伪代码:
curr_timestamp > alarm.when;//超时了
//OS发送SIGALRM信号到alarm.p;
具体的实现细节有兴趣的小伙伴可以看看源码是怎么管理的,这里本喵只是介绍一种思想。
🔔核心转储
是否有一个疑问,31个信号的默认处理方式都是结束进程,并且还可以自定义处理方式,那么为什么要这么多信号呢?一个信号不就行了吗?
- 重要的不是产生信号的结果,而是产生信号的原因。
- 所有出现异常的进程,必然是收到了某一个信号。
在man的7号手册中介绍了信号的名称,对应的编号,默认处理方式,以及产生该信号的原因。
- 我们可以根据这个表找到不同信号产生所对应的不同原因。
以信号2和3为例,他两的默认处理方式一个是Term,一个是Core。
- Term和Core的结果都是结束进程。
那么这两个方式的区别在哪里呢?
- Term方式仅仅是结束进程,结束了以后就什么都不干了。
- 但是Core不仅结束进程,而且还会保存一些信息。
在数据越界非常严重的时候,该进程会接收到SIGSEGV信号,来结束进程。
- 11号信号的默认处理方式是Core。
在云服务器上,默认情况下是看不到Core退出的现象的,这是因为云服务器关闭了core file选项:
- core file size(红色框)的大小是0,意味着这个选项是关闭的。
- 从这里还可以看到别的关于这个云服务器的信息,比如能够打开的最多文件个数,管道个数,以及栈的大小等等信息。
为了能够看到Core方式的明显现象,我们需要将core file选项打开:
此时该选项就打开了,表示的意思就是核心转储文件的大小是1024个数据块。
- 再运行数据越界的程序时,同样会收到SIGSEGV信号停止。
- 但是在当前目录下会多出一个文件,如上图中的绿色框。
- core.1739:被叫做核心转储文件,其中后缀1739是接收到该信号进程的pid值。
对于一个奔溃的程序,我们最关心的是它为什么崩溃,在哪里崩溃?
- 当进程出现异常的时候,将进程在对应的时刻,在内存中的有效数据转储到磁盘中-------核心转储。
- 核心转储的文件我们可以拿着它进行调试,快速定位到出现异常而崩溃的位置。
- 使用gdb调试我们的可执行程序。
- 调试开始后,输入core-file core.pid值,表明调试核心转储文件。
- 此时gdb就会直接定位到产生异常的位置。
这就是核心转储的重要意义,它相比Term方式,能够让我们快速定位出现异常的位置。
篇幅有限,下篇文章再接着介绍。
相关文章:

【Linux学习】信号——预备知识 | 信号产生 | 核心转储
🐱作者:一只大喵咪1201 🐱专栏:《Linux学习》 🔥格言:你只管努力,剩下的交给时间! 信号🔔信号🎵预备知识🎵信号处理方法的注册🔔信号…...

2023中国程序员薪酬报告出炉,你拖后腿了吗?
程序员薪资高已是公认的事实,但是具体高到什么程度呢?近期,全球人力服务公司 Michael Page Internatioal 就发布了《2023 中国大陆薪酬报告》,揭示了中国程序员的薪酬情况。 该报告中一共调研了国内 7 个行业以及 6 大城市不同职…...

Mac下Python3安装及基于Idea开发
本篇文章带大家基于Mac OS操作系统,下载、安装Python环境,并基于Idea编写第一个Demo。 Python3安装 访问Python官网:https://www.python.org/。找到“Download”菜单,点击下载: 此处下载的为Mac的安装包,…...

2017年 团体程序设计天梯赛——题解集
前言: Hello各位童学大家好!😊😊,茫茫题海你我相遇即是缘分呐,或许日复一日的刷题已经让你感到疲惫甚至厌倦了,但是我们真的真的已经达到了我们自身极限了吗?少一点自我感动…...

“唯一靶点”的华堂宁会成控糖爆品吗?
一上市,两次“断货”的货华堂宁有爆品那味儿了。 2022年10月28日华领医药-B(02552.HK)公告华堂宁(多格列艾汀)正式进入商业化,一周后各个渠道便进入到了断货和限售的状态。 对于一个不在传统九大降糖药品…...

Spring《三》DI依赖注入
🍎道阻且长,行则将至。🍓 上一篇:Spring《二》bean的实例化与生命周期 下一篇:敬请期待 目录一、setter注入🍉1.注入引用数据类型2.注入简单数据类型二、构造器注入🍊1.注入引用数据类型2.简单数…...

leetcode 面试题 17.06. 2出现的次数
编写一个方法,计算从 0 到 n (含 n) 中数字 2 出现的次数。 示例: 输入: 25 输出: 9 解释: (2, 12, 20, 21, 22, 23, 24, 25)(注意 22 应该算作两次) 该问题用的方法数数组dp,首先我通过总结规律写出了相关的code。使用一个dp数组记录10i10^i10i以内会出…...

CMake入门教程【基础篇】5.configure_file构建配置
configure_file配置文件 文章目录 知识点实例代码目录代码实现知识点 configure_fileconfigure_file 源文件转换为目标文件 实例 代码目录 |-📁prj4 |-- 🎴CMakeLists.txt |-- 🎴TutorialConfig.h.in |-- 🎴tutorial.cxx 代码实现 /prj4/CMakeLists.…...

软件开发可行性分析——健康食谱小程序
关于软件开发可行性分析先给大家介绍下面几个关键点: 什么是可行性分析? 检查并确定是否值得为项目或产品投入时间、金钱和资源。这样的评估活动称为“可行性分析”。 为什么要进行可行性分析? 在软件项目开发过程中,只要资源…...

ShuffleNet V1 对花数据集训练
目录 1. shufflenet 介绍 分组卷积 通道重排 2. ShuffleNet V1 网络 2.1 shufflenet 的结构 2.2 代码解释 2.3 shufflenet 代码 3. train 训练 4. Net performance on flower datasets 1. shufflenet 介绍 shufflenet的亮点:分组卷积 通道重排 mobilenet …...

测试人员转型是大势所趋:我的10年自动化测试经验分享
做测试十多年,有不少人问过我下面问题: 现在的手工测试真的不行了吗? 测试工程师,三年多快四年的经验,入门自动化测试需要多久? 自学自动化测试到底需要学哪些东西? 不得不说,随着行…...

Pandas高级操作,建议收藏(一)
在数据分析和数据建模的过程中需要对数据进行清洗和整理等工作,有时需要对数据增删字段。下面为大家介绍Pandas对数据的复杂查询、数据类型转换、数据排序的使用。 复杂查询 实际业务需求往往需要按照一定的条件甚至复杂的组合条件来查询数据,接下来为大家介绍如何…...

ASIC-WORLD Verilog(1)一日Verilog
写在前面 在自己准备写一些简单的verilog教程之前,参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。 这是网站原文&…...

数据治理工具项目投标书技术部分-V1.6
本资料来源公开网络,仅供个人学习,请勿商用,如有侵权请联系删除 项目背景 二、项目目标 提供一套后勤数据治理工具部署文件及配套文档,主要技术指标如下: (1)具备数据抽取转换装载、元数据管理、…...

ARMv8如何读取cache line中MOESI 状态以及Tag信息(tag RAM dirty RAM)
本文以Cortex-A53处理器为例,通过访问 处理器中的内部存储单元(tag RAM和dirty RAM),来读取cache line 中的MOESI信息。 Cortex-A53提供了一种通过读取一些系统寄存器,来访问Cache 和 TLB使用的一些内部存储单元的机制…...

学习通学习--脚本
刷客就爱学学习-首页 (xxbwk.top) 所有科目答案可以网上找超星尔雅学习通《形势与政策》2023年春章节测试答案 (3gmfw.cn) 学习通全部答案 萌面人 – 萌面人官网 (mengmianren.com) 自动答题教程 想要使用自动答题功能,只需要一个配置项就可以让OCS脚本拥有自动答…...

C盘的深度清理
随着反复安装和移除软件,c盘虽然给了80或者100G的空间,也经不住垃圾文件的堆积。居然只剩下几兆空间了。真是可气,某些软件虽然移除了。但是他们不负责自己产生的文件夹和文件的深度清理。 1. 清理系统的垃圾 2. 移动或者清理大文件。 某…...

43掌握自动化运维工具 Puppet 的基本用法,包括模块编写、资源管理
Puppet是一种自动化配置管理工具,可以自动化地部署、配置和管理大规模服务器环境。本教程将介绍Puppet的基本用法,包括模块编写和资源管理。 模块编写 在Puppet中,模块是一组相关的类、文件和资源的集合。模块可以用于部署和配置应用程序、服…...

【新2023Q2押题JAVA】华为OD机试 - 硬件产品销售方案
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:硬件产品销售方案 题目描述 …...

three.js实现3d球体树状结构布局——树状结构的实现
目录系列文章安装依赖基本分析实体类场景相机渲染器辅助线环境光点光源球形几何体球形几何体的材质线几何体线几何体的材质物体文本轨道控制实现效果实现源码参考文档系列文章 three.js实现3d球体树状结构布局——添加入场、出场、点击放大等动画 安装依赖 npm i three three…...

ChatGPT大解密:带您探讨机器学习背后的秘密、利用与发展
一、什么是机器学习?二、ChatGPT 的运作原理三、ChatGPT 生活利用1、自然语言处理2、翻译3、自动回复四、ChatGPT vs 其他相关技术五、ChatGPT 的未来1、未来发展2、职业取代3、客服人员4、翻译人员5、语言学家6、机遇与挑战六、结语这篇文章,将带着各位…...

3ds max2024带来了什么新功能(一)
文章目录1、安装2、操作界面3、快捷键(不冲突了)4、 修改器列表(可以搜索了)5、超级阵列功能(Array)6、超级布尔1、安装 传说3dmax2024有很多牛叉的改进二话不说,先安装按起来!这个…...

HNU-电路与电子学-实验3
实验三 模型机组合部件的实现(二)(实验报告格式案例) 班级 计XXXXX 姓名 wolf 学号 2021080XXXXX 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能&am…...

Hadoop MapReduce各阶段执行过程以及Python代码实现简单的WordCount程序
视频资料:黑马程序员大数据Hadoop入门视频教程,适合零基础自学的大数据Hadoop教程 文章目录Map阶段执行过程Reduce阶段执行过程Python代码实现MapReduce的WordCount实例mapper.pyreducer.py在Hadoop HDFS文件系统中运行Map阶段执行过程 把输入目录下文件…...

GitLab CI/CD 新书发布,助企业降本增效
前言 大家好,我是CSDN的拿我格子衫来, 昨天我的第一本书《GitLab CI/CD 从入门到实战》上架啦,这是业内第一本详细讲解GitLab CI/CD的书籍。 历经无数个日夜,最终开花结果。感触良多,今天就借这篇文章来谈一谈这本书的…...

【分享】如何写出整洁的代码?
文章目录前言1.为什么要保持代码整洁?1.1 所以从一开始就要保持整洁1.2 如何写出整洁的代码?2.命名3.类3.1单一职责3.2 开闭原则3.3 内聚4.函数4.1 只做一件事4.2 函数命名4.3 参数4.4 返回值4.5 怎样写出这样的函数?4.6 代码质量扫描工具5.测试5.1 TDD5.2 FIRST原则5.3 测试…...

视频剪辑:教你如何调整视频画面的大小。
大家应该都会调整图片的大小吧,那你们会调整视频画面的大小吗?我想,应该会有人不还不知道要调整的吧,今天就让小编来教大家一个方法怎样去调整视频画面的大小尺寸。 首先,我们要有以下材料: 一台电脑 【…...

操作系统概述
Overview Q1(Why):为什么要学操作系统?Q2(What):到底什么是操作系统?Q3(How):怎么学操作系统? 一.为什么要学操作系统? 学习操作系统…...

记录重启csdn
有太多收藏的链接落灰了,在此重启~ 1、社会 https://mp.weixin.qq.com/s/Uq0koAbMUk8OFZg2nCg_fg https://mp.weixin.qq.com/s/yCtLdEWSKVVAKhvLHxjeig https://zhuanlan.zhihu.com/p/569162335?utm_mediumsocial&utm_oi938179755602853888&ut…...

蓝牙耳机哪个品牌质量最好最耐用?蓝牙耳机排行榜10强推荐
现今,外出佩戴蓝牙耳机的人越来越多,各大品牌厂商对于蓝牙耳机各种性能的设计也愈发用心。那么,无线耳机哪个品牌音质好?下面,我来给大家推荐几款质量好的无线蓝牙耳机,可以当个参考。 一.南卡…...