Linux:进程信号(一.认识信号、信号的产生及深层理解、Term与Core)

上次结束了进程间通信的知识介绍:Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量


文章目录

  • 1.认识信号
    • 进程看待信号方式
  • 2.信号的产生
    • 2.1信号的处理的方式 --- signal()函数
    • 2.2kill指令产生信号
    • 2.3键盘产生信号
    • 2.4系统调用发送信号 —kill系统调用、raise()和abort()库函数
    • 2.5软件条件产生信号
    • 2.6异常产生信号
  • 3.信号产生的深层理解
    • 键盘产生信号
    • 异常产生信号
  • 4.Term与Core
    • core文件
    • 在这里插入图片描述


1.认识信号

**概念:**在Linux系统中,进程之间可以通过信号进行通信,实现异步信息的发送和接收

信号是Linux系统中一种轻量级的通信机制,用于通知进程发生了某种事件或异常情况。进程可以发送信号给其他进程,也可以接收来自其他进程或系统的信号。

  • 异步:是一种编程模型或通信方式,指的是在进行操作或通信时,不需要等待前一个操作完成或响应返回,而是可以继续执行下一个操作或任务(二者是并发的,一个不用等另一个)。异步编程可以提高系统的并发性和响应性,使得程序能够更高效地利用资源和处理多个任务
  • 异步信息通常指的是在通信或交流过程中,信息的发送和接收是不同步的,即发送方和接收方的速度或时间不一致。这种情况下,接收方可能会在不同的时间点接收到发送方发送的信息

可以使用kill -l来查看信号

在这里插入图片描述

  • 1-31是普通信号,34-64是实时信号,没有0,没有32 33

  • 使用数字和名称都行(本质也是宏定义)

进程看待信号方式

  1. 在没有发生的时候,进程就已经知道如果发生了,怎么进行处理:这句话可能指的是预先设置好的信号处理方式。在Linux系统中,进程可以使用signal()或者sigaction()等系统调用来注册信号处理函数,这样当特定信号发生时,系统会调用相应的信号处理函数来处理该信号。

  2. 进程能认识信号:这句话指的是我们可以识别和处理特定的信号。Linux系统定义了一系列标准信号(如SIGINT、SIGTERM、SIGKILL等),每个信号都有特定的含义和默认处理方式,进程可以根据需要识别和处理这些信号。

  3. 信号到来的时候,如果进程正在处理更重要的事情,导致暂时不能处理到来的信号,那么进程必须要把到来的信号进行临时保存:这指的是信号的异步性。当进程正在执行某些重要任务时,如果接收到信号,可能无法立即处理,此时系统会将信号暂时保存,等到合适的时机再进行处理。

  4. 信号到了,可以不立即处理,选择在合适的时候处理:进程可以选择在合适的时机处理信号,而不是立即响应。这种灵活性使得进程能够根据自身状态和需求来处理信号。

  5. 信号的产生是随时产生的,我们无法准确预料,所以信号是异步发送的:信号是由其他用户、进程或系统事件产生的,进程无法准确预测信号的产生时机。因此,信号的发送是异步的,进程需要通过信号处理函数来处理这种异步事件。

异步发送指的是信号是由其他用户或进程产生的,而接收信号的进程在信号到达之前可能一直在处理自己的任务


2.信号的产生

2.1信号的处理的方式 — signal()函数

在这里插入图片描述

signal()函数是Linux系统中用于注册信号处理函数的函数。它的原型如下:

#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);//sighandler_t是个函数指针

