【Linux】进程信号 --- 信号产生

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 一、通过终端按键产生信号
      • 1.1 Ctrl + /
      • 1.2 Ctrl + z
  • 二、通过kill命令产生信号
  • 三、通过系统调用接口产生信号
      • 3.1 kill
      • 3.2 raise
      • 3.3 abort
  • 四、硬件异常产生信号
      • 4.1 除 0 错误导致异常
      • 4.2 状态寄存器
      • 4.3 野指针异常
  • 五、软件条件产生信号
      • 5.1 alarm 函数
  • 六、核心转储 core dump
      • 6.1 知识回顾
      • 6.2 打开与关闭核心转储
      • 6.3 核心转储的作用

一、通过终端按键产生信号

1.1 Ctrl + /

在上篇博客中(点击跳转),我们已经验证了Ctrl + c是向前台进程发送2号信号SIGINT来中断程序。

这篇再介绍一个组合键:Ctrl + \。它其实是向前台进程发送3号信号SIGQUIT来终止程序,同时会产生一个core文件。我们可以使用捕捉信号函数signal来验证。

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signum)
{// 修改的行为:打印信号编号后退出cout << "信号编号为:" << signum << endl;exit(1);
}int main()
{// 捕捉3号信号signal(SIGQUIT, myhandler);while (true){cout << "我是一个进程,我在做死循环操作" << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

其原理再简单重复一遍:当按下ctrl + \,操作系统识别到键盘上有数据,触发了硬件中断,CPU收到中断请求后,会暂停当前执行的程序,保存当前状态,并根据中断号来调用对应硬件的方法。由于操作系统识别到是特殊控制字符,就将其转化为3号信号发送给前台进程

1.2 Ctrl + z

Ctrl + z会向前台进程发送19号信号SIGSTOP。当进程收到此信号时,它会立即停止运行。

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;int main()
{while (true){cout << "我是一个进程,我在做死循环操作" << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

需要注意的是:19号信号SIGSTOP不能使用signal函数捕捉。类似地,9号信号SIGKILL用于立即终止一个进程,并且也不能被signal函数捕捉。

二、通过kill命令产生信号

kill -n <pid>
# n是信号编号

以以下代码为例:

#include <iostream>
#include <unistd.h>
using namespace std;int main()
{while (true){cout << "进程:" << getpid() << ", 我在做死循环啦~" << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

三、通过系统调用接口产生信号

3.1 kill

系统调用kill函数是用于向进程发送某种信号,其函数原型如下:

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

参数解释:

  • pid:要发送信号的进程pid
  • sig:要发送的信号编号,可以是标准信号如SIGINTSIGKILL,也可以是用户自定义的信号。
  • 返回值:成功时,返回0;失败时,返回-1,并设置errno来指示错误的原因。

我们可以使用这个kill系统调用接口来模拟实现一个kill命令。首先kill命令必须是如下形式:

kill -信号编号 进程pid

那么这里就可以巧用main函数的参数。main函数的第一个参数argc:表示字符指针数组当中的有效元素个数。argv:是一个字符指针数组(向量表),数组以NULL指针结尾,注意argv的第一个值原因是程序名。

因此,代码如下:

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <signal.h>
#include <sys/types.h>using namespace std;void Usage(string proc)
{cout << "格式:\n\t" << proc << " signum pid\n\n";
}// argv:程序名argv[0]、信号编号argv[1]、进程pid argv[2]
int main(int argc, char *argv[])
{// 如果是三个参数,可以杀掉用户指定进程if (argc == 3){int signum = stoi(argv[1]);pid_t pid = stoi(argv[2]);int n = kill(pid, signum);if (n != 0){perror("kill");exit(2);}}// 如果不是输入三个参数,就提醒用户输入格式else{Usage(argv[0]);exit(1);}return 0;
}

接下来我再写一个死循环代码程序如下:

#include <unistd.h>
#include <iostream>
using namespace std;int main()
{while (true){cout << "my pid is " << getpid() << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

3.2 raise

raise函数用于向当前进程发送一个信号。它的基本形式如下:

#include <signal.h>int raise(int sig);
  • sig:要发送的信号编号,可以是标准信号(如SIGINTSIGTERM等),也可以是用户自定义的信号。
  • 返回值:成功时,返回0;失败时,返回非0值。

这个函数可以这么理解:相当于一颗地雷,当程序“踩到”它,就会产生信号并执行。

例如:程序是死循环打印,当打印完3条消息后,就立马杀掉进程。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;int main()
{int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 9号信号raise(SIGKILL);}}return 0;
}

【程序结果】

在这里插入图片描述

这个函数通常搭配signal函数使用。因为有些信号终止程序不会像9号信号一样给killed提示,比方说2号信号SIGINT

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;int main()
{int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 2号信号raise(SIGINT);}}return 0;
}

【程序结果】

在这里插入图片描述

当加了signal函数后,就直观很多了。代码如下:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我得到了2号信号SIGINT" << endl;exit(1);
}int main()
{signal(2, handler);int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){// 2号信号raise(SIGINT);}}return 0;
}

【程序结果】

在这里插入图片描述

这个函数其实封装了系统调用接口kill,例如以上代码等价于kill(getpid(), 2);

3.3 abort

abort函数用于向自己发送一个6号信号SIGABRT,异常终止当前进程的执行。

函数原型如下:

#include <stdlib.h>void abort(void);

【代码样例】

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我得到了6号信号SIGABRT" << endl;exit(1);
}int main()
{signal(6, handler);int cnt = 3;while (true){cout << "My pid is " << getpid() << endl;sleep(1);cnt--;if (cnt == 0){abort();}}return 0;
}

【程序结果】

在这里插入图片描述

值得一提的是,abort函数即使在修改执行动作后(没有exit(int)),最后仍然会发送6号信号来退出进程。

因此,abort函数也是被封装的,相当于kill(getpid(), 6);

总的来说,系统调用中举例的这三个函数关系是:kill包含raiseraise 包含 abort,作用范围是在逐渐缩小的

在这里插入图片描述

四、硬件异常产生信号

4.1 除 0 错误导致异常

程序异常本质是进程收到了某种信号。

比方说,我们可以使用signal函数来捕捉除0异常的进程收到了几号信号。

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void handler(int signum)
{cout << "我收到了" << signum << "号信号" << endl;
}int main()
{for (int i = 1; i <= 31; i++){signal(i, handler);}printf("1 / 0 = %d\n", 1 / 0);return 0;
}

【程序结果】

在这里插入图片描述

从上我们发现:

  1. 操作系统发送8号信号SIGFPE
  2. 只发生了一次 除0异常,进程非但没退出,还在死循环打印。

首先大部分进程对信号的处理行为都是终止程序,这里没有终止程序是因为我们修改了系统默认的行为,在handler函数最后一行补上exit函数即可正常终止。

但非常奇怪的是,我们代码中只有一次除0异常啊,正常来说只需要打印一次就行了,即使进程没有退出,将进程阻塞不就好了。为什么会一直死循环打印呢?想要明白背后的原理,需要先认识一下状态寄存器

4.2 状态寄存器

代码中除0和野指针行为,操作系统是怎么知道的呢?

CPU中,存在很多寄存器,其中大部分主要用来存储数据信息,用于运算。除此之外,还存在一种特殊的 寄存器:状态寄存器。这个寄存器是一个位图结构。专门用来检测当前进程是否出现错误行为。如果有,就会把位图结构中对应的比特位置设置成1,意味着出现了异常。

在这里插入图片描述

当检测到状态寄存器中某个异常标志位被设置为1时,硬件(寄存器)会向CPU发送一个硬件中断,CPU收到来自硬件的中断信号后,又因为操作系统是这些硬件的“管理者”,它会暂停当前正在执行的进程。然后检查当前进程的上下文异常标志位的具体含义,以确定发生了什么样的异常。根据异常类型调用相应的异常处理程序。这些程序通常是预先定义好的。

所以现在就可以解释为什么进程收到来自操作系统的异常信号后,虽然我们修改了默认处理动作(不退出),但是会死循环打印的情况。这是因为操作系统一直检测当前进程的状态寄存器仍然处于异常状态,再加上不退出进程,此时进程一直在被调度运行,所以操作系统才会不断发送8号信号,才会死循环式的打印。

因此,异常信号被捕捉不是为了解决什么问题,而是让用户清除的知道程序是因为什么而挂掉的。

4.3 野指针异常

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <string>
#include <signal.h>
using namespace std;int main()
{int *p = nullptr;*p = 10;return 0;
}

【程序结果】

在这里插入图片描述

Segmentation fault是一个段错误,这是每个C/C++程序猿都会遇到的问题,因为太容易触发了,出现段错误问题时,操作系统会发送11SIGSEGV信号终止进程,这里不再证明演示。

在这里插入图片描述

在这里插入图片描述

那么 野指针问题是如何引发的呢?本质上就是:虚拟地址转化为物理地址失败。比方说:虚拟地址映射到不属于它的物理空间(越界访问)、对该物理空间没有写的权限等。具体解释如下:

  • 当代的MMU已经被集成在CPU内部,除此之外CPU还有一个寄存器,称为BadVAddrBad Virtual Address,坏虚拟地址)。这个寄存器的作用是在虚拟地址转换失败时,将引起异常的虚拟地址存储起来。

  • 所以,硬件(寄存器)会向CPU发送一个硬件中断,CPU收到来自硬件的中断信号后,又因为操作系统是这些硬件的“管理者”,它会暂停当前正在执行的进程。然后检查当前进程的上下文异常标志位的具体含义,以确定发生了什么样的异常。根据异常类型调用相应的异常处理程序。这些程序通常是预先定义好的。

