Linux信号详解

文章目录

  • 一、Linux信号
    • 1. 信号的概念
    • 2. 信号的定义
    • 3. 系统定义的信号
  • 二、信号产生的方式
    • 1.通过键盘产生
    • 2. 通过系统调用
    • 3. 软件条件
    • 4. 硬件异常
  • 三、信号处理函数
    • 1. OS发送信号的实质
    • 2. 指令发送信号
    • 3. signal()
    • 4. sigaction()
  • 四、信号屏蔽机制
    • 1. 信号处理方式
    • 2.信号集操作函数
  • 五、临界资源和临界区

  Linux 信号是操作系统中的重要组成部分,可以用于进程间通信、处理异常等多种场景。本文将深入介绍 Linux 信号的相关知识,包括信号的定义、类型、发送和接收、处理等内容,帮助读者更好地理解和使用 Linux 信号

一、Linux信号

1. 信号的概念

生活角度的信号

  • 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”
  • 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  • 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”
  • 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种
      1. 执行默认动作(幸福的打开快递,使用商品)
      1. 执行自定义动作(快递是零食,你要送给你你的女朋友)
      1. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
  • 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

技术应用角度的信号

  • 用户输入命令,在Shell下启动一个前台进程。 用户按下Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
  • 前台进程因为收到信号,进而引起进程退出


2. 信号的定义

信号是 Linux 操作系统中用于进程间通信、处理异常等情况的一种机制。它是由操作系统向一个进程或者线程发送的一种异步通知,用于通知该进程或线程某种事件已经发生,需要做出相应的处理。

信号的作用:

  • 进程间通信:进程可以通过向其他进程发送信号的方式进行通信,例如某个进程在完成了某项工作之后,可以向另一个进程发送 SIGUSR1 信号,通知其进行下一步的操作。

  • 处理异常:信号可以被用来处理程序中的异常情况,例如当一个进程尝试访问未分配的内存或者除以 0 时,系统会向该进程发送 SIGSEGV 或 SIGFPE 信号,用于处理这些异常情况。

  • 系统调试:信号可以用于程序的调试,例如在程序运行时,可以向该进程发送 SIGUSR2 信号,用于打印程序的状态信息等。

3. 系统定义的信号

Linux 中,信号分为标准信号实时信号,每个信号都有一个唯一的编号。

  • 标准信号:最基本的信号类型,由整数编号表示,编号范围是 1 到 31。
  • 实时信号:Linux 中的扩展信号类型,由整数编号表示,编号范围是 32 到 64。

可以用kill -l命令可以察看系统定义的信号列表

kill -l

查询结果如下:
在这里插入图片描述

常见信号编号以及对应信号的名称:
在这里插入图片描述

注意不同的操作系统可能对信号的编号有所不同,因此在跨平台开发时应当注意信号编号的兼容性。

  • term 表示终止进程
  • core 表示生成核心转储文件,核心转储文件可用于调试
  • ignore 表示忽略信号
  • cont 表示继续运行进程
  • stop 表示停止进程(注意停止不等于终止,而是暂停)

相关信号的解读:

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

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

4) SIGILL
执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。

5) SIGTRAP
由断点指令或其它trap指令产生. 由debugger使用。

6) SIGABRT
调用abort函数生成的信号。

7) SIGBUS
非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)8) SIGFPE
在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。

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

10) SIGUSR1
留给用户使用

11) SIGSEGV
试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.

12) SIGUSR2
留给用户使用

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

14) SIGALRM
时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.

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

17) SIGCHLD
子进程结束时, 父进程会收到这个信号。
如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)18) SIGCONT
让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符

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

20) SIGTSTP
停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号

21) SIGTTIN
当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.

22) SIGTTOU
类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.

23) SIGURG
有"紧急"数据或out-of-band数据到达socket时产生.

24) SIGXCPU
超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。

25) SIGXFSZ
当进程企图扩大文件以至于超过文件大小资源限制。

26) SIGVTALRM
虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.

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

28) SIGWINCH
窗口大小改变时发出.

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

30) SIGPWR
Power failure