这个函数接受两个参数:signum表示要捕捉的信号编号,handler表示要注册的信号处理函数。

  1. 参数说明

    • signum:表示要捕捉的信号编号,可以是预定义的信号宏如SIGINTSIGTERM等,也可以是用户自定义的信号编号。
    • handler:表示要注册的信号处理函数,其**原型通常是void handler(int signal_number)(调用这样的函数)。**可以是函数指针(自定义),也可以是SIG_IGN(忽略信号)或SIG_DFL(默认处理)。
  2. 返回值

    • signal()函数的返回值是一个函数指针,指向之前注册的信号处理函数。如果之前未注册过该信号的处理函数,则返回SIG_DFL(默认处理)。
  3. 信号处理方式

    • 如果handler为函数指针,则表示注册自定义的信号处理函数,当收到指定信号时,系统会调用该函数进行处理。
    • 如果handlerSIG_IGN,表示忽略该信号,即当收到指定信号时不进行任何处理。
    • 如果handlerSIG_DFL,表示使用系统默认的处理方式,通常是终止进程或执行默认操作。
  4. 注意事项

    • 当使用signal()函数注册信号处理函数时,处理函数并不会立即执行,而是在未来收到对应的信号时才会执行
    • 如果注册了一个处理SIGINT信号的处理函数,但是进程从未收到SIGINT信号,那么注册的处理函数也就永远不会被调用。这种情况可能会发生
  • 定义信号处理函数:指的是编写实际的处理信号的函数,即编写处理SIGINT信号的具体函数逻辑。这个函数通常具有特定的原型,如void handler(int signal_number)

  • 注册信号处理函数:指的是使用signal()函数将定义好的信号处理函数特定的信号关联起来。通过注册信号处理函数,系统会在收到对应的信号时调用这个函数来处理信号。

完整的表述应该是:定义一个处理SIGINT信号的处理函数,并通过signal()函数将这个处理函数注册到SIGINT信号上。当进程收到SIGINT信号时,系统会调用注册的处理函数来处理该信号。

2.2kill指令产生信号

kill指令是用于向进程发送信号的命令。通过kill命令,可以向指定进程发送不同类型的信号,例如SIGTERMSIGKILL等。这些信号可以触发进程中注册的信号处理函数,或者直接终止进程的执行。

kill命令的基本语法为:

kill [options] <PID>

<PID>是要发送信号的目标进程的进程ID。可以通过ps命令或其他方式获取目标进程的进程ID。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>using namespace std;int main()
{while(true){cout << "I'm a process, pid:" << getpid() << endl;sleep(2);//我们写个死循环,每隔两秒打印一下}return 0;
}

在这里插入图片描述

2.3键盘产生信号

  1. 我们之前使用的Ctrl+c:ctrl + c -> OS 解释成为2(SIGINT)号信号 -> 向目标进程进行发送 -> 进程收到-> 进程响应

    • 用户按下Ctrl+C组合键,操作系统会将这个操作解释为发送SIGINT(信号编号为2)信号给目标进程。
    • 目标进程收到SIGINT信号后,会执行与之关联的信号处理函数。通常情况下,SIGINT信号会导致进程终止执行,类似于用户主动输入exit或者点击关闭窗口。
  2. 之前使用的Ctrl+\:ctrl + c -> OS 解释成为3(SIGQUIT)号信号 -> 向目标进程进行发送 -> 进程收到-> 进程响应

    • 用户按下Ctrl+\组合键,操作系统会将这个操作解释为发送SIGQUIT(信号编号为3)信号给目标进程。
    • 目标进程收到SIGQUIT信号后,会执行与之关联的信号处理函数。与SIGINT不同的是,SIGQUIT信号通常用于请求进程终止,并且会生成core文件(如果core文件生成是启用的话)

验证:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>using namespace std;void handler(int signum)
{cout << "got a signal, number is : " << signum << endl;
}int main()
{signal(SIGINT, handler);  // 对2号信号SIGINT处理signal(SIGQUIT, handler); // 对号信号SIGQUIT处理while(true){cout << "I'm a process, pid:" << getpid() << endl;sleep(2);}return 0;
}

在这里插入图片描述

2.4系统调用发送信号 —kill系统调用、raise()和abort()库函数

kill是一个常见的系统调用,用于向指定的进程发送信号。通过kill系统调用,一个进程可以向另一个进程发送不同类型的信号,从而实现进程之间的通信和控制。

kill系统调用的原型如下:

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);
  • pid参数指定了要发送信号的目标进程的进程ID(PID)。如果pid为正数,则表示发送信号给进程ID为pid的进程;如果pid为0,则表示发送信号给与调用进程在同一进程组的所有进程;如果pid为-1,则表示发送信号给所有有发送权限的进程。
  • sig参数指定了要发送的信号的编号,可以是预定义的信号常量(如SIGKILLSIGTERM等),也可以是自定义的信号编号。

