Linux基础知识点(五-信号)

一、信号的基本概念

1.1 信号的概念

信号(signal),又称为软中断信号,用于通知进程发生了异步事件,它是Linux系统响应某些条件而产生的一个事件,它是在软件层次上对中断机制的一种模拟,是一种异步通信方式。

一句话总结:信号本质是一种异步通知机制用户或操作系统通过发送信号来通知进程某些事情已经发生,进程可以进行后续处理。

1.2 信号的产生 

① 通过终端按键产生信号

用户在终端按某些键时,引发终端产生信号,例如在终端上按Ctrl + C键通常产生中断信号(SIGINT),终止进程。

通过系统函数向进程发信号

进程调用kill(2)函数可以将任意信号发送给另一个进程或进程组,但接收信号和发送信号进程的所有者必须相同,或发送信号进程的所有者是超级用户。在终端通过"kill -SIGNO PID" 也可以将指定的信号发送给指定pid的进程,如kill -SIGINT 4568。

③ 由软件条件产生信号

当检测到某种软件条件已经发生,将其通知有关进程时也产生信号,例如SIGURG(在网络连接上传来外面的数据)、SIGPIPE(在管道的读进程已终止后,一个进程写此管道)、SIGALRM(进程所设置的闹钟已经超时)。

硬件异常产生信号

除数为0、无效的内存引用等,这些条件通常由硬件检测到,并通知内核,然后内核为该条件发生时正在运行的进程产生适当的信号。

 1.3 信号的通信机制

信号是进程间通信机制中唯一异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上, 进程也不知道信号到底什么时候到达。它和中断服务函数一样,在中断发生的时候, 就会进入中断服务函数中去处理,同样的,当进程接收到一个信号的时候,也会相应地采取一些行动。 

1.4 信号的种类 

我们可以使用" kill -l "命令来查看系统中支持的信号的种类

从图中可以看出,Linux系统支持信号62种信号,每种信号名称都以SIG三个字符开头, 注意,编号为32和33的信号值是不存在的。

可以将这62种信号分为两大类:信号值为1~31的信号属于非实时信号(也称为不可靠信号),它们是从UNIX系统中继承下来的信号,信号值为34~64的信号为实时信号(也称为可靠信号)

这些信号各自在什么条件下产生,默认的处理动作是什么,可以通过 " man 7 signal " 来查询。

信号值

名称

描述

默认处理

1

SIGHUP

控制终端被关闭时产生。

终止

2

SIGINT

程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl + C)时发出,用于通知前台进程组终止进程。

终止

3

SIGQUIT

SIGQUIT 和SIGINT类似,但由QUIT字符(通常是Ctrl + )来控制,进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。

终止并产生转储文件(core文件)

4

SIGILL

CPU检测到某进程执行了非法指令时产生,通常是因为可执行文件本身出现错误, 或者试图执行数据段、堆栈溢出时也有可能产生这个信号。

终止并产生转储文件(core文件)

5

SIGTRAP

由断点指令或其它trap指令产生,由debugger使用。

终止并产生转储文件(core文件)

6

SIGABRT

调用系统函数 abort()时产生。

终止并产生转储文件(core文件)

7

SIGBUS

总线错误时产生。一般是非法地址,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。

终止并产生转储文件(core文件)

8

SIGFPE

处理器出现致命的算术运算错误时产生,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。

终止并产生转储文件(core文件)

9

SIGKILL

系统杀戮信号。用来立即结束程序的运行,本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号将进程杀死。

终止

10

SIGUSR1

用户自定义信号。

终止

11

SIGSEGV

访问非法内存时产生,进程试图访问未分配给自己的内存,或试图往没有写权限的内存地址写数据。

终止

12

SIGUSR2

用户自定义信号。

终止

13

SIGPIPE

这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止,也会产生这个信号。

终止

14

SIGALRM

定时器到期信号,计算的是实际的时间或时钟时间,alarm函数使用该信号。

终止

15

SIGTERM

程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号,如果进程终止不了,才会尝试SIGKILL。

终止

16

SIGSTKFLT

已废弃。

终止

17

SIGCHLD

子进程暂停或终止时产生,父进程将收到这个信号,如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程,这种情况我们应该避免。父进程默认是忽略SIGCHILD信号的,我们可以捕捉它,做成异步等待它派生的子进程终止,或者父进程先终止,这时子进程的终止自动由init进程来接管。