五、软件条件产生信号

异常不仅由硬件产生,还可以由软件产生

比如我们之前的管道,如果一开始读写端都打开,但是关闭了读端,那么写端进程就会收到一个13号信号SIGPIPE。这就是一种软件异常。【Linux】进程间通信之匿名管道

5.1 alarm 函数

alarm函数是一个系统调用接口,用来设置一个定时器。其功能是:在指定的秒数后发送14号信号SIGALRM给调用进程。该信号的默认处理动作是终止当前进程。

原型如下:

#include <unistd.h>unsigned int alarm(unsigned int seconds);
  • seconds:指定的秒数,即多少秒后发送SIGALRM信号。如果参数为0,则任何已设置的定时器都会被取消,但不会产生信号。
  • 返回值:返回调用之前的剩余定时器时间。如果之前没有设置定时器,则返回0
  • aralm函数的原理:我们知道每个进程都可以使用alarm设置闹钟,所以操作系统中一定有大量的闹钟。所以操作系统要管理闹钟,所以闹钟就会用struct结构体描述,一定有进程pid(表示哪个进程设置的闹钟)等字段,然后用链表等数据结构管理起来。这样所谓的闹钟管理就变成了对链表等的增删查改。操作系统底层中alarm的底层所用的时间用的是时间戳,只要系统的当前时间大于等于里面设置的时间,就会发送信号。我们遍历链表的时候是比较浪费时间的。所以用一个小堆是最简单的。