kill系统调用的返回值为0表示成功发送信号,-1表示发送信号失败,并且在这种情况下,可以通过errno全局变量获取具体的错误信息。

我们可以利用这个,来实现一个kill指令

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <cerrno>
#include <cstring>using namespace std;int main(int argc, char *argv[])
{if (argc != 3){cout << "Usage: kill -signum pid" << endl;return 1;}int signum = stoi(argv[1] + 1); // 获取信号的数字int pid = stoi(argv[2]);        // 获取pidint n = kill(pid, signum);if (n == -1){cerr << "kill failed: " << strerror(errno) << endl;}return 0;
}

可以对任意进程发送任意的信号

  1. raise()函数:raise()函数用于向当前进程发送一个信号。它的原型如下:

    int raise(int sig);
    

    sig是要发送的信号编号。

    成功时返回0,失败时返回非零值

    对自己发送任意信号

    int main()
    {int count = 0;while (true){cout << "cnt: " << count++ << endl;sleep(1);if (count == 3){cout << "send 9 to itself" << endl;raise(9);}}return 0;
    }
    

    在这里插入图片描述

  2. abort()函数:abort()函数用于异常终止程序的执行。当调用abort()函数时,程序会立即终止,并向操作系统发送SIGABRT信号。abort()函数的原型如下:

    void abort(void);
    

    abort()函数会导致程序生成一个core文件,用于调试。一般来说,abort()函数被用于发现程序中的严重错误,并且需要立即终止程序执行。

给自己放指定信号(6号SIGABRT)

2.5软件条件产生信号

  1. 管道

读端关闭其文件描述符并且不再读取数据时,如果写端继续向管道写入数据,操作系统会发送一个SIGPIPE信号给写端进程。默认情况下,这个信号会终止写端进程SIGPIPE信号是一个用于处理管道写端在写操作时无读端接收的情况的信号。

  1. alarm()函数
#include <unistd.h>unsigned int alarm(unsigned int seconds);

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

函数的返回值是0或者是以前设定的闹钟时间还余下的秒数

在Linux系统中,SIGALRM信号的默认行为是终止进程。当程序设置一个定时器并在定时器到期时产生SIGALRM信号时,如果程序没有显式地捕获和处理这个信号,那么默认情况下操作系统会终止该进程。

alarm(0),代表取消闹钟:alarm(0)函数会清除之前设置的定时器,并返回剩余的定时器时间(如果有的话)而且不会再触发SIGALRM信号

怎么理解软件条件:

软件条件是指软件层面上的一种异常情况或特定的条件,通常由软件中断信号触发,用来通知进程某种特定的事件已经发生

结构体与堆等数据结构都是软件,也有条件触发

在这里插入图片描述

2.6异常产生信号

  1. 代码除0了,收到8号信号SIGFPE

    void handler(int signum)
    {cout << "got a signal, number is : " << signum << endl;exit(0);
    }int main()
    {signal(8, handler); // 8号信号SIGFPEint a = 10;int b = a / 0;cout << b << endl;return 0;
    }
    

    在这里插入图片描述

  2. 访问野指针指向的空间,收到11号信号SIGSEGV(段错误)

    void handler(int signum)
    {cout << "got a signal, number is : " << signum << endl;exit(0);
    }int main()
    {signal(11, handler); // 11号信号SIGSEGVint* pa=nullptr;*pa=10;return 0;
    }
    

3.信号产生的深层理解

键盘产生信号

在这里插入图片描述

那么现在又有问题了,什么叫做解释成为信号,什么叫做发送给进程?

信号临时保存在哪里呢?

进程的PCB中,使用位图结构来存1到31号的信号:比特位的位置来表示信号编号,比特位的01来表示是否收到指定的信号

那么发送信号本质上是写入信号:

在这里插入图片描述

task_struct是内核数据结构,只有OS有能力写入。我们用户只能使用系统调用。所以,无论信号产生的方式有多少种,最终都是OS在进程中写入信号的