忽略

18

SIGCONT

系统恢复运行信号,让一个停止(stopped)的进程继续执行,本信号不能被阻塞,可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作

恢复运行

19

SIGSTOP

系统暂停信号,停止进程的执行。注意它和terminate以及interrupt的区别:该进程还未结束,只是暂停执行,本信号不能被阻塞,处理或忽略。

暂停

20

SIGTSTP

由控制终端发起的暂停信号,停止进程的运行,但该信号可以被处理和忽略,比如用户键入SUSP字符时(通常是Ctrl+Z)发出这个信号。

暂停

21

SIGTTIN

后台进程发起输入请求时控制终端产生该信号。

暂停

22

SIGTTOU

后台进程发起输出请求时控制终端产生该信号。

暂停

23

SIGURG

套接字上出现紧急数据时产生。

忽略

24

SIGXCPU

处理器占用时间超出限制值时产生。

终止并产生转储文件(core文件)

25

SIGXFSZ

文件尺寸超出限制值时产生。

终止并产生转储文件(core文件)

26

SIGVTALRM

由虚拟定时器产生的虚拟时钟信号,类似于SIGALRM,但是计算的是该进程占用的CPU时间。

终止

27

SIGPROF

类似于SIGALRM / SIGVTALRM,但包括该进程用的CPU时间以及系统调用的时间。

终止

28

SIGWINCH

窗口大小改变时发出。

忽略

29

SIGIO

文件描述符准备就绪, 可以开始进行输入/输出操作。

终止

30

SIGPWR

启动失败时产生。

终止

31  SIGUNUSED非法的系统调用。终止并产生转储文件(core文件)

 1.5 实时信号和非实时信号

Linux系统中有许多信号,其中前面 31 个信号都有一个特殊的名字,对应一个特殊的事件, 比如信号值为1的信号SIGHUP(Signal Hang UP),这个信号就是通知系统关闭中断的, 当系统中的一个控制终端被关闭(即挂断,hang up)时,都会产生这个信号。

信号值为1~31的信号属性非实时信号,它主要是因为这类信号不支持排队, 因此信号可能会丢失。比如发送多次相同的信号,进程只能收到一次, 也只会处理一次,因此剩下的相同的信号将被丢弃。而实时信号(信号值为34~64的信号)则不同, 它是支持排队的,发送了多少个信号给进程,进程就会处理多少次。

二、信号的处理

生成信号的事件一般可以归为三大类程序错误、外部事件以及显式请求

  • 程序错误如:零作除数、非法存储访问等,这种情况通常是由硬件而不是由Linux内核检测到的, 但由内核向发生此错误的那个进程发送相应的信号;

  • 外部事件如:当用户在终端按下某些键时产生中断生成的信号,当进程超越了CPU或文件大小的限制时, 内核会生成一个信号通知进程;

  • 显式请求如:进程通过使用kill()函数来发送任何信号给其他进程或进程组。

信号的生成既可以是同步的,也可以是异步的

  • 同步信号大多数是程序执行过程中出现了某个错误而产生的, 由进程显式请求生成的给自己的信号也是同步的。

  • 异步信号是接收进程可控制之外的事件所生成的信号,这类信号一般是进程无法控制的, 只能被动接收,因为进程也不知道这个信号会何时发生,只能在发生的时候去处理它。 一般外部事件总是异步地生成信号,异步信号可在进程运行中的任意时刻产生, 进程无法预期信号到达的时刻,它所能做的只是告诉Linux内核假如有信号生成时应当采取什么行动(这相当于注册信号对应的处理)。

无论是同步还是异步信号,当信号发生时,我们可以告诉Linux内核采取如下3种动作中的任意一种:

  • 忽略信号:大部分信号都可以被忽略,但有两个除外:SIGSTOP和SIGKILL绝不会被忽略。 不能忽略这两个信号的原因是为了给超级用户提供杀掉或停止任何进程的一种手段。 此外,尽管其他信号都可以被忽略,但其中有一些却不宜忽略。例如,若忽略硬件例外(非法指令)信号, 则会导致进程的行为不确定。

  • 捕获信号:这种处理是要告诉Linux内核,当信号出现时调用专门提供的一个函数。 这个函数称为信号处理函数,它专门对产生信号的事件作出处理。

  • 让信号默认动作起作用:系统为每种信号规定了一个默认动作,这个动作由Linux内核来完成, 有以下几种可能的默认动作:

    • 终止进程并且生成内存转储文件,即写出进程的地址空间内容和寄存器上下文至进程当前目录下名为cone的文件中;

    • 终止终止进程但不生成core文件。

    • 忽略信号。

    • 暂停进程。

    • 若进程为暂停状态,恢复进程,否则将忽略信号。