31) SIGSYS
非法的系统调用。
  • 在以上列出的信号中,程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
  • 不能恢复至默认动作的信号有:SIGILL,SIGTRAP
  • 默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
  • 默认会导致进程退出的信号有:
    SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
  • 默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
  • 默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
  • 此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞。

二、信号产生的方式

1.通过键盘产生

Ctrl + C 

通过键盘敲入 Ctrl + C ,可以向进程发送 2 号信号使其终止。

2. 通过系统调用

① kill

#include<signal.h>
#include<sys/types.h>
 
int kill(int pid,int signal);  //通过函数向指定进程发送信号

② raise

raise接口可以向当前调用进程发送任意信号

可以给当前进程发送指定的信号,即自己给自己发信号

#include <stdio.h>
#include <signal.h>

raise(11);   //向当前进程发送 11 号信号

③ abort

使当前进程接收到信号而异常终止

#include <stdio.h>
#include <stdlib.h>

abort();

3. 软件条件

alarm

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

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

打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

#include <stdio.h>
#include <unistd.h>

int main()
{
    int ret = alarm(20);

    while (1) {
        printf("I am a process, ret = %d\n", ret);
        sleep(5);

        int res = alarm(0); //取消闹钟
        printf("res = %d\n", res);
    }

    return 0;
}

4. 硬件异常

#include <stdio.h>
#include <signal.h>
 
void handler(int sig)
 {
    printf("catch a sig : %d\n", sig);
 }
 
int main()
 {
    signal(SIGSEGV, handler);
    sleep(1);
    int *p = NULL;
    *p = 100;
 
    while(1);
    return 0;
 }

这里出现野指针异常。系统本该直接清除掉该进程,但因为修改了野指针异常所对应的操作函数,导致系统只打印了一句话,而不正常的对该进程进行清除。但系统不断地检测到有野指针异常。但操作系统做的工作仅仅是打印一句话。那么这里就造成了循环打印catch a sig : xxx

在这里插入图片描述

三、信号处理函数

1. OS发送信号的实质

信号产生的方式种类虽然非常多,但是无论产生信号的方式千差万别,但是最终一定都是通过OS向目标进程发送信号。产生信号的方式,其实都是OS发送信号数据给task_struct

struct task_struct 中有进程的各种属性,那么其中也一定有对应的数据变量,来保存是否收到了对应的信号,而信号的编号也是有规律的1~31

进程中采用 uint32_t sigs ;——位图结构来标识该进程是否收到信号

0000 0000 0000 0000 0000 0000 0000 0000

比特位的位置(第几个)代表的就是哪一个信号,比特位的内容(0或1),代表的就是是否收到了信号

故本质是OS向指定进程的task_struct中的信号位图写入比特1,即完成信号的发送,也可以说是信号的写入

struct task_struct
{
	//信号位图
	0000 0000 10...
	uint32_t sigmap;
}

2. 指令发送信号

  1. kill 命令

    kill 命令是 Linux 中最常用的发送信号的命令,语法如下:

     kill [-signal] PID
    

    其中,-signal 可选参数表示要发送的信号类型,如果省略该参数,则默认发送 SIGTERM 信号。PID 表示接收信号的进程 ID。

    例如,要向进程 ID 101080 发送 SIGINT 信号,可以执行以下命令:

    kill -SIGINT 101080
    kill -2 101080
    

    当然可以发送对应的信号编号

  2. kill 函数

    除了使用 kill 命令,程序中也可以通过 kill 函数来发送信号。kill 函数的原型如下:

    int kill(pid_t pid, int sig);
    

    其中,pid 表示接收信号的进程 ID,sig 表示要发送的信号类型。如果函数调用成功,则返回 0,否则返回 -1 并设置 errno。

    例如,要向进程 ID 123 发送 SIGINT 信号,可以执行以下代码:

    #include <signal.h>
    #include <unistd.h>
    
    int main() 
    {
        pid_t pid = 123;
        int sig = SIGINT;
        if (kill(pid, sig) == -1) 
        {
       		perror("kill");
       		return 1;
     	}
         return 0;
    }
    

3. signal()