【代码样例】

#include <iostream>
#include <unistd.h>
#include <stdlib.h>using namespace std;int main()
{// 设定一个5s的定时器alarm(5);int n = 1;while (true){cout << "我是一个进程,已经运行了 " << n++ << " 秒 PID: " << getpid() << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

那我们就可以在执行主要任务的同时,去定时完成其他任务了。例如:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>using namespace std;void work()
{cout << "我在完成其他任务" << endl;
}void handler(int signum)
{// 异常捕捉时,再设置一个定时器完成其他任务alarm(5);work();
}int main()
{// 设定一个5s的定时器alarm(5);// 信号捕捉signal(14, handler);int n = 1;while (true){// 主要任务cout << "我是一个进程,已经运行了 " << n++ << " 秒 PID: " << getpid() << endl;sleep(1);}return 0;
}

【程序结果】

在这里插入图片描述

六、核心转储 core dump

6.1 知识回顾

我们可以使用以下命令来看信号的详细信息手册

man 7 signal

在这里插入图片描述
我们可以注意到,常见的信号中大部分是终止Term(terminate)信号的。还有一些是暂停(Stop),继续(Cont),忽略(Ign)。

那么这个核心Core是什么东西呢?

在我们当时学习进程控制的时候,还有一个这个字段core dump标志我们还没提到过(【Linux】进程控制 )

在这里插入图片描述

当一个进程异常退出时(参看上面ActionCore信号),该core dump标志会被设置成1,并且会生成一个core.pid二进制文件,然后不再设置退出码(为0,因为这种情况下程序没有正常的执行路径结束)

我们可以写代码来验证一下:

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{pid_t pid = fork();if (pid == 0) // 子进程{int cnt = 500;while (cnt--){cout << "I am child process, pid:" << getpid() << ", cnt:" << cnt << endl;sleep(1);}// 打印5句话后子进程退出exit(1);}else // 父进程{int status = 0;pid_t rid = waitpid(pid, &status, 0);if (rid == pid){cout << "child quit info, rid:" << rid << ", exit code:" << ((status >> 8) & 0xff)<< ", exit signal:" << (status & 0x7f) << ", core dump:" << ((status >> 7) & 1)<< endl;}}return 0;
}

【程序结果】

在这里插入图片描述

为什么没有生成核心转储文件啊?难道是我们的环境有问题吗?

确实,当前环境确实有问题,因为它是云服务器,而云服务器中默认是关闭核心转储功能的。

6.2 打开与关闭核心转储

通过以下指令可以查看当前系统中的资源限制情况

 ulimit -a 

在这里插入图片描述

可以看到,当前系统中的核心转储文件大小为0,即不生成核心转储文件。

通过以下指令手动设置核心转储文件大小(打开核心转储文件)

ulimit -c <大小自己定>
# 关闭大小设置为0即可
# 重启xhell自动关闭

在这里插入图片描述

我们可以再来测试一下,是否dump core是否还为1

在这里插入图片描述

我们观察到:核心转储文件是很大的

6.3 核心转储的作用

当一个进程在出现异常的时候,操作系统会将进程在内存中的运行信息进行核心转储,转储到当前进程的运行目录下(磁盘),形成core.pid这样的二进制大文件(核心转储文件)。

核心转储的作用是什么呢?

答案是:调试(事后调试),并且直接从出错的地方开始调试

以下是调试样例代码

#include <iostream>using namespace std;int main()
{int a = 1;int b = 0;a = a / b;cout << "a = " << a << endl;return 0;
}

调试方法如下(四个步骤):

  1. 首先gcc/g++在编译时加-g生成可调试文件

在这里插入图片描述

  1. 运行程序,生成 core-dump 文件

在这里插入图片描述

  1. gdb 可执行文件进入调试模式

在这里插入图片描述

  1. gdb命令行输入:core-file <核心转储文件>。即利用核心转储文件,快速定位至出错的地方。

在这里插入图片描述

最后一个问题:既然核心转储文件这么好用,为什么大多数的云服务器默认是将它关闭的呢?

现在许多大型IT公司以及许多中小型企业,都倾向于使用云服务器来托管其后端服务和应用程序。如果打开的话,当应用程序如果收到某个信号,核心转储文件会在进程的当前目录下创建一个大文件。而有很多进程都可能会产生核心转储文件,当文件足够多时,磁盘被挤满,导致系统IO异常,最终会导致整个服务器挂掉的,所以云服务器一般默认是关闭的。

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

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

相关文章

【转型Web3开发第二课】Dapp开发入门基础 | 02 | MetaMask配置网络

本文首发于公众号&#xff1a;Keegan小钢 前言 完成了《转型 Web3 开发第一课》之后&#xff0c;得到了不少读者的认可&#xff0c;很多都在问什么时候开始下一课&#xff0c;近期终于抽出了时间开始搞起这第二课。 这第二课的主题为「Dapp开发入门基础」&#xff0c;即想要转…

浅谈Visual Studio 2022

Visual Studio 2022&#xff08;VS2022&#xff09;提供了众多强大的功能和改进&#xff0c;旨在提高开发者的效率和体验。以下是一些关键功能的概述&#xff1a;12 64位支持&#xff1a;VS2022的64位版本不再受内存限制困扰&#xff0c;主devenv.exe进程不再局限于4GB&#xf…

【ffmpeg命令入门】重新编码媒体流、设置码率、设置帧速率

文章目录 前言ffmpeg的描述重新编码媒体流重新编码媒体流的命令ffmpeg支持的媒体流 设置视频码率视频码率是什么设置视频的码率 设置文件帧数率帧数率是什么ffmpeg设置帧数率 总结 前言 在数字媒体处理领域&#xff0c;ffmpeg是一款非常强大的工具&#xff0c;它可以用来进行媒…

自动驾驶车道线检测系列—3D-LaneNet: End-to-End 3D Multiple Lane Detection

文章目录 1. 摘要概述2. 背景介绍3. 方法3.1 俯视图投影3.2 网络结构3.2.1 投影变换层3.2.2 投影变换层3.2.3 道路投影预测分支 3.3 车道预测头3.4 训练和真实值关联 4. 实验4.1 合成 3D 车道数据集4.2 真实世界 3D 车道数据集4.3 评估结果4.4 评估图像仅车道检测 5. 总结和讨论…

windows下gcc编译C、C++程序 MinGW编译器

文章目录 1、概要2、MinGW安装2.1 编译器下载2.2 编译器安装2.3 设置环境变量2.4 查看gcc版本信息 3、编译C、C程序3.1 编写Hello World.c3.2 编译C程序3.3 运行程序3.4 编译C程序 1、概要 GCC原名为GNU C语言编译器&#xff08;GNU C Compiler&#xff09;&#xff0c;只能处…

Linux系统下weblogic10.3.6版本打补丁步骤

linux系统 weblogic补丁压缩包&#xff1a;p35586779_1036_Generic.zip 链接&#xff1a;https://pan.baidu.com/s/1EEz_zPX-VHp5EU5LLxfxjQ 提取码&#xff1a;XXXX &#xff08;补丁压缩包中包含以下东西&#xff09; 打补丁步骤&#xff1a; 1.备份原weblogic(需要先确保服…

Langchain[3]:Langchain架构演进与功能扩展:流式事件处理、事件过滤机制、回调传播策略及装饰器应用

Langchain[3]:Langchain架构演进与功能扩展&#xff1a;流式事件处理、事件过滤机制、回调传播策略及装饰器应用 1. Langchain的演变 v0.1: 初始版本&#xff0c;包含基本功能。 从0.1~0.2完成的特性&#xff1a; 通过事件流 API 提供更好的流式支持。标准化工具调用支持Tool…

哪些基于 LLMs 的产品值得开发?从用户体验和市场接受度的角度探讨

编者按&#xff1a;在大语言模型&#xff08;LLMs&#xff09;相关技术高速发展的今天&#xff0c;哪些基于 LLMs 的产品真正值得我们投入精力开发&#xff1f;如何从用户体验和市场接受度的角度评估这些产品的潜力&#xff1f; 今天为大家分享的这篇文章&#xff0c;作者的核心…

visual studio开发C++项目遇到的坑

文章目录 1.安装的时候&#xff0c;顺手安装了C模板&#xff0c;导致新建项目执行出问题2.生成的exe&#xff0c;打开闪退问题3.项目里宏的路径不对&#xff0c;导致后面编译没有输出4. vs编译ui&#xff0c;warning跳过&#xff0c;未成功5.vs编译.h&#xff0c;warning跳过&a…

K8S 中的 CRI、OCI、CRI shim、containerd

K8S 如何创建容器&#xff1f; 下面这张图&#xff0c;就是经典的 K8S 创建容器的步骤&#xff0c;可以说是冗长复杂&#xff0c;至于为什么设计成这样的架构&#xff0c;继续往下读。 前半部分 CRI&#xff08;Container Runtime Interface&#xff0c;容器运行时接口&#xf…

避免海外业务中断,TikTok养号注意事项

TikTok已成为企业和个人拓展海外业务的重要平台。然而&#xff0c;由于平台规则严格&#xff0c;账号被封禁或限制访问的风险始终存在。为了确保用户在TikTok上的业务顺利进行&#xff0c;着重说一些养号的注意事项。 文章分为三个部分&#xff0c;分别是遵守平台规则、养号策略…

LATEX格式的高等数学题库(导数和概率论与数理统计)

\documentclass{ctexart} \usepackage{amsmath,amssymb,amsfonts,hyperref} \usepackage{CJKutf8} \usepackage{enumitem} % 引入宏包 \usepackage [colorlinkstrue] {} \begin{document}\begin{CJK}{UTF8}{gkai}%正文放在此行下与\end{CJK}之间就行\tableofcontents\newpage\s…

F1-score(标准度量)

什么是F1-score&#xff1f; F1分数&#xff08;F1-score&#xff09;是分类问题的一个衡量指标。一些多分类问题的机器学习竞赛&#xff0c;常常将F1-score作为最终测评的方法。它是精确率和召回率的调和平均数&#xff0c;最大为1&#xff0c;最小为0&#xff0c;如公式1所示…

使用GPT3.5,LangChain,FAISS和python构建一个本地知识库

引言 介绍本地知识库的概念和用途 在现代信息时代&#xff0c;我们面临着海量的数据和信息&#xff0c;如何有效地管理和利用这些信息成为一项重要的任务。本地知识库是一种基于本地存储的知识管理系统&#xff0c;旨在帮助用户收集、组织和检索大量的知识和信息。它允许用户…

Excel 学习手册 - 精进版(包括各类复杂函数及其嵌套使用)

作为程序员从未想过要去精进一下 Excel 办公软件的使用方法&#xff0c;以前用到某功能都是直接百度&#xff0c;最近这两天跟着哔哩哔哩上的戴戴戴师兄把 Excel 由里到外学了一遍&#xff0c;收获良多。程序员要想掌握这些内容可以说是手拿把掐&#xff0c;对后续 Excel 的运用…

linux的学习(七):读取,函数,正则表达式,文本处理工具cut和awk

##简介 shell编程中的读取&#xff0c;函数&#xff0c;正则表达式&#xff0c;文本处理工具的简单使用 read read&#xff1a;读取控制台的输入 参数&#xff1a; -p&#xff1a;指定读取时的提示符-t&#xff1a;等待读取的时间 脚本例子 编写i.sh脚本&#xff0c;enter…

算法实验3:贪心算法的应用

实验内容 &#xff08;1&#xff09;活动安排问题 设有n个活动的集合E{1, 2, …, n}&#xff0c;其中每个活动都要求使用同一资源&#xff0c;而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi&#xff0c;且si <f…

JavaWeb-【2】CSS和JavaScript

笔记系列持续更新,真正做到详细!!本次系列重点讲解后端,那么第一阶段先讲解前端【续上篇HTML】 目录 一、CSS 1、CSS介绍 2、CSS快速入门 3、CSS语法 4、字体颜色和边框 5、背景颜色和字体样式 6、div和文本居中 7、超链接去下划线和表格细线 8、无序列表去掉样式…

持续集成03--Jenkins的安装与配置

前言 在持续集成/持续部署&#xff08;CI/CD&#xff09;的实践中&#xff0c;Jenkins作为一个开源的自动化服务器&#xff0c;扮演着至关重要的角色。本篇“持续集成03--Jenkins的安装配置”将带您走进Jenkins的世界&#xff0c;深入了解如何在Linux环境中安装并配置Jenkins。…

VUE:跨域配置代理服务器

//在vite.config。js中&#xff0c;同插件配置同级进行配置server:{proxy:{"/myrequest":{//代理域名&#xff0c;可自行修改target:"https://m.wzj.com/",//访问服务器的目标域名changeOrigin:true,//允许跨域configure:(proxy,options) > {proxy.on(&…