异常产生信号

  • 除0异常

在这里插入图片描述

但如果我们自定义处理里,没有进行exit()退出,那么就会一直打印

因为,寄存器中的数据都是进程的上下文,CPU一直在进行进程的调度,那么就涉及到进程上下文的保存和恢复,因为我们没有进行退出操作,所以每次恢复后,异常还是存在。

  • 野指针异常

在这里插入图片描述

最终信号一定都是OS进行写入进程中的信号位图中

总结一下:

  • 上面所说的所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者

  • 信号的处理是否是立即处理的?在合适的时候

4.Term与Core

在这里插入图片描述

  1. Term(Termination)
    • 当进程接收到一个默认处理动作为Term的信号时,操作系统会立即终止该进程的执行。
    • 进程在接收到这样的信号后,会立即停止运行,并释放其所占用的系统资源。
    • 除非进程已经捕获了该信号并定义了自己的信号处理函数,否则进程会按照默认的Term动作被终止。
  2. Core(Core Dump)
    • 当进程接收到一个默认处理动作为Core的信号时,操作系统不仅会终止该进程的执行,还会生成一个核心转储文件(core dump file)
    • 核心转储文件是进程在异常终止时的内存映像,它包含了进程在终止时的状态信息,如变量值、函数调用栈等。
    • 这个文件对于程序员来说非常有用,因为它可以帮助他们分析进程崩溃的原因,进行调试和修复。
    • Term不同,Core动作在终止进程的同时还会生成一个额外的文件。

需要注意的是云服务器默认关闭了core file的选项:因为如果程序崩溃是由于某种未知的错误或条件触发的,并且这个问题没有得到及时解决,那么核心转储(core dump)文件可能会不断生成,占用大量的磁盘空间

ulimit -a 是一个在 Linux中用于显示当前 shell 会话的资源限制的命令。ulimit 命令允许用户设置或查看各种 shell 和进程资源限制。这些限制可以帮助防止系统资源的滥用,如 CPU 时间、文件大小、打开的文件描述符数量等。

当你运行 ulimit -a 时,它会列出所有当前设置的资源限制。以下是一些常见的 ulimit 资源和它们的描述:

  • -c: core file size (blocks, -c unlimited disables core files)
  • -d: data seg size (kbytes, -d unlimited)
  • -e: scheduling priority (-e 0 to 20)
  • -f: file size (blocks, -f unlimited)
  • -i: max locked memory (kbytes, -i unlimited)

如果想要修改某个限制,可以使用 ulimit 命令加上相应的选项和新的限制值。

例如,要设置最大打开文件描述符数量为 4096,你可以运行 ulimit -n 4096。但是请注意,这些限制通常只影响当前 shell 会话和由该 shell 启动的子进程。它们不会永久地改变系统配置。

我们想要产生core文件的话:ulimit -c选项设置core file的大小

core文件

  • 为什么要有这个文件:我们想通过core来知道进程为什么退出,以及执行到哪行代码退出的
  • 是什么:将进程在内存中的核心数据(与调试有关的)转储到磁盘中形成core、core.pid的文件
  • 作用:最大的作用是方便我们调试了

Core文件是Linux系统下的内核转储文件,当程序崩溃时由操作系统生成,主要用于对程序进行调试

当程序出现内存越界、段错误(Segmentation Fault)或其他异常情况导致崩溃时,操作系统会中止该进程,并将当前内存状态、寄存器状态、堆栈指针、内存管理信息以及各个函数使用堆栈信息等保存到Core文件中。这样,程序员就可以通过读取和分析Core文件来找出程序崩溃的原因和位置,从而进行调试和修复。

Core文件的存在是为了帮助程序员更好地理解和解决程序崩溃的问题。由于Core文件包含了程序崩溃时的详细内存状态信息,因此它对于调试复杂的内存问题、并发问题以及系统调用等问题非常有用。同时,由于Core文件是在程序崩溃时自动生成的,因此它也可以作为一种自动记录程序崩溃信息的机制,方便程序员进行事后分析和排查。