signal 函数可以用来自定义信号捕捉函数

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

sighandler_t signal(int signum, sighandler_t handler);


//示例
void handler(int signum)
{
    cout<<"get a signum: "<<signum<<endl;
}

int main()
{
    signal(14,handler);  //此时进程收到14号信号的默认处理动作就是执行handler函数
}

signal 函数的两个宏:

signal(2,SIG_DFL);   //default 使对应的2号信号恢复默认动作
signal(2,SIG_IGN);   //ignore 忽略二号信号
  1. 参数:

    • signum:此参数指定需要进行设置的信号,可使用信号名(宏)或信号的数字编号,建议使用信号名。
    • handler:sighandler_t 类型的函数指针,指向信号对应的信号处理函数,当进程接收到信号后会自动执行该处理函数;参数 handler 既可以设置为用户自定义的函数,也就是捕获信号时需要执行的处理函数,也可以设 置为 SIG_IGN 或 SIG_DFL,SIG_IGN 表示此进程需要忽略该信号,SIG_DFL 则表示设置为系统默认操作。
    • sighandler_t 函数指针的 int 类型参数指的是,当前触发该函数的信号,可将多个信号绑定到同一个信号处理函数 上,此时就可通过此参数来判断当前触发的是哪个信号。
  2. 返回值:此函数的返回值也是一个 sig_t 类型的函数指针

    • 成功:返回值则是指向在此之前的信号处理函数;
    • 出错:则返回 SIG_ERR,并会设置 errno。

​ 由此可知,signal()函数可以根据第二个参数 handler 的不同设置情况,可对信号进行不同的处理。

4. sigaction()

在Linux中,sigaction函数是用于设置和检索信号处理器的函数。

sigaction函数有以下语法:

#include <signal.h>
 
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

其中,signum 表示要注册的信号编号,act 是一个指向 struct sigaction 结构体的指针,表示新的信号处理函数和信号处理选项,oldact 是一个指向 struct sigaction 结构体的指针,用于获取之前注册的信号处理函数和信号处理选项。

struct sigaction 结构体的定义如下:

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:指定信号处理函数的地址。如果设置为SIG_IGN,则表示忽略该信号。如果设置为SIG_DFL,则表示使用默认处理器,也可以自己设置需处理的函数逻辑。

  • sa_sigaction:指定一个信号处理器函数,这个函数包含三个参数:一个整数表示信号编号,一个指向siginfo_t结构体的指针,和一个指向void类型的指针。

  • sa_mask:指定了在执行信号处理函数期间要阻塞哪些信号。

  • sa_flags:是一个标志位,可以包括以下值:

    • SA_NOCLDSTOP:如果设置了该标志,则当子进程停止或恢复时不会生成SIGCHLD信号。
    • SA_RESTART:如果设置了该标志,则系统调用在接收到信号后将被自动重启。
    • SA_SIGINFO:如果设置了该标志,则使用sa_sigaction字段中指定的信号处理器。
  • sa_restorer:是一个指向恢复函数的指针,用于恢复某些机器状态。

返回值:如果成功,则返回0,否则返回-1,并设置errno错误号。可以使用以下代码来检查errno

注意:一般情况下,sa_handlersa_mask 使用较多,这里再次详细说明:

  1. sa_handler:

    • 赋值为常数SIG_IGN表示忽略信号

    • 赋值为常数SIG_DFL表示执行系统默认动作

    • 赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数

      该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信 号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

  2. sa_mask:

    • 当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来 的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。
    • 如果 在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号, 则用sa_mask字段说明这些需 要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

    注意:信号在被处理前,pendding位图就修改了对应的比特位

四、信号屏蔽机制

1. 信号处理方式

  1. 信号的处理方式有三种,称为信号的递达

    • 信号的忽略( SIG_IGN ),即不采取任何动作
    • 信号的默认,使用系统默认的处理方法
    • 信号的自定义捕捉,采用用户定义的方法处理
  2. 信号从产生到递达之间的状态,称为信号的未决( Pending )

    • 信号在位图中,未被处理(未决)
  3. 进程选择阻塞( Block )某个信号

    • 未决之后,暂时不递达,直到解除对信号的阻塞,期间对该信号为屏蔽