三、信号的保存

3.1 信号其他相关概念

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态称为信号未决(Pending)
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
  • 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

 3.2 信号在内核中的表示

  • 进程通过二个位图来管理信号的处理过程, 每个信号占用两个位图某一位分别表示阻塞(block)和未决(pending),还有一个函数指针数组表示处理动作。信号产生时,内核在进程控制块(task_struct)中设置该信号的未决标志,直到信号递达才清除该标志。
  • 在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,且不能抵达,它的处理动作是用户自定义函数sighandler。

 3.3 信号集

每个信号在未决和阻塞位图中各占用一位,非0即1,不记录该信号产生了多少次。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,即图中未决和阻塞的位图。这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

3.4 信号集操作函数 

用户只能通过信号集操作函数来操作信号集(sigset_t)。

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清0,表示该信号集不包含任何有效信号。
  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置1,表示该信号集的有效信号包括系统支持的所有信号。
  • sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。
  • 注意,在使用sigset_t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信,这四个函数都是成功返回0,出错返回-1。

 3.5 sigprocmask

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集),成功返回0,失败返回-1。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

参数说明:

how:表示如何修改信号屏蔽字。

set:根据how的参数修改信号屏蔽字的值。

oset:输出型参数,保存信号屏蔽字修改前的值。

假设当前的信号屏蔽字为mask,则how参数如下表。

SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号,相当于mask = mask | set
SIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask = mask&~set
SIG_SETMASK  设置当前信号屏蔽字为set的值,相当于mask = set

3.6 sigpending 

读取当前进程的未决信号集,通过set参数传出,成功返回0,失败返回-1。

#include <signal.h>
int sigpending(sigset_t* set);

四、信号的捕获

4.1 signal()

signal()主要是用于捕获信号,可以改变进程中对信号的默认行为,我们在捕获这个信号后, 也可以自定义对信号的处理行为

使用signal()时,它需要提前设置一个回调函数,即进程接收到信号后将要跳转执行的响应函数, 或者设置忽略某个信号,才能改变信号的默认行为,这个过程称为“信号的捕获”。 对一个信号的“捕获”可以重复进行,不过signal()函数将会返回前一次设置的信号响应函数指针

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

参数说明:

signum:signum是指定捕获的信号名称,如果指定的是一个无效的信号, 或者尝试处理的信号是不可捕获或不可忽略的信号(如SIGKILL),errno将被设置为EINVAL。

handler:handler是一个函数指针,它的类型是 void(*sighandler_t)(int) 类型,拥有一个int类型的参数, 这个参数的作用就是传递收到的信号值,返回类型为void。

signal()函数会返回一个sighandler_t类型的函数指针,这是因为调用signal()函数修改了信号的行为, 需要返回之前的信号处理行为是哪个,以便让应用层知悉, 如果修改信号的默认行为识别则返回对应的错误代码SIG_ERR。

handler需要用户自定义处理信号的方式,当然还可以使用以下宏定义:

  • SIG_IGN:忽略该信号。

  • SIG_DFL:采用系统默认方式处理信号。

注意:如果调用处理程序导致信号被阻塞,则从处理程序返回后, 信号将被解除阻塞。无法捕获或忽略信号SIGKILL和SIGSTOP

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

/* 信号处理函数 */
void signal_handler(int sig)            
{
    printf("\nthis signal number is %d \n",sig);

    if (sig == SIGINT) {
        printf("I have get SIGINT!\n\n");
        /* 恢复信号为默认情况 */
        signal(SIGINT, SIG_DFL);
        printf("default processing has been restored\n");               
    }
}

int main(void)
{
    printf("\nthis is an singal test function\n\n");
    /* 设置信号处理的回调函数 */
    signal(SIGINT, signal_handler);         
    while (1) {
        printf("waiting for the SIGINT signal , please enter \"ctrl + c\"...\n");
        sleep(1);                           
    }
    exit(0);
}