但是,由于Core文件可能包含大量的内存数据,因此它可能会占用较大的磁盘空间。在不需要进行调试或分析的情况下,可以通过修改操作系统的配置来禁止生成Core文件或将其保存到其他位置。

在这里插入图片描述

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>using namespace std;int main()
{pid_t id = fork();if (id == 0){// childint a = 10;a /= 0;exit(1);}// fatherint status = 0;pid_t rid = waitpid(id, &status, 0);cout << "exit code:" << ((status >> 8) & 0xff) << endl;cout << "exit signal:" << (status & 0x7f) << endl;cout << "core dump:" << ((status >> 7) & 0x1) << endl;return 0;
}

在这里插入图片描述

今天也是到这里了,(存货太多慢慢发了)。学了网络部分的要赶快做项目了

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

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

相关文章

最新快乐二级域名分发系统重置版v1.7源码-最新美化版+源码+可对接支付

源码简介&#xff1a; 最新快乐二级域名分发系统重置版v1.7源码&#xff0c;它是最新美化版源码可对接支付。 快乐二级域名分发系统重置版v1.7源码&#xff0c;简单快捷、功能强大的控制面板。系统稳定长久&#xff0c;控制面板没任何广告&#xff0c;让网站更实用方便。 最…

现货白银交易中spring形态的应用

在现货白银市场中交易想取得成功并从市场中获利&#xff0c;掌握一些工具是必不可少的&#xff0c;而今天我们要介绍的现货白银的交易工具就是spring形态。 对于spring这个英文&#xff0c;我们都很熟悉&#xff0c;它有春天的意思&#xff0c;但这里所说的spring形态并不是指春…

ComfyUI进阶:Comfyroll节点 (最终篇)+应用实例

前言&#xff1a; 学习ComfyUI是一场持久战&#xff0c;而Comfyroll 是一款功能强大的自定义节点集合&#xff0c;专为 ComfyUI 用户打造&#xff0c;旨在提供更加丰富和专业的图像生成与编辑工具。借助这些节点&#xff0c;用户可以在静态图像的精细调整和动态动画的复杂构建…

【LabVIEW作业篇 - 5】:水仙花数、数组与for循环的连接

文章目录 水仙花数数组与for循环的连接 水仙花数 水仙花数&#xff0c;是指一个3位数&#xff0c;它的每个位上的数字的3次幂之和等于它本身。如371 3^3 7^3 1^3&#xff0c;则371是一个水仙花数。 思路&#xff1a;水仙花数是一个三位数&#xff0c;通过使用for循环&#xf…

redis的使用场景和持久化方式

redis的使用场景 热点数据的缓存。热点&#xff1a;频繁读取的数据。限时任务的操作&#xff1a;短信验证码。完成session共享的问题完成分布式锁。 redis的持久化方式 什么是持久化&#xff1a;把内存中的数据存储到磁盘的过程&#xff0c;同时也可以把磁盘中的数据加载到内存…

FPGA实现二选一数据选择器

在FPGA开发当中&#xff0c;我们最早开始接触的就是关于二选一选择器的设计。 1、原理 通过一个sel选择位判断输出out为a还是b&#xff0c;这里我们规定&#xff1a; sel0时&#xff0c;outa sel1时&#xff0c;outb 2、工程代码 多路选择器的缩写为MUX&#xff0c;这里我们见…

Git报错fatal: detected dubious ownership in repository

报错信息 fatal: detected dubious ownership in repository at 解决办法 一行代码解决 git config --global --add safe.directory "*";如何使用git工具初始胡项目并且和远程仓库建立联系 git init–建立一个本地仓库 git add README.md–将README.md文件加入…

【Day1415】Bean管理、SpringBoot 原理、总结、Maven 高级

0 SpringBoot 配置优先级 从上到下 虽然 springboot 支持多种格式配置文件&#xff0c;但是在项目开发时&#xff0c;推荐统一使用一种格式的配置 &#xff08;yml是主流&#xff09; 1 Bean管理 1.1 从 IOC 容器中获取 Bean 1.2 Bean 作品域 可以通过注解 Scope("proto…

计算机网络八股文(后续更新)

文章目录 一、计算机网络体系结构1、计算机网络的各层协议及作用&#xff1f; 二、物理层三、数据链路层四、网络层五、传输层1、TCP和UDP的区别&#xff1f;2、UDP 和 TCP 对应的应用场景是什么&#xff1f;3、详细介绍一下 TCP 的三次握手机制4、为什么需要三次握手&#xff…

测试管理工具、自动化测试工具、跨浏览器测试工具 推荐

测试管理工具 1&#xff09;Xray Xray 是排名第一的手动与自动化测试管理应用&#xff0c;专为质量保证而设计。它是一个功能齐全的工具&#xff0c;能够无缝集成于 Jira 中。其目的是通过有效和高效的测试帮助公司提高产品质量。 功能特点&#xff1a; 需求、测试、缺陷和执…

Java | Leetcode Java题解之第275题H指数II

题目&#xff1a; 题解&#xff1a; class Solution {public int hIndex(int[] citations) {int n citations.length;int left 0, right n - 1;while (left < right) {int mid left (right - left) / 2;if (citations[mid] > n - mid) {right mid - 1;} else {lef…

深入浅出mediasoup—协议交互

本文主要分析 mediasoup 一对一 WebRTC 通信 demo 的协议交互&#xff0c;从协议层面了解 mediasoup 的设计与实现&#xff0c;这是深入阅读 mediasoup 源码的重要基础。 1. 时序图 下图是 mediasoup 客户端-服务器协议交互的总体架构&#xff0c;服务器是一个 Node.js 进程加…

【计算机毕业设计】877陪诊系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

使用nginx实现一个端口和ip访问多个vue前端

前言&#xff1a;由于安全组要求&#xff0c;前端页面只开放一个端口&#xff0c;但是项目有多个前端&#xff0c;此前一直使用的是一个前端使用单独一个端口进行访问&#xff0c;现在需要调整。 需要实现&#xff1a;这里以80端口为例&#xff0c;两个前端分别是&#xff1a;p…

AI 驱动下的一体化分布式数据库:滴滴、快手、中国恩菲、好未来、翼鸥教育共话创新应用实践|OceanBase Meetup 精彩回顾

7月6日&#xff0c;OceanBase Meetup 北京站——“AI 驱动下的一体化分布式数据库&#xff1a;跨行业多场景的创新应用与实战”举办。来自滴滴、快手、中国恩菲、好未来、翼鸥教育、蚂蚁集团及OceanBase等众多行业技术专家与资深用户&#xff0c;围绕众多用户关注的AI 与数据库…

物联网mqtt网关搭建背后的技术原理

前言 物联网是现在比较热门的软件领域&#xff0c;众多物联网厂商都有自己的物联网平台&#xff0c;而物联网平台其中一个核心的模块就是Mqtt网关。这篇文章的目的是手把手教大家写书写一个mqtt网关&#xff0c;后端存储支持Kafka/Pulsar&#xff0c;支持mqtt 连接、断链、发送…

Zabbix监控系统:zabbix服务部署+基于Proxy分布式部署+zabbix主动与被动监控模式

一、Zabbix概述 1.1 简介 zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff0c;提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbix…

中文之美:每日成语【瓜田李下】

文章目录 引言瓜田李下相关谜语成语接龙引言 中文之美,美在诗词歌赋,美在绝句华章,也美在对事物名称的雅致表达。 瓜田李下 拼音:guā tin lǐ xi 释义 1.经过瓜田时弯腰提鞋,经过李子树下时抬手扶帽,容易被人怀疑是偷瓜、偷李子。 2.比喻嫌疑之地。 出处:三国曹植…

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理 目录 AGI 之 【Hugging Face】 的【从零训练Transformer模型】之二 [ 从零训练一个模型 ] 的简单整理 一、简单介绍 二、Transformer 1、模型架构 2、应用场景 3、Hugging …

基于微信小程序+SpringBoot+Vue的校园自助打印系统(带1w+文档)

基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 管理信息可以处理复杂的信息从而提高用户的工作效率&#xff0c;减少失误。所以本基于Vue和微信小程序的校园自助打印系统的开发非常有意义&#xff0c;本系…