信号在内核中的表示示意图:

  1. block 信号阻塞表

    在该表中标记为 1 的信号将被阻塞

  2. pending 信号未决表

    信号被进程接收,但还未被处理的信号,就存放在该表中

    • 普通信号:只在pending表记录一次,如果产生多次,也只记录一次,之后系统对该信号的处理动作为 1 次
    • 实施信号:采用队列的形式,记录历史中产生的所有信号,后系统对该信号的处理动作为 1历史中产生的次数

    注意:pendding 表存在多个信号时,会全部处理完,才返回用户端

  3. bandler 信号对应处理方法表

    该表中记录了信号对应操作方法的函数指针

2.信号集操作函数

头文件:

#include <signal.h>  //头文件
  1. sigemptyset

    int sigemptyset(sigset_t *set);
    

    初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。

  2. sigemptyset

    int sigemptyset(sigset_t *set);
    

    函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。

  3. sigfillset

    int sigfillset(sigset_t *set);
    

    函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。

  4. sigaddset

    int sigaddset (sigset_t *set, int signo); 
    

    添加屏蔽信号

  5. sigdelset

    int sigdelset(sigset_t *set, int signo);
    

    删除屏蔽信号

  6. sigismember

    int sigismember(const sigset_t *set, int signo); //查询是否有该屏蔽信号
    
    示例:
    sigset_t  set oset;
    sigismember(pending , int signo)
    

    查询是否有该屏蔽信号

  7. sigprocmask

    int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
    

    示例:

    sigset_t  set, oset;
    sigprocmask(SIG_BLOCK, &set, &oset);
    
    1. 参数:
    • how

      SIG_BLOCK    //添加屏蔽字
      SIG_UNBLOCK  //删除
      SIG_SETMASK  //直接用新的替换
      
    • const sigset_t

      当前需要修改的 sigset_t

    • oset

      传出之前的pending码

    使用示例:

    int main()
    {
    sigset_t set, oset;
    // 初始化,全置空
    //  sigfillset 全置1
    sigemptyset(&set);
    sigemptyset(&oset);
    
    // 设置屏蔽字
    //  sigdelset 清除
    sigaddset(&set, 2); // 对于set位图,对2号信号设置屏蔽字
    
    // 将 set 信号设置进入当前进程
    
    
    sigprocmask(SIG_BLOCK, &set, &oset); // oset为之前的屏蔽字  
    }
    
  8. sigpending

    int sigpending(sigset_t *set);
    
    示例:
    sigset_t pending;
    sigemptyset(&pending);
    
    • 功能:读取当前进程的未决信号集,通过set参数传出。调用成功则返回 0 ,出错则返回 -1

    示例:

     //通过此中方式读出
     void Printsignal (const sigset_t &pending)
    {
       for(int signo = 31; signo > 0; signo--)
      {
          if(sigismember(&pending, signo))
          {
              std::cout << "1";
          }
          else
          {
              std::cout << "0";
          }
      }
      std::cout << "\n";
     }
    