4.2 sigaction()

sigaction()函数原型如下:

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

这个函数的参数比signal()函数多了一些,参数区别如下:

  • signum:指定捕获的信号值。

  • act:是一个结构体,该结构体的内容如下。

struct sigaction {
            void     (*sa_handler)(int);
            void     (*sa_sigaction)(int, siginfo_t *, void *);
            sigset_t   sa_mask;
            int        sa_flags;
            void     (*sa_restorer)(void);
        };
  • sa_handler是一个函数指针,是捕获信号后的处理函数,它也有一个int类型的参数,传入信号的值,这个函数是标准的信号处理函数。

  • sa_sigaction则是扩展信号处理函数,它也是一个函数指针,但它比标准信号处理函数复杂的多, 事实上如果选择扩展接口的话,信号的接收进程不仅可以接收到int型的信号值, 还会接收到一个 siginfo_t类型的结构体指针,还有一个void类型的指针,还有需要注意的就是, 不要同时使用sa_handler和sa_sigaction,因为这两个处理函数是有联合的部分(联合体)。

  • sa_mask是信号屏蔽字,它指定了在执行信号处理函数期间阻塞的信号掩码,被设置在该掩码中的信号, 在进程响应信号期间被临时阻塞。除非使用SA_NODEFER标志,否则即使是当前正在处理的响应的信号再次到来的时候也会被阻塞。

  • re_restorer则是一个已经废弃的成员变量,不要使用。

  • sa_flags是指定一系列用于修改信号处理过程行为的标志,由下面的0个或多个标志组合而成:

    • SA_NOCLDSTOP:如果signum是SIGCHLD,则在子进程停止或恢复时,不会传信号给调用sigaction()函数的进程。 即当它们接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU(停止)中的一种时或接收到SIGCONT(恢复)时, 父进程不会收到通知。仅当为SIGCHLD建立处理程序时,此标志才有意义。

    • SA_NOCLDWAIT:它表示父进程在它的子进程终止时不会收到SIGCHLD 信号, 这时子进程终止则不会成为僵尸进程。

    • SA_NODEFER:不要阻止从其自身的信号处理程序中接收信号,使进程对信号的屏蔽无效, 即在信号处理函数执行期间仍能接收这个信号,仅当建立信号处理程序时,此标志才有意义。

    • SA_RESETHAND:信号处理之后重新设置为默认的处理方式。

    • SA_SIGINFO:指示使用sa_sigaction成员而不是使用sa_handler 成员作为信号处理函数。

  • oldact:返回原有的信号处理参数,一般设置为NULL即可。

上面的成员变量绝大部分我们是几乎使用不到的,因为我们如果是对信号的简单处理,直接使用sa_handler处理即可, 根本无需配置siginfo_t这些比较麻烦的信息。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

/* 信号处理函数 */
void signal_handler(int sig)                    
{
    printf("\nthis signal number is %d \n",sig);
    if (sig == SIGINT) {
        printf("I have get SIGINT!\n\n");
        printf("The signal is automatically restored to the default handler!\n\n");
        /* 信号自动恢复为默认处理函数 */
    }

}

int main(void)
{
    struct sigaction act;

    /* 设置信号处理的回调函数 */
    act.sa_handler = signal_handler;           

    /* 清空屏蔽信号集 */
    sigemptyset(&act.sa_mask);                  

    /* 在处理完信号后恢复默认信号处理 */
    act.sa_flags = SA_RESETHAND;               
    sigaction(SIGINT, &act, NULL);              
    while (1)
    {
        printf("waiting for the SIGINT signal , please enter \"ctrl + c\"...\n\n");
        sleep(1);
    }
    exit(0);
}

 五、信号的发送

5.1 kill()

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

kill()函数的参数有两个,分别是pid与sig,还返回一个int类型的错误码。

  • pid的取值如下:

    • pid > 1:将信号sig发送到进程ID值为pid指定的进程。

    • pid = 0:信号被发送到所有和当前进程在同一个进程组的进程。

    • pid = -1:将sig发送到系统中所有的进程,但进程1(init)除外。

    • pid < -1:将信号sig发送给进程组号为-pid (pid绝对值)的每一个进程。

  • sig:要发送的信号值。

  • 函数返回值:

    • 0:发送成功。

    • -1:发送失败。

