【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异常,最终会导致整个服务器挂掉的,所以云服务器一般默认是关闭的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Android】活动的生命周期与启动模式

【Android】活动的生命周期与启动模式 活动的生命周期 返回栈 返回栈&#xff08;Back Stack&#xff09;是Android操作系统中用于管理用户在应用中导航历史的一种数据结构。它允许用户通过按下硬件返回键或调用系统返回功能来回到之前的操作步骤。以下是返回栈的一些关键特…

R与机器学习系列|15.可解释的机器学习算法(Interpretable Machine Learning)(中)

在上次推文中我们介绍了几种可解释机器学习算法的常见方法&#xff0c;包括置换特征重要性、偏依赖图和个体条件期望及其实现。本次我们将继续介绍其他的用来解释机器学习算法的方法。 1.特征交互&#xff08;Feature interactions&#xff09; 1.1介绍 在机器学习中&#xff0…

SpringCache介绍

SpringCache是Spring提供的缓存框架。提供了基于注解的缓存功能。 SpringCache提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff08;只需要导入不同的Jar包即可&#xff09;&#xff0c;如EHCache&#xff0c;Caffeine&#xff0c;Redis。 2个重要依赖已经导入&a…

肿瘤微生态研究利器——5R 16S rDNA测序

肿瘤微生物组&#xff08;Tumor Microbiome&#xff09;是肿瘤微环境中不可或缺的成员&#xff0c;肿瘤内微生物群通过多种机制影响肿瘤发展&#xff0c;在不同类型的肿瘤中&#xff0c;肿瘤内微生物群的组成和丰度具有高度异质性。由于它们的低生物量和其他障碍&#xff0c;全…

Web常见漏洞之po解

暴力破解 概述应用场景实验工具实训准备实训开始四种模式 验证码绕过前端验证码验证码有存活周期 概述 暴力破解是Web漏洞里常见的一种渗透方式&#xff0c;攻击者会试图通过尝试各种可能的用户名和密码组合来猜测密码或密钥&#xff0c;直到猜对为止123。攻击者会经常使用自动…

使用base64通用文件上传

编写一个上传文件的组件 tuku,点击图片上传后使用FileReader异步读取文件的内容&#xff0c;读取完成后获得文件名和base64码&#xff0c;调用后端uploadApi,传入姓名和base64文件信息&#xff0c;后端存入nginx中&#xff0c;用于访问 tuku.ts组件代码&#xff1a; <templa…

win10删除鼠标右键选项

鼠标右键菜单时&#xff0c;发现里面的选项特别多&#xff0c;找一下属性&#xff0c;半天找不到。删除一些不常用的选项&#xff0c;让右键菜单变得干净整洁。 1、按下键盘上的“winR”组合按键&#xff0c;调出“运行”对话框&#xff0c;输入“regedit”命令&#xff0c;点击…

mybatisPlus和mybatis的版本冲突问题、若依换成MP、解决git无法推送、使用若依框架的swagger、以后再遇到团队项目应该怎么做。

20240716 一. mybatisPlus和mybatis的版本冲突问题1. 使用前的准备2. 我遇到了一个很严重的问题。3. 解决问题&#xff0c;好吧也没解决&#xff0c;发现问题&#xff01;&#xff01; 二、该死的git&#xff01;&#xff01;&#xff01;&#xff01;1. 解决无法在idea中使用g…

2.RabbitMQ相关概念

介绍 RabbitMQ是一个消息中间件&#xff0c;接受并转发消息。它接收、存储和转发消息数据。 四大核心概念&#xff1a; 1.生产者 产生数据发送消息的程序是生产者。 2.消费者 3.队列 每一个队列对应一个消费者。 如果两个消费者对应同一个队列&#xff0c;那么队列中的…

R绘制Venn图及其变换

我自己在用R做各种分析时有不少需要反复用到的基础功能&#xff0c;比如一些简单的统计呀&#xff0c;画一些简单的图等等&#xff0c;虽说具体实现的代码也不麻烦&#xff0c;但还是不太想每次用的时候去找之前的代码。 索性将常用的各种函数整成了一个包&#xff1a;pcutils…

深度刨析程序中的指针

前面我们已经学习过了指针的一下性质&#xff1a; 指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识的一块内存空间指针的大小是固定的4/8个字节&#xff08;32位平台/64位平台&#xff09;指针是有类型&#xff0c;指针的类型决定了指针的加减整数的步长&…

Pr 2024下载安装,Adobe Premiere pro2024剪辑软件下载合集获取

Premiere Pro 2023中文版简称Pr&#xff0c;pr2023是一款视频编辑软件。 pr 2023不仅可以帮助用户对各种视频进行剪辑、旋转、分割、合并、字幕添加、背景音乐等基础的处理&#xff0c;还能帮助用户进行视频颜色校正、颜色分级、稳定镜头、调整层、更改片段的持续时间和速度、效…

进程通信(3): System V IPC

System IPC包括System V消息队列&#xff0c;System V信号量&#xff0c;System V共享内存区。 System V IPC使用一个key&#xff08;key_t&#xff09;作为他们名字&#xff0c;这个值由ftok函数通过路径名和一个id生成。 客户和服务器通过在路径名和id达成一致&#xff0c;双…

SpringCloud教程 | 第十篇: 读取Nacos的配置(失败情况2)

参考&#xff1a;SpringCloud搭建Nacos配置中心_springcloud nacos maven-CSDN博客 解决了pom.xml的依赖冲突后&#xff0c;在启动服务时&#xff0c;报错了&#xff0c;并且自动停止了。 2024-07-17 11:13:03.597 ERROR [com.alibaba.nacos.client.remote.worker] com.alibab…

在 Windows 平台搭建 MQTT 服务

一、引言 出于稳定性、可靠性、成本等多方面的考虑&#xff0c;众多 MQTT 服务实现更倾向于选择拥有丰富开源生态系统的 Linux 环境&#xff0c;Windows 平台上可选的 MQTT 服务相对有限。NanoMQ 是用于物联网边缘的超轻量级 MQTT 消息服务器&#xff0c;有着强大的跨平台和可…

超详细信息收集篇

1 域名信息收集 1.1 域名是什么 域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的 Internet 上某一台 计算机 或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地…

监控系统怎样做?

监控类型自底向上分为资源监控、服务监控和业务监控。希望打造公司级的监控系统最好的时机是系统规划时&#xff0c;如果把监控设计往后放&#xff0c;将会面临一个巨大的难题&#xff1a;推行和现有不兼容的规范。 三种监控类型 资源监控 这个相对简单&#xff0c;随着k8s的兴…

【深度学习】BeautyGAN: 美妆,化妆,人脸美妆

https://www.sysu-hcp.net/userfiles/files/2021/03/01/3327b564380f20c9.pdf 【深度学习】BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversarial Network BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversaria…

嵌入式物联网在教育行业的应用——案例分析

作者主页: 知孤云出岫 嵌入式物联网在教育行业的应用——案例分析 目录 作者主页:嵌入式物联网在教育行业的应用——案例分析一、引言二、智能教室&#xff1a;环境监测系统1. 硬件需求2. 电路连接3. 代码实现 三、个性化学习&#xff1a;智能学习平台1. 数据处理与分析2. 代…

下载仓颉sdk安装时遇到“无法运行”问题

图1. 社区地址&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 在GitCode社区中下载Cangjie-0.53.4-windows_x64的sdk后&#xff0c;双击安装时遇到“此应用无法在你的电脑上运行的问题” 经过反复排查后&#xff0c;确定是sdk直接下载有问题&#xff1b;‘需要…