五、临界资源和临界区

  1. 临界资源

    • 概念:被保护起来的公共资源,一次仅允许一个进程使用的共享资源。其他都是非临界资源
  2. 临界区

    • 概念:每个进程中访问临界资源的那段程序(代码)称之为临界区

    • 临界区不是内核对象,而是系统提供的一种数据结构,程序中可以声明一个该类型的变量,之后用它来实现对资源的互斥访问。

      ​ 当欲访问某一临界资源时,先将该临界区加锁(若临界区不空闲则等待),用完该资源后,将临界区释放。

    • 补充(待定):分类:临界区也是代码的称呼,所以一个进程可能有多个临界区,分别用来访问不同的临界资源。

      • 内核程序临界资源:系统时钟

      • 普通临界资源:普通I/O设备,如打印机(进程访问这些资源的时候,很慢,会自动阻塞,等待资源使用完成)

  3. 信号量

    ​ 表示资源数目的计数器。每一个执行流想访问公共资源内的某一份资源,不应该让执行流直接访问,而是先申请信号量。资源其实就是对信号量计数器进行自减操作,本质上只要自减成功。就完成了对资源的预定机制。如果申请不成功,执行流将被挂起阻塞。

  4. 进程进入临界区的调度原则

    • 如果有若干进程请求进入空闲的临界区(空闲即0进程访问),一次仅允许 一个进程进入。
    • 任何时候,处于临界区内的进程不可多于一个(0 或 1),若已有进程进入自己的临界区,则其它想进入自己临界区的进程必须等待。
    • 进行临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
    • 如果其它进程不能进入自己的临界区,则应让出 CPU,避免进程出现 “忙等” 现象。
    • 访问临界资源时先访问信号量资源(可以理解为信号量引用计数自减1,如果自减成功,说明还能访问,未达到上限,允许访问,访问结束后信号量自增1)
    • 临界资源的访问是原子的
  5. 细节问题

    • 每个进程都得先看到同一个信号量资源,就只能由OS提供IPC体系。

    • 信号量本身也是公共资源。

    • 单个信号量

      struct sem
      {
          int count;  //引用计数
          task_struct *wait_queue;  //进程等待队列
      }
      
      //如果当前引用计数不为零,那么新的进程将会直接被运行。
      //如果当前引用计数为零,那么新的进程将会加入等待队列。当别的进程结束时,引用计数自增,同时等待队列中的进程将会被执行,同时引用计数自减
      

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

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

相关文章

python学习26

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

数字化转型导师坚鹏:政府数字化转型智慧城市类案例研究

政府数字化转型智慧城市类案例研究 课程背景&#xff1a; 很多地方政府存在以下问题&#xff1a; 不清楚政府数字化转型的智慧城市类成功案例 不清楚政府数字化转型的城市大脑类成功案例 不清楚政府数字化转型的综合实践类成功案例 课程特色&#xff1a; 针对性强 …

【uniapp】uniapp开发的微信公众号,微信设置字体大小或者关怀模式,页面布局字体大小不受影响的解决方法:

文章目录 一、问题及效果&#xff1a;二、解决&#xff1a; 一、问题及效果&#xff1a; 二、解决&#xff1a; 在uniapp的app.vue的script标签内添加以下代码&#xff1a; (function(){//安卓端function handleFontSize () {// 设置网页字体为默认大小WeixinJSBridge.invoke…

Redis+Caffeine 太强了!二级缓存可以这样实现!

在实际的项目中&#xff0c;我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中&#xff0c;只有当缓存的访问没有命中时再查询数据库。 在一些场景下可能还需要进一步配合本地缓存使用&#xff0c;例如Guava cache或Caffeine&#xff0c;从而再次提升程序的响应…

力扣--双指针167.二数之和Ⅱ

