Linux——进程信号(一)
1.信号入门
1.1生活中的信号
什么是信号?
结合实际红绿灯、闹钟、游戏中的"!"等等这些都是信号。
以红绿灯为例子:
一看到红绿灯我们就知道:红灯停、绿灯行;我们不仅知道它是一个红绿灯而且知道当其出现不同的状况时我们应该做出怎么样的行为去应对。
识别=认识+行为产生
对于红绿灯:
我们之前受过的教育,让我们能够识别这个红绿灯(即使它还未出现在我们眼前),也就是说你能识别"红绿灯"
当绿灯量了后我们不一定要直接走,也可以等一会再走(也就是说接收信号后我们不一定要立刻产生对应的行为)
当红灯亮了之后,我们可以玩手机,要记得这个时候还是红灯
当红或绿灯亮时我们可以做出其他行为,也可以忽略红绿灯
对于信号来说(进程看待信号的方式)
- 在没有发生的时候,进程已经知道发生的时候该怎么处理了
- 信号进程能够认识,在遇见信号之前有人在"大脑"中设置了识别特定信号的方式(进程能够识别一个信号并处理)
- 信号到来的时候,进程正在处理更重要的事情,这时进程暂时不能立即处理到来的信号,进程必须暂时将到来的信号进行临时保存
- 信号到了进程可以不立即处理,等在合适的时候处理
- 信号的产生是随时产生的,进程无法准确预料,所以信号是异步发送的(信号的产生是由别人(用户、进程)产生的,在进程收到信号之前,进程一直在忙自己的事情,并发在跑的)
为什么?
停止、删除...系统要求进程要有随时响应外部信号的能力,随后做出反应
我们学习信号是学习它的整个生命周期,在进程运行期间信号的生命周期分为以下几个阶段:
准备、信号的产生、信号的保存、信号的处理
1.2Linux中的信号
用户输入命令,在Shell下启动一个前台进程
用户按下 Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
前台进程因为收到信号,进而引起进程退出
#include<iostream>
#include<unistd.h>using namespace std;int main()
{while(1){cout<<"i am main process..."<<endl;sleep(1);}return 0;
}
- Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
- Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
- 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
1.3信号的概念
信号是进程之间事件异步通知的一种方式,属于软中断
在Linux操作系统中命令:kill -l可以查看系统定义的信号列表
上面的数字和名字都可以标识信号,名字其实就是宏;总共62个信号(没有0、32、33信号)
其中:1~31号是普通信号 34~64是实时信号
我们这里只学普通信号
上面我们说到:每个信号都有一个编号和一个宏定义名称,这些宏定义名称可以在signal.h中找到,这些信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal
根据我们对Linux的了解,信号存放在哪呢?既然信号是给进程的,而进程又是通过内核数据结构来管理的,那么我们可以推断出,信号是放在进程的task_struct结构体中
既然他是在PCB中,如果创建31变量把信号全存进去,那就太浪费了;进程中信号的状态分为有或者没有,那么我们可以大胆的推断:把31个信号存放在一个只有32位整型变量中,每一个比特位都代表一个信号。
比特位的位置,代表信号的编号
比特位的内容:代表进程是否收到信号,1表收到,0表没收到
那么问题来了,内核数据结构的修改,这是由谁来完成的?
毫无疑问,是操作系统,毕竟task_struct就是由它来维护的,只有OS才有权利去修改它
所以说,无论哪个信号,最后都是经由OS之手发送给进程的(发送指的是修改进程PCB中存放信号那个变量的比特位)
信号发送的本质就是:在修改进程PCB中信号位图
信号处理常见方式概览:
1. 忽略此信号。
2. 执行该信号的默认处理动作。
3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。
2.信号的产生
kill系统调用接口
kill -9 pid //杀死进程
kill -2 pid //终止进程
2.1通过按键产生信号
通过键盘上按一些热键,来给进程发送相应的信号,比如上面的ctrl c,它产生的是2号信号;ctrl \产生的则是3号信号
如何进行自定义处理信号呢?
信号捕捉
signal
sighandler_t 返回值 signum几号信号
handler自定义方法 typedef void (*sighandler_t)(int)
#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
using namespace std;void handler(int signo)
{cout<<"get a sig,number is:"<<signo<<endl;
}int main()
{signal(SIGINT,handler);while(1){cout<<"I am activing...,pid:"<<getpid()<<endl;sleep(1);}return 0;
}
设置信号捕捉只需要一次
signal调用完了handler方法不会被立即执行,这里只是设置对应的信号的处理方法
这时候我们发现Ctrl C杀不了这个进程了,这是因为我们捕捉信号后(信号的处理方式是自定义处理信号)对于2号信号的处理动作变成执行handler方法
2.2调用系统函数向进程发信号
系统调用和命令的名字一样,man 2 kill 查看
pid_t pid :要给发信号的pid
int sig:要发送的信号编号
返回值:发送成功返回0,失败返回-1
该系统调用是一个进程给另一个进程发送指定信号,可以向任意进程发送任意信号
#include<iostream>
#include<unistd.h>
#include <string.h>
#include<signal.h>
#include<sys/types.h>
using namespace std;int main(int argc, char *argv[])
{if(argc != 3){cout << "Usage: " << argv[0] << " -signumber pid" << endl;return 1;}int signumber = stoi(argv[1]+1);int pid = stoi(argv[2]);int n = kill(pid, signumber);if(n < 0){cerr << "kill error, " << strerror(errno) << endl;}return 0;
}
raise给自己发送任意信号
#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;
int main()
{cout<<"开始运行..."<<endl;sleep(1);int n = raise(2);cout<<"运行结束"<<endl;return 0;
}
abort发信号终止自己(指定信号 6号:SIGABRT)
2.3软件条件产生信号
比如管道那时候,把读端关闭(没有了写的条件),那么写端也会收到信号关闭(SIGPIPE)
再举个闹钟的例子:
alarm
unsigned int 返回值 :上一个闹钟剩余秒数
#include<iostream>
#include<unistd.h>
#include <string.h>
#include<signal.h>
#include<sys/types.h>
using namespace std;
int g_cnt = 0;
void handler(int sig)
{std::cout << "get a sig: " << sig << " g_cnt: " << g_cnt << std::endl;unsigned int n = alarm(5);cout << "还剩多少时间: " << n << endl;exit(0);
}int mian()
{//设定一个闹钟signal(SIGALRM,handler);alarm(5);while(true){g_cnt++;}int cnt = 0;while(true){sleep(1);cout << "cnt : " << cnt++ << ", pid is : "<< getpid() << endl; //IO其实很慢if(cnt == 2){int n = alarm(0); // alarm(0): 取消闹钟cout << " alarm(0) ret : " << n << endl;}}return 0;
}
2.4硬件异常产生信号
最后一种产生信号的方式:异常
硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
除零错误引发异常 8)SIGFPE
void handler(int sig)
{cout<<"get a sig:"<<sig<<endl;exit(1);
}int main()
{signal(SIGFPE,handler);int a = 10;a /= 0;while(1){sleep(1);}return 0;
}
野指针 11)SIGSEGV
void handler(int sig)
{cout<<"get a sig:"<<sig<<endl;exit(1);
}
int main()
{signal(SIGSEGV, handler);sleep(1);int *p = NULL;*p = 100;while(1){sleep(1);}return 0;
}
2.5关于信号产生的各种情况的理解
2.5.1键盘产生信号——硬件中断
键盘产生信号:
a.按键按下了 b.哪些按键按下了 c.字符输入(字符设备),组合键输入
字符输入:abcd 组合键输入:CTRL C 键盘驱动和OS联合解释的
OS怎么知道键盘输入了什么数据呢? 硬件中断的技术
键盘 将电信号传到CPU的针脚上 通过硬件中断转化位软信号电流,CPU再传给OS,OS通过中断向量表中以中断号去检索解释 判定是字符就放到缓冲区里面,是命令(ctrl+c)解释为信号
2.5.2异常(硬件)产生信号——除零、解引用空指针
除零
void handler(int sig)
{cout<<"get a sig:"<<sig<<endl;
}int main()
{signal(SIGFPE,handler);int a = 10;a /= 0;while(1){sleep(1);}return 0;
}
如果捕捉除零产生的信号不用exit退出进程的话,随着CPU时间片的轮转就会再次被调到
CPU中只有一份寄存器,但是寄存器的内容属于当前进程的硬件上下文
当进程被切换的时候,就有无数次的状态寄存器被保存和恢复的过程
而除0操作导致溢出位置一的数据还会被恢复到CPU中
所以每次恢复的时候,操作系统就会识别到,并且给对应的进程发送SIGFPE信号,这就导致了上面不停调用自定义处理函数,不停地打印
如何理解除0?
进行计算的是CPU这个硬件
CPU内部有寄存器,状态寄存器(位图)有对应的状态标记位、溢出标记位,OS会自动进行计算完后的检测,如果溢出标记位是1,CPU会告诉OS有溢出的问题,OS就会来查看问题并且找到出问题的那个进程,并且将所识别到的硬件错误包装成信号发送给目标进程,本质就是操作系统去直接找到这个进程的task_struct并向该进程写入8信号
野指针异常
对于野指针异常来说,实际上也是CPU、OS共同配合的结果
我们所说的野指针指的是虚拟地址,虚拟地址和物理地址的转化如果是成功的,那么就不会抛出野指针异常,如果是失败的,那么CPU就会告诉OS出问题了,OS就会找到那个进程并发出11号信号,让他终止
虚拟地址与物理地址之间的映射是由一个叫MMU的硬件完成的,他是一种负责处理CPU的内存访问请求的计算机硬件
页表实际上是页表和MMU的结合,而MMU位于CPU中。在讨论中一般会简化称为页表。
当对空指针解引用的时候,MMU会拒绝这种操作,从而产生异常标志
操作系统拿到MMU产生的异常以后就会给对应的进程发送SIGSEGV信号
3.信号的保存
上面我们提到:如果当前进行更重要的工作,那么它会将信号进行保存,等到合适的时候再做处理,那么信号的保存机制是怎么实现的呢?
3.1信号常见概念
- 实际执行信号的处理动作称为信号递达(Delivery)
- 信号从产生到递达之间的状态,称为信号未决(Pending)。
- 进程可以选择阻塞 (Block )某个信号
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
注意: 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
进程PCB中有一张位图:用来表示有无收到指定信号
还有一张位图:比特位的位置依然是信号编号,比特位的内容表示是否阻塞该信号
如:1号位图4号信号位置为1,2号位图4号信号位置为0,4号信号无阻塞通畅递达
1号位图6号信号位置为1,2号位图6号信号位置为1,6号信号阻塞,不递达(除非未来解除阻塞)
3.2在内核中的表示
记住三张表
block表:位图 pending表:位图 handler表:函数指针数组
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。
3.3三张表匹配的操作和系统调用
在此之前,了解一下sigset_t
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。
因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的
3.3.1信号集操作函数
man sigemptyset:
sigset_t set:信号集变量
int signum:信号编号
返回值:成功返回0,失败返回-1。
函数sigemptyset:
初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号
函数sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号
sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号
3.3.2sigprocmask
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
man sigprocmask
int how: 修改方式,有三个选项
set:
我们设置号的sigset_t变量
oldset:
输出型参数,将修改之前的信号屏蔽字保存到oldset
返回值:sigprocmask函数调用成功返回0,出错返回-1
3.3.3pending的系统调用sigpending
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
man sigpending
4.代码
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cassert>
#include <sys/wait.h>
using namespace std;void PrintSig(sigset_t &pending)
{cout<<"Pending bitmap:"<<endl;for(int i=31;i>0;i--)//i为比特位,也是信号编号{if(sigismember(&pending,i)){cout<<"1";}else{cout<<"0";}}cout<<endl;
}
void handler(int sig)
{sigset_t pending;sigemptyset(&pending);int n = sigpending(&pending);//正在处理2号信号assert(n==0);cout<<"递达中..."<<endl;PrintSig(pending);cout<<sig<<"号信号被递达处理"<<endl;}int main()
{//对2号信号进行自定义捕捉signal(2,handler);sigset_t block,o_block;//屏蔽2号信号sigemptyset(&block);sigemptyset(&o_block);sigaddset(&block,2);//进入内核int n = sigprocmask(SIG_SETMASK,&block,&o_block);assert(n==0);cout<<"block 2 signal success"<<"pid:"<<getpid()<<endl;int cnt = 0;while(1){sigset_t pending;sigemptyset(&pending);n = sigpending(&pending);assert(n == 0);//打印pending位图中收到的信号PrintSig(pending);cnt++;//解除对2号信号的屏蔽if(cnt==10){n = sigprocmask(SIG_UNBLOCK,&block,&o_block);assert(n==0);}sleep(1);}return 0;
}
相关文章:

Linux——进程信号(一)
1.信号入门 1.1生活中的信号 什么是信号? 结合实际红绿灯、闹钟、游戏中的"!"等等这些都是信号。 以红绿灯为例子: 一看到红绿灯我们就知道:红灯停、绿灯行;我们不仅知道它是一个红绿灯而且知道当其出现不同的状况…...

centos9 stream在线安装NVIDIA驱动(rockylinux9.4也成功安装nvidia驱动)
Install NVIDIA Drivers on CentOS Stream 9(rockylinux9.4成功) 主板为技嘉mz72-hb2 显卡为4090 一.Disable Secure Boot From the BIOS 二.Enabling the EPEL Repository on CentOS Stream 9 1.update the DNF package repository cache sudo dnf …...

springmvc不同格式的参数解析
参数解析 application/x-www-form-urlencoded格式 这种格式就是传统的表单提交格式,就是一个个的键值对,会进行url编码,使用springmvc接收时使用RequestParam来进行接收,与传入的字段一一对应,此时使用的参数处理器是R…...

Unity3D让BoxCollider根据子物体生成自适应大小
系列文章目录 unity工具 文章目录 系列文章目录unity工具 👉前言👉一、编辑器添加👉二、代码动态添加的方法(第一种)👉三、代码动态添加的方法(第二种)👉四、重新设置模型的中心点👉壁纸分享👉…...

WSL 2 installation is incomplete.
使用的wsl2版本很旧,因此需要手动更新。 https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi...

Servlet的request对象
request对象的继承关系 1.HttpServletRequest接口继承了ServletRequest接口,对其父接口进行了扩展,可以处理满足所有http协议的请求 2.HttpServletRequest和ServletRequest都是接口,不能创建对象,因此在tomcat底层定义实现类并创…...

蓝桥杯-合并数列
小明发现有很多方案可以把一个很大的正整数拆成若干正整数的和。他采取了其中两种方案,分别将它们列为两个数组 {a1, a2, …, an} 和 {b1, b2, …, bm}。两个数组的和相同。 定义一次合并操作可以将某数组内相邻的两个数合并为一个新数,新数的值是原来两…...

《web应用技术》第9次课后作业
一、将前面的代码继续完善功能 1、采用XML映射文件的形式来映射sql语句; 2、采用动态sql语句的方式,实现条件查询的分页。 二、学习git的使用。 1、每个小组将自己的项目上传到gitee,学会协作开发; 2、学会从gitee上拉取项目…...

FRAUDARCatchSync算法简介
参考:https://blog.51cto.com/u_15127663/2778705 1. 背景 Fraudar 要解决的问题是:找出社交网络中最善于伪装的虚假用户簇。虚假用户会通过增加和正常用户的联系来进行伪装,而这些伪装(边)会形成一个很密集的子网络,可以通过定义…...

刷题之将有序数组转换成二叉搜索树(leetcode)
将有序数组转换成二叉搜索树 正常递归,中序遍历 递归经常会把自己绕晕,还是得画图分析 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(null…...

K-means聚类模型教程(个人总结版)
K-means聚类是一种广泛应用于数据挖掘和数据分析的无监督学习算法。它通过将数据点分成K个簇(cluster),使得同一簇内的数据点之间的相似度最大,不同簇之间的相似度最小。本文将详细介绍K-means聚类算法的背景、基本原理、具体实现…...

android怎么告诉系统不要回收
在Android中,如果你想告诉系统不要回收你的应用程序,可以通过设置Activity的属性来实现。你可以设置android:configChanges属性,指定在哪些配置更改时不重新创建Activity。 例如,如果你想指示系统在屏幕方向更改时不要重新创建Ac…...

【FAQ】HarmonyOS SDK 闭源开放能力 —IAP Kit(2)
1.问题描述: 应用内支付IAP Kit和Payment Kit的区别以及适用场景? 解决方案: IAP Kit是四方支付,仅支持在线虚拟商品,如会员,游戏钻石等,双框架支持全球,目前单框架暂时只支持国内…...

ubuntu设置root开机登录,允许root用户ssh远程登录
ubuntu与centos系统不同,默认root开机不能登录。 1、输入一下命令创建root密码,根据提示输入新密码 sudo passwd root 2、打开gdm-autologin文件,将auth required pam_succeed_if.so user ! root quiet_success这行注释掉,这行就…...

Web测试面试题(二)
一:简述HTTP协议的状态码包含哪些? 2XX,表示成功 3XX,表示重定向 4XX,表示客户端错误 5XX,表示服务器错误 二:HTTP和HTTPS的区别? 《1》安全性上的区别: HTTPS&#x…...

VBA宏指令写的方法突然不能用了
背景:项目组有个自动化测试项目,实在excel中利用VBA开发的;时间比较久远,我前面的哥们走后,这个软件一直在用,最近系统不知道是不是更新的缘故;有些代码除了问题; 先上源码: Dim Conn As Object, Rst As Object Dim sqlStr$ Dim str_start_SN$, str2$ str_start_SN …...

第13章 Python建模库介绍
以下内容参考自https://github.com/iamseancheney/python_for_data_analysis_2nd_chinese_version/blob/master/%E7%AC%AC05%E7%AB%A0%20pandas%E5%85%A5%E9%97%A8.md 《利用Python进行数据分析第2版》 用以学习和记录。 本书中,我已经介绍了Python数据分析的编程基…...

IP学习——ospf1
OSPF:开放式最短路径优先协议 无类别IGP协议:链路状态型。基于 LSA收敛,故更新量较大,为在中大型网络正常工作,需要进行结构化的部署---区域划分、ip地址规划 支持等开销负载均衡 组播更新 ---224.0.0.5 224.0.0.6 …...

别说废话!说话说到点上,项目高效沟通的底层逻辑揭秘
假设你下周要在领导和同事面前汇报项目进度,你会怎么做?很多人可能会去网上搜一个项目介绍模板,然后按照模板来填充内容。最后,汇报幻灯片做了 80 页,自己觉得非常充实,但是却被领导痛批了一顿。 这样的境…...

前后端编程语言和运行环境的理解
我已重新检查了我的回答,并确保信息的准确性。以下是常用的编程语言,以及它们通常用于前端或后端开发,以及相应的框架和运行环境: 前端开发 JavaScript 框架:React, Angular, Vue.js, Ember.js, Backbone.js运行环境:Web 浏览器HTML (HyperText Markup Language) 不是编…...

一顿五元钱的午餐
在郑州喧嚣的城市一隅,藏着一段鲜为人知的真实的故事。 故事的主角是一位年过半百的父亲,一位平凡而又伟大的劳动者。岁月在他脸上刻下了深深的痕迹,但他眼神中闪烁着不屈与坚韧。 他今年52岁,为了给远在家乡的孩子们一个更好的…...

【前端每日基础】day60——TDK三大标签及SEO引擎优化
TDK 是指 Title(标题)、Description(描述)、Keywords(关键词)这三个网页的重要元信息标签,对于 SEO(搜索引擎优化)至关重要。下面是它们的作用和 SEO 优化建议࿱…...

vscode添加代办相关插件,提高开发效率
这里写目录标题 前言插件添加添加TODO Highlight安装TODO Highlight在项目中自定义需要高亮显示的关键字 TODO Tree安装TODO Tree插件 单行注释快捷键 前言 在前端开发中,我们经常会遇到一些未完成、有问题或需要修复的部分,但又暂时未完成或未确定如何处…...

JS对象超细
目录 一、对象是什么 1.对象声明语法 2.对象有属性和方法组成 二、对象的使用 1.对象的使用 (1)查 (2)改 (3)增 (4)删(了解) (5…...

远程PLC、工控设备异地调试,贝锐蒲公英异地组网方案简单高效
北京宇东宁科技有限公司专门提供非标机电设备,能够用于金属制品的加工制造。设备主要采用西门子的PLC作为控制系统,同时能够连接上位机用于产量、温度、压力、电机运行数据的监控,以及工厂的大屏呈现需求。目前,客户主要是市场上的…...

【算法】梦破碎之地---三数之和
相信大家都有做过两数之和, 题目链接: 15. 三数之和 - 力扣(LeetCode) 在文章的开始让我们回顾一下三数之和吧! 题目描述: 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], …...

c语言如何将一个文本内容复制到另外一个文本里
c语言如果要把一个文本文件的文件复制到另外一个文件里,代码如下 #include<stdio.h>int main() {FILE *fp1,*fp2;char a;fp1fopen("D://cyy//aaa.txt","r");fp2fopen("ccc.cpu","w");while(a!EOF){afgetc(fp1);fput…...

JavaScript基础(九)
冒泡排序 用例子比较好理解: var arry[7,2,6,3,4,1,8]; //拿出第一位数7和后面依次比较,遇到大的8就换位,8再与后面依次比较,没有能和8换位的数,再从下一位2依次与下面的数比较。 console.log(排列之前:arry); for (…...

决策树最优属性选择
本文以西瓜数据集为例演示决策树使用信息增益选择最优划分属性的过程 西瓜数据集下载:传送门 首先计算根节点的信息熵: 数据集分为好瓜、坏瓜,所以|y|2根结点包含17个训练样例,其中好瓜共计8个样例,所占比例为8/17坏…...

NER 数据集格式转换
NER 数据集格式 格式一 某些地方的数据和标签拆成两个文件了 sentences.txt 如 何 解 决 足 球 界 长 期 存 在 的 诸 多 矛 盾 , 重 振 昔 日 津 门 足 球 的 雄 风 , 成 为 天 津 足 坛 上 下 内 外 到 处 议 论 的 话 题 。 该 县 一 手 抓 农 业…...