进程可以通过调用kill()函数向包括它本身在内的其他进程发送一个信号。 如果程序没有发送该信号的权限,对kill函数的调用就将失败,失败的常见原因是目标进程由另一个用户所拥有。 因此要想发送一个信号,发送进程必须拥有相应的权限,这通常意味着两个进程必须拥有相同的用户ID(即你只能发送信号给属于自己的进程, 但超级用户可以发送信号给任何进程)。

kill()函数会在失败时返回-1并设置errno变量。失败的原因可能是:给定的信号无效(errno设置为INVAL)、 发送进程权限不够(errno设置为EPERM)、目标进程不存在(errno设置为ESRCH)等情况。

5.2 raise()

raise()函数也是发送信号函数,不过与 kill()函数所不同的是, raise()函数只是进程向自身发送信号的,而没有向其他进程发送信号, 可以说kill(getpid(),sig)等同于raise(sig)。

#include <signal.h>

int raise(int sig);

raise()函数只有一个参数sig,它代表着发送的信号值,如果发送成功则返回0,发送失败则返回-1, 发送失败的原因主要是信号无效,因为它只往自身发送信号,不存在权限问题,也不存在目标进程不存在的情况。

 5.3 alarm()

alarm()也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间seconds到时, 它就向进程发送SIGALARM信号。其函数原型如下:

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

如果在seconds秒内再次调用了alarm()函数设置了新的闹钟,则新的设置将覆盖前面的设置, 即之前设置的秒数被新的闹钟时间取代。它的返回值是之前闹钟的剩余秒数,如果之前未设闹钟则返回0。 特别地,如果新的seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。

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

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

相关文章

创新美食体验:从零开始的同城上门做饭APP开发指南

同城上门做饭APP为用户提供了一种全新的用餐方式。本文将带领读者从零开始&#xff0c;探索同城上门做饭APP的开发过程&#xff0c;深入了解技术细节和创新要点。 1.了解用户需求 在着手开发同城上门做饭APP之前&#xff0c;首要任务是深入了解目标用户的需求。调查用户对于美…

直接形式1(三阶)补偿器

直接形式1(三阶)补偿器 直接形式1&#xff08;DF1&#xff09;结构是一种常见类型的离散时间控制结构&#xff0c;用于实现被指定为极点零点集或z&#xff08;传递函数&#xff09;中的有理多项式的控制律。 请注意&#xff0c;系数已被调整以标准化分母中 z 的最高幂。 一般…

【漏洞复现】冰峰VPN存在敏感信息泄露漏洞

漏洞描述 冰峰VPN log/system.log模块日志信息泄露漏洞 免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重社会公德&#xff0c;不得利用网络从事危害国家安全、荣誉和利益&#xff0c;未经授权请勿利…

TinyEngine 服务端正式开源啦!!!

背景介绍 TinyEngine 低代码引擎介绍 随着企业对于低代码开发平台的需求日益增长&#xff0c;急需一个通用的解决方案来满足各种低代码平台的开发需求。正是在这种情况下&#xff0c;低代码引擎应运而生。它是一种通用的开发框架&#xff0c;通过对低代码平台系统常用的功能进…

yolov5简单手势识别

实验目的 实验要求只需要识别五个简单的手势即可&#xff0c;分别对应的一下五个动作 动作对应标签名点赞goodOKok单手比心love数字 5five数字8eight 使用yolov5实现目标检测功能&#xff0c;有一下几个主要步骤 环境配置&#xff08;包括conda、labelimg、yolov5的下载&am…

2023海内外零知识证明学习资料汇总(二)(深入理解零知识证明篇)

工欲善其事,必先利其器 Web3开发中&#xff0c;各种工具、教程、社区、语言框架.。。。 种类繁多&#xff0c;是否有一个包罗万象的工具专注与Web3开发和相关资讯能毕其功于一役&#xff1f; 参见另一篇博文&#x1f449; 2024最全面且有知识深度的web3开发工具、web3学习项目…

PACC:数据中心网络的主动 CNP 生成方案

PACC&#xff1a;数据中心网络的主动 CNP 生成方案 文章目录 PACC&#xff1a;数据中心网络的主动 CNP 生成方案PACC算法CNP数据结构PACC参数仿真结果参考文献 PACC算法 CNP数据结构 PACC参数 仿真结果 PACC Hadoop Load0.2 的情况&#xff1a; PACC Hadoop Load0.4 的情况&a…