这题一个穷举方法是比较好想到的&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& numbers, int target) {int i,j;int nnumbers.size();vector<int>result(2,0);for(i0;i<n-1;i){for(ji1;j<n;j){if(numbers[i]numbers[j…

win10安装使用AxurePR9

背景&#xff1a;win10 安装、汉化 Axure Pr9 下载 安装包 链接&#xff1a;https://pan.baidu.com/s/1taMgh2zLbaFK7VTfUXTHdQ 提取码&#xff1a;kygo 安装 修改安装目录 打开是英文的 汉化 复制lang包到Axure安装包 再打开就是中文 问题 发布html后火狐无法打开 一、…

[计算机网络]--IP协议

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、IP协议…

C/C++文件操作

一、文本文件操作 1、写文件操作 代码 #include<fstream> #include<iostream>int main() {ofstream outfile("Student.txt", ios::out);if (!outfile) {cout << "文件写入失败" << endl;exit(0); //程序终止}cout << &qu…

学算法要读《算法导论》吗?

大家好&#xff0c;我是 方圆。这篇文章是我学习算法的心得&#xff0c;希望它能够给一些将要学习算法且准备要读大部头算法书籍的朋友一些参考&#xff0c;节省一些时间&#xff0c;也为了给经典的“黑皮书”祛魅&#xff0c;我觉得这些书籍在大部分互联网从业者心中已经不再是…

PHP中的飞碟运算符、取反运算符、对比非ASCII字符串、对比浮点数操作

对比浮点数 在电脑里存储的浮点数可能会和输入的值有些许差异&#xff0c;比如输入的是10.0&#xff0c;但存储的是10.00001. 在比较两个浮点数是否相等时可以计算下两个数的差值&#xff0c;然后查看下两数之差是否小于可以接受的阈值&#xff0c;如果要求精度在小数点后5位的…

如何实现固定公网地址远程访问内网Wagtail管理界面

文章目录 前言1. 安装并运行Wagtail1.1 创建并激活虚拟环境 2. 安装cpolar内网穿透工具3. 实现Wagtail公网访问4. 固定的Wagtail公网地址 前言 Wagtail是一个用Python编写的开源CMS&#xff0c;建立在Django Web框架上。Wagtail 是一个基于 Django 的开源内容管理系统&#xf…

【总结】Maxwell学习笔记

1.Maxwell简介 Maxwell 是一款用Java编写的MySQL变更数据抓取软件&#xff0c;它会实时监控Mysql数据库的数据变更操作&#xff08;包括insert、update、delete&#xff09;&#xff0c;并将变更数据以 JSON 格式发送给 Kafka、Kinesi等流数据处理平台 官网地址&#xff1a;M…

代码随想录算法训练营第四三天 | 最后一块石头的重量 II、目标和、一和零

目录 最后一块石头的重量 II目标和一和零 LeetCode 1049. 最后一块石头的重量 II LeetCode 494. 目标和 LeetCode 474.一和零 最后一块石头的重量 II class Solution {// dp[j] 容量为j 的背包&#xff0c;最多可以背最大重量为dp[j]。// dp[j] Math.max(dp[j], dp[j - sto…

索引学习以及索引原理

有时候&#xff0c;建索引并不一定会加快查询效率。但是&#xff0c;有时候&#xff0c;表的数据量是大数据量的话&#xff0c;还是要看下是否能使用索引优化查询效率。 1、建索引的几大原则&#xff1a; 1.1、最左前缀匹配原则非常重要的原则&#xff0c;mysql会一直向右匹配…

猫头虎分享已解决Bug || AttributeError: ‘Sequential‘ object has no attribute ‘session‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

大数据开发项目--音乐排行榜

环境&#xff1a;windows10&#xff0c;centos7.9&#xff0c;hadoop3.2、hbase2.5.3和zookeeper3.8完全分布式&#xff1b; 环境搭建具体操作请参考以下文章&#xff1a; CentOS7 Hadoop3.X完全分布式环境搭建 Hadoop3.x完全分布式环境搭建Zookeeper和Hbase 1. 集成MapReduce…

猫头虎分享已解决Bug || Error: Maximum update depth exceeded in React

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【Crypto | CTF】BugKu 简单的RSA

天命&#xff1a;这题也不算简单了&#xff0c;要反编译&#xff0c;要灵活一点 首先收到pyc文件&#xff0c;拿去反编译出来&#xff0c;可以用在线反编译&#xff0c;也可以用工具反编译 在线&#xff1a;python反编译 - 在线工具 工具&#xff1a;https://download.csdn.n…

【算法小讲堂】#1 贪心算法

引入——关于贪心算法 我们先来做一个小游戏——现在假设自己是一个小偷&#xff0c;桌上有一些物品&#xff0c;包括一台iPhone15、一个充电宝、一个眼罩和一个溜溜梅。此时&#xff0c;你听说警察即将到来&#xff0c;那么你会先带走哪个东西呢&#xff1f; 一般来讲&#xf…

c++数据结构算法复习基础--1

一、大体复习内容 复习思路&#xff1b; 二、数据结构算法-常见复杂度汇总介绍-性能对比-图表展示 数据结构: 相互之间存在一种或者多种特定关系的数据元素的集合。在逻辑上可以分为线性结构&#xff0c;散列结构、树形结构&#xff0c;图形结构等等。 数据结构说的是组织…