旅游平台网页前后端

功能清单 游客功能 用户注册、登录登录权限拦截按名称搜索房间支付流程查看订单信息和状态评论预定过的房间&#xff0c;并自动修改订单状态查看统计剩余房间数量&#xff0c;数量为0时不可预定 管理员功能 房间分类管理 类型的删除、修改、查询&#xff08;准备添加增添功能…

vivo 数据库备份恢复系统演化

作者&#xff1a;vivo 互联网数据库团队 - Han Chaobing 介绍 vivo 数据库备份恢复功能的演化&#xff0c;以及对备份文件的功能扩展。 一、概述 vivo互联网领域拥有的数据库组件分别为 MySQL、MongoDB、TiDB 等&#xff0c;其中MySQL集群占比绝大部分&#xff0c; MongoDB …

轻松提升软件性能:快速学习和使用Memcached

目录 1、前言 2、Memcached的简介 3、Memcached的安装与配置 4、Memcached的数据结构 5、Memcached的常用命令 6、Memcached的高级特性 7、Memcached在系统中如何使用 8、结语 1、前言 Memcached是一个广泛用于提升软件性能的开源内存缓存系统。它可以有效地减少对数据…

iOS问题记录 - iOS 17通过NSUserDefaults设置UserAgent无效(续)

文章目录 前言开发环境问题描述问题分析1. 准备源码2. 定位源码3. 对比源码4. 分析总结 解决方案补充内容1. UserAgent的组成2. UserAgent的设置优先级 最后 前言 在上篇文章中对该问题做了一些判断和猜测&#xff0c;并给出了解决方案。不过&#xff0c;美中不足的是没有进一…

JAVA开发中几个常用的lambda表达式!记得收藏起来哦~

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

关于解决引用第三方依赖突然失效的问题解决办法

目录 背景回顾解决办法结果 背景 出现该问题的背景是这样的。在项目中需要支持加载pdf文档的功能。所以采取了使用第三方PDF库的方法来实现加载pdf文档。集成完后&#xff0c;功能是正常的。后来过了一段时间&#xff0c;发现加载pdf的功能不能正常使用了&#xff0c;加载不出…

聊一下JVM调优

闲聊一下&#xff1a; 这个JVM 相信大家都了解过 但是很少用这个东西 但是面试 一些高级架构师又是必问的一些问题 之前一直不了解这个东西 感觉就是面试造火箭 实际拧螺丝 用于筛选人才 毕业这么多年 也是很少接触这些 就大学的时候学过 简单了解过一些底层 &#xff0c;找工…

计算字符串的长度几种方法 | 递归 | 指针减指针 | 计数器 | C语言 | 详解 | 期末考试必看!!!

一&#xff0c;使用 递归 计算 字符串 的 长度 1&#xff0c;题目描述 2&#xff0c;分析题目 Ⅰ&#xff0c;题目中要求除了函数的形参&#xff0c;函数中不能够使用多余的变量&#xff08;这是比较苛刻的要求&#xff09;。 Ⅱ&#xff0c;根据此&#xff0c;很自然的…

【Java系列】文件操作详解

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 …

Ubuntu之修改时区/时间

1、查看当前时间及时区状态 sudo timedatectl status # 显示当前时区为Asia/Shanghai 2、查看当前系统时间 sudo date 3、查看当前系统时间及时区 sudo date -R # 显示当前时间及对应时区&#xff0c;时区为“0800”北京时区 4、修改硬件时间 修改日期格式&#xff1a…

Attention机制

前置知识&#xff1a;RNN&#xff0c;LSTM/GRU 提出背景 Attention模型是基于Encoder-Decoder框架提出的。Encoder-Decoder框架&#xff0c;也就是编码-解码框架&#xff0c;主要被用来处理序列-序列问题。 Encoder&#xff1a;编码器&#xff0c;将输入的序列<x1,x2,x3……

【docker】安装 Redis

查看可用的 redis版本 docker search redis拉取 redis最新镜像 docker pull redis:latest查看本地镜像 docker images创建挂在文件 mkdir -pv /test1/docker_volume/redis/datamkdir -pv /test1/docker_volume/redis/confcd /test1/docker_volume/redis/conf/touch redis.con…