Linux系统 -- 信号

一 信号的概念

   在我们的日常生活中,红绿灯其实也叫信号灯,因此,我们将用红绿灯给大家讲解一下信号的概念。

  当你开着车经过红绿灯路口时,你也在收到红绿灯给你的通知,假设是红灯,那么你就需要停下来等待,如果是黄灯,那就可以看看地区,考虑一下冲不冲(建议等待),绿灯就可以直接通过。

  也就是说,你能”识别红绿灯的颜色作用“。

  同时,当你在等待绿灯的时候或者红灯刚好变绿的时候,你刚好在喝最后一口水,那你就可以喝完再走(容易挨骂)。

  也就是说,你可以“在合适的是时间去通过红绿灯。

  在你收到红灯变绿的这个变化的时候,在这段时间,你并没有看到绿灯,但是你知道绿灯马上就要来了。本质上是你“知道马上要有绿灯”。

  当你的时间合适,并且刚好有绿灯,就要开始处理。而处理绿灯的方式一般有三种:

  • 1. 执行默认动作(冲!!!)
  • 2. 执行自定义动作(有点渴,喝口水)
  • 3. 忽略绿灯(你是副驾驶

  绿灯到来的整个过程,对你来说是异步的,你不需要关注红灯是如何变绿的,你可以做一些你想做的事情。

ps:诸如此类的还有快递到达通知,火灾警报等待。

  在了解了此类场景之后,我们就会发现信号拥有以下特点。

  • 1.在我们人类的大脑中能够识别这个信号
  • 2.如果特定的信号没有产生,但是我们依旧知道我们应该如何处理这个信号
  • 3.我们在接受这个信号的时候,可能不会立即处理这个信号
  • 4.信号本身在我们无法立即被处理的时候,也一定要先被临时地记住

  二 Linux中信号的概念

2.1 信号在Linux中的概念

  通过上面的解读,我们大概已经理解了,信号其实就是一种通知行为,通知进程,某些事件已经发生了,可以根据情况进行处理了。

结论:

1.进程要处理信号,必须具备信号“识别”的能力。即 看到,知道,能够处理。 
2.信号是随机产生的,进程可能正在忙自己的业务,所以信号的处理可能并不是被立即处理的
3.信号会临时地记录一下对应的信号,一般在合适的时候会进行处理。
4.一般而言,对于进程而言信号的产生是异步的。

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

2.2 Linux中的信号

  在linux命令行中,可以通过kill -l命令查看所有信号。

  1~31是普通信号,34~64是带有RT的实时信号。信号的种类及其繁多,目前的阶段我们只需要记住几个常用的 普通信号 就可以了。

   信号的本质其实是一种宏,在这里我们可以通过源代码提前了解以下:

    

信号编号信号名称描述
1SIGHUP控制终端挂起或者断开连接
2SIGINT中断信号,通常由 Ctrl+C 发送
3SIGQUIT退出信号,通常由 Ctrl+\ 发送
4SIGILL非法指令信号
5SIGTRAP跟踪异常信号
6SIGABRT中止信号
7SIGBUS总线错误信号
8SIGFPE浮点错误信号
9SIGKILL强制退出信号
10SIGUSR1用户定义信号1
11SIGSEGV段错误信号
12SIGUSR2用户定义信号2
13SIGPIPE管道破裂信号
14SIGALRM闹钟信号
15SIGTERM终止信号
16SIGSTKFLT协处理器栈错误信号
17SIGCHLD子进程状态改变信号
18SIGCONT继续执行信号
19SIGSTOP暂停进程信号
20SIGTSTP终端停止信号
21SIGTTIN后台进程尝试读取终端输入信号
22SIGTTOU后台进程尝试写入终端输出信号
23SIGURG套接字上的紧急数据可读信号
24SIGXCPU超时信号
25SIGXFSZ文件大小限制超出信号
26SIGVTALRM虚拟定时器信号
27SIGPROF分析器定时器信号
28SIGWINCH窗口大小变化信号
29SIGIO文件描述符上就绪信号
30SIGPWR电源失效信号
31SIGSYS非法系统调用信号
32SIGRTMIN实时信号最小编号
.........
64SIGRTMAX实时信号最大编号

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

2.3 信号的阻塞,递达,未决

  • 信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。
  • 信号的 “阻塞” 是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
  • 信号的”递达“指处理信号的过程。

 信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号(一般称为忽略),所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

   2.4 信号集的概念

  •   许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t。
  •   在 PCB 中有两个非常重要的信号集。一个称之为 “阻塞信号集” ,另一个称之为“未决信号集” 。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改。(除了这两个信号集之外,还有一个对应的处理函数的集合)

block为阻塞信号集,pending为未决信号集,handler为处理函数地址集合。

 2.5 信号集的操作

 2.5.1 信号集增减操作 

#include <signal.h>

int sigemptyset(sigset_t *set);//将set集合置空
int sigaddset(sigset_t *set,int signo);//将signo信号加入到set集合
int sigdelset(sigset_t *set,int signo);//从set集合中移除signo信号
int sigfillset(sigset_t *set); //将所有信号加入set集合

//set为操作的信号集
//signo表示操作的信号
//返回值:若成功,返回0,若出错,返回-1
 

 2.5.2 检查信号是否已经加入信号集

   int sigismember(const sigset_t *set, int signum);

   //测试参数signum 代表的信号是否已加入至参数set信号集里
   //返回值:若成功,返回0,若出错,返回-1
 

2.5.3 设置信号集的阻塞

  调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//可以根据参数指定的方法修改进程的信号屏蔽字。
//新的信号屏蔽字由参数set(非空)指定,而原先的信号屏蔽字将保存在oset(非空)中。
//如果set为空,则how没有意义,但此时调用该函数,如果oset不为空,则把当前信号屏蔽字保存到oset中。
 

/* 底层系统调用的原型 */
int rt_sigprocmask(int how, const kernel_sigset_t *set,kernel_sigset_t *oldset, size_t sigsetsize);
       /* Prototype for the legacy system call (deprecated) */
 
 

 参数:

how:不同取值及操作如下所示:

注:调用这个函数才能改变进程的屏蔽字,之前的函数都是为改变一个变量的值而已,并不会真正影响进程的屏蔽字。

  1.  SIG_BLOCK :    附加set到阻塞表,原来的保存在到oldset
  2. SIG_UNBLOCK:从阻塞表中删除set中的信号,原来的保存到oldset
  3. SIG_SETMASK:清空阻塞表并设置为set,原来的保存到oldset

返回值:

        若成功,返回0

        若出错(how取值无效返回-1),返回-1,并设置errno为EINVAL。

2.5.4 读取未决信号集合 

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

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

2.5.5 示例

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


void printsigset(sigset_t *set){
    
    for(int i=31;i>=0;i--){
       if(sigismember(set,i)){
          putchar('1');
       }
       else{
         putchar('0');
       }
    }
    printf("\n");
}//打印未决信号集合

int main(){

    sigset_t s,p;
    sigemptyset(&s);//清空s信号集
    sigaddset(&s,SIGINT);//将2号信号加入s信号集

    sigprocmask(SIG_BLOCK,&s,NULL);//将阻塞信号集内容变为s信号集
    while(1){
        sigpending(&p);//获取未决信号集
        printsigset(&p);//一直打印未决信号集合
        sleep(1);
    }

    return 0;
}

三 Linux中信号的产生,发送,接收

 3.1 信号的产生

  首先我们要理解,在我们的日常生活中,接收信号的主体一般是人,但是在操作系统中,接收信号的主体就变得多了起来。

  首先我们要知道,信号是一种通知,当进程向计算机申请资源时,进程发出这个通知,操作系统给予进程资源,也需要发出给予的通知,一般而言,信号的使用主体大多是:人,操作系统,进程。

  人,也就是程序员,通过键盘发出信号,以此来实现对操作系统或者是进程的操控。

  操作系统则通过信号,协调自己的内部,实现统筹管理。

  进程则通过信号,汇报内部错误或者是进行通信,以此来完成任务。

  因此,信号的产生是有多样性的,由通过键盘、系统调用、软件条件、硬件异常产生等等方法产生信号。

 3.2 信号的发送

  这里我们介绍几种程序员常用的发送信号的方法:

3.2.1  kill 命令

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

kill  [-signal]  进程PID

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

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

kill -SIGINT 123

3.2.2  kill 函数

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

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

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.2.3  raise 函数

  raise 函数是一个简单的发送信号的函数,可以用来向当前进程发送信号。raise 函数的原型如下:

int raise(int sig);
//其中,sig 表示要发送的信号类型.
//如果函数调用成功,则返回 0,否则返回 -1 并设置 errno。

例如,要向当前进程发送 SIGTERM 信号,可以执行以下代码:

#include <signal.h>
 
int main() {
  int sig = SIGTERM;
  if (raise(sig) == -1) {
    perror("raise");
    return 1;
  }
  return 0;
}

3.2.4  pthread_kill 函数

  如果在多线程程序中需要向另一个线程发送信号,可以使用 pthread_kill 函数。pthread_kill 函数的原型如下:

int pthread_kill(pthread_t thread, int sig);
//其中,thread 表示接收信号的线程 ID
//sig 表示要发送的信号类型
//如果函数调用成功,则返回 0,否则返回错误码。

例如,要向线程 ID 456 发送 SIGUSR1 信号,可以执行以下代码: 

#include <pthread.h>
#include <signal.h>
 
void* thread_func(void* arg) {
  // 线程函数
  return NULL;
}
 
int main() {
  pthread_t tid = 456;
  int sig = SIGUSR1;
  if (pthread_kill(tid, sig) != 0) {
    perror("pthread_kill");
    return 1;
  }
  return 0;
}

3.3 信号的接收

  一般而言,我们的操作系统对常见信号都有默认的接收处理方式,当时,我们也可以通过一些函数,对一些信号进行自定义接收。 

 3.3.1 signal函数

  signal 函数可以用来创建信号处理函数。signal 函数的原型如下:

   #include <signal.h>

   typedef void (*sighandler_t)(int);

   sighandler_t signal(int signum, sighandler_t handler);

   //其中,signum 表示要注册的信号类型
   //handler 是一个函数指针,指向信号处理函数
   //signal 函数返回一个函数指针,指向之前注册的信号处理函数。如果注册信号处理函数失败,则返回 SIG_ERR。

 示例,要自定义接收注册 SIGINT 信号的处理函数,可以执行以下代码:
 

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


void handler(int sig){
    std::cout<<"触发事件信号值"<<sig<<std::endl;
}
int main(){
    
    std::cout<<"当前进程pid为:"<<getpid()<<std::endl;
    signal(SIGINT,handler);

    while(true){
        std::cout<<"进程仍在执行,pid:"<<getpid()<<std::endl;
        sleep(1);
    }

    return 0;
}

果然我们发送2号信号不再退出程序,而是打印了一段话,我们用其它信号中断程序,成功中断

3.3.2  sigaction 函数

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

  #include <signal.h>

  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  
  signum 表示要注册的信号类型
  act 是一个指向 struct sigaction 结构体的指针,表示新的信号处理函数和信号处理选项
  oldact 是一个指向 struct sigaction 结构体的指针,用于获取之前注册的信号处理函数和信号处理选项。

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字段是一个标志位,可以包括以下值:

  1.     SA_NOCLDSTOP:如果设置了该标志,则当子进程停止或恢复时不会生成SIGCHLD信号。
  2.     SA_RESTART:如果设置了该标志,则系统调用在接收到信号后将被自动重启。
  3.     SA_SIGINFO:如果设置了该标志,则使用sa_sigaction字段中指定的信号处理器。


  sa_restorer字段是一个指向恢复函数的指针,用于恢复某些机器状态。

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

  现在我们只需要理解sa_handler的用法就可以了。

例如,要注册 SIGINT 信号的处理函数,可以执行以下代码:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<cstdio>
void sigint_handler(int sig){
  std::cout<<"引起异常的信号为:"<<sig<<std::endl;
}

int main(){

    struct sigaction newact,oldact;

    newact.sa_handler = sigint_handler;

    if(sigaction(SIGINT,&newact,&oldact)==-1){
       perror("sigaction");
       return 1;
    }
    
    while(true){
        sleep(1);
        std::cout<<"当前进程pid:"<<getpid()<<std::endl;
    }
    return 0;
}

  在使用sigaction函数之前,应该先定义一个信号处理函数并将其注册。可以使用signal函数来注册一个信号处理函数,但是signal函数在某些情况下可能会出现问题,建议使用sigaction函数来注册信号处理函数。 

3.3.3  sigwait 函数

 sigwait 函数可以用于阻塞等待一个或多个信号,并在信号到达时唤醒。sigwait 函数的原型如下:

 #include <signal.h>

 int sigwait(const sigset_t *set, int *sig);
 其中,set 表示要等待的信号集
 sig 是一个指针,用于返回收到的信号编号。

例如,要阻塞等待 SIGINT 信号的到来,可以执行以下代码: 

#include <cstdio>
#include <csignal>
 
int main() {
  sigset_t set;
  int sig;
 
  sigemptyset(&set);
  sigaddset(&set, SIGINT);
 
  if (sigwait(&set, &sig) == -1) {
    perror("sigwait");
    return 1;
  }

  printf("Received signal: %d\n", sig);
 
  return 0;
}

 3.3.4  pause 函数

  pause 函数可以用于阻塞进程,直到接收到一个信号为止。pause 函数的原型如下:

  #include <unistd.h>

  int pause(void);
   

  例如,要阻塞进程直到接收到 SIGINT 信号为止,可以执行以下代码

#include <stdio.h>
#include <signal.h>
 
void sigint_handler(int sig) {
  printf("Received SIGINT signal\n");
}
 
int main() {
  if (signal(SIGINT, sigint_handler) == SIG_ERR) {
    perror("signal");
    return 1;
  }
 
  printf("Waiting for SIGINT signal...\n");
 
  pause();

  return 0;
}

以上是几种常见的接收信号的方法,根据不同的需求可以选择合适的方法来注册信号处理函数和接收信号。

3.4 信号的处理

  假设一个进程接收完信号,那么这个信号肯定是需要处理的。在 Linux 中,信号的处理方式一共有三种:

  1.    忽略信号
  2.   执行默认处理函数
  3.   执行自定义处理函数。

这三种处理方式可以通过 signal 函数和 sigaction 函数来设置。

3.4.1  忽略信号

  如果将一个信号的处理方式设置为忽略,则当进程接收到该信号时,内核将不做任何处理,直接丢弃该信号。通常情况下,只有少数信号可以被忽略,如 SIGCHLD 信号。忽略信号的方式可以通过 signal 函数来设置。

  SIG_DFL、SIG_IGN 分别表示无返回值的函数指针,指针值分别是 0 和 1 。这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值。
  SIG_DFL:默认信号处理程序
  SIG_IGN:忽略信号的处理程序

将signal函数和SIG_IGN函数结合,我们就可以忽略某个信号,如:

#include <stdio.h>
#include <signal.h>
 
int main() {
  //也是一个宏  #define SIG_ERR / *实现定义* / 当通过信号返回时,表示发生了错误。
  if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
    perror("signal");
    return 1;
  }
 
  printf("Waiting for SIGINT signal...\n");
 

 
  return 0;
}

在上面的代码中,将 SIGINT 信号的处理方式设置为忽略,当进程接收到 SIGINT 信号时,该信号将被直接丢弃。

3.4.2  执行默认处理函数

  每个信号都有一个默认的处理方式,当进程接收到该信号时,内核会执行默认的处理函数。默认的处理函数可以是终止进程、核心转储、停止进程、忽略信号等。

  通过SIG_DFL也可以自定义设置。

  不再做过多示例。

3.4.3  执行自定义处理函数

  如果想要自定义一个信号的处理方式,可以编写一个信号处理函数,并使用 signal 函数或 sigaction 函数将其注册为该信号的处理函数。当进程接收到该信号时,内核将执行该信号处理函数。

  参考signal和sigaction函数详解。

四 coro dump 文件

 在linux系统中,当一个程序在运行时发生严重错误,如访问非法内存地址或除以零等,操作系统会向该程序发送一个信号,通知它出现了一个错误。如果程序没有处理该信号,操作系统会将该程序终止,并将程序的内存映像保存到一个称为 core dump 文件的文件中

  Core dump 文件通常包含程序在崩溃时的内存快照和一些其他有用的调试信息,例如程序的寄存器状态、堆栈跟踪信息和调用栈信息等。这些信息可以帮助程序员在调试时定位错误。

  Core dump 文件通常非常大,可以是程序内存使用量的几倍甚至几十倍。因此,在生产环境中,可以通过禁用 core dump 生成来减少磁盘空间的使用。在开发和测试环境中,生成 core dump 文件可以提供有用的调试信息,帮助程序员解决问题。

  在Linux系统中,默认情况下,默认是关闭的,core dump 文件被保存在当前工作目录下的名为 core 或者 core.<pid> 的文件中。其中 <pid> 是崩溃程序的进程ID。可以通过 ulimit -c unlimited 命令来打开 core dump 生成。此外,还可以使用 gdb 或其他调试工具来分析 core dump 文件中的信息。

  ps:一般用不上,不用动

五 信号的捕捉

   如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。

   由于信号处理函数的代码 是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行 main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号 SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler 和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。

  sighandler函数返 回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复 main函数的上下文继续执行了。

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

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

相关文章

【MySQL】lower_case_table_names作用及使用

知识点&#xff1a; lower_case_table_names 是mysql设置大小写是否敏感的一个参数。 场景&#xff1a;在使用dataease时&#xff0c;连接外部数据库&#xff0c;启动报错&#xff01;后查看官方文档&#xff0c;特别要求改数据库配置文件&#xff1a;lower_case_table_names …

linux系统---selinux

目录 前言 一、SELinux 的作用及权限管理机制 1.SELinux 的作用 1.1DAC 1.2MAC 1.3DAC 和 MAC 的对比 2.SELinux 基本概念 2.1主体&#xff08;Subject&#xff09; 2.2对象&#xff08;Object&#xff09; 2.3政策和规则&#xff08;Policy & Rule&#xff09; …

MybatisPlus入门详解

一、MyBatisPlus 简介 1.1 创建新模块 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency> 由于mp并未被收录到idea的系统内置配置,无法…

停工待料,责任真的全在PMC吗?天行健深度剖析背后的原因

在现代制造业中&#xff0c;停工待料的现象时有发生&#xff0c;这不仅影响了生产进度&#xff0c;还增加了企业的运营成本。很多人会自然而然地将责任归咎于生产物料控制&#xff08;PMC&#xff09;部门&#xff0c;认为是他们没有做好物料计划和管理。但事实上&#xff0c;停…

CentOS7 Sqoop 1.4.7 安装 (Hadoop 3.3.0)

CentOS7 Sqoop 1.4.7 安装 (Hadoop 3.3.0) 1、 Sqoop 1.4.7 官网链接下载&#xff1a; https://archive.apache.org/dist/sqoop/1.4.7/ 2、把压缩包用mobaxterm拖到 /tools文件夹 3、解压 tar -zvxf /tools/sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /training/4、进入 /t…

基于redis实现用户登陆

因为session有数据共享问题&#xff0c;不同tomcat服务器中的session不能共享&#xff0c;之后负载均衡就无法实现。所以我们用redis代替session。redis可以被多个tomcat服务器共享&#xff0c;redis基于内存。 之前的session可以看做登陆凭证&#xff0c;本次登陆凭证由sessi…

应用案例 | Softing echocollect e网关助力汽车零部件制造商构建企业数据库,提升生产效率和质量

为了提高生产质量和效率&#xff0c;某知名汽车零部件制造商采用了Softing echocollect e多协议数据采集网关——从机器和设备中获取相关数据&#xff0c;并直接将数据存储在中央SQL数据库系统中用于分析处理&#xff0c;从而实现了持续监控和生产过程的改进。 一 背景 该企业…

FreeRTOS操作系统学习——空闲任务及其钩子函数

空闲任务 当 FreeRTOS 的调度器启动以后就会自动的创建一个空闲任务&#xff0c;这样就可以确保至少有一任务可以运行。但是这个空闲任务使用最低优先级&#xff0c;如果应用中有其他高优先级任务处于就绪态的话这个空闲任务就不会跟高优先级的任务抢占 CPU 资源。空闲任务还有…

鸿蒙NEXT实战开发:【截屏】

展示全屏截图和屏幕局部截图。通过[screenshot]模块实现屏幕截图 &#xff0c;通过[window]模块实现隐私窗口切换&#xff0c;通过[display]模块查询当前隐私窗口。 效果预览 全屏截图局部截图选择区域局部截图 使用说明&#xff1a; 点击右上角图标打开弹窗&#xff0c;选…

常见的验证码

一、短信验证码 前端&#xff1a; 用户填写手机号&#xff0c;点击按钮发送请求用户短信得到验证码后&#xff0c;用户填写表单提交 form 表单&#xff0c;进行验证 后台&#xff1a; 随机生成几位验证码并将生成时间、手机号、验证码存储起来&#xff0c;可以存到session、…

windows安装程序无法将windows配置为此计算机

目录 问题描述 问题原因 解决办法 方法一 方法二 方法三&#xff1a; 问题描述 重装系统时显示windows安装程序无法将windows配置在此计算机硬件上. 问题原因 安装介质已损坏 如果可引导的安装介质&#xff08;如DVD或USB驱动器&#xff09;损坏或损坏&#xff0c;安装过…

C++并发编程 -5. 基于锁实现线程安全的队列和栈容器

如何通过互斥锁和条件变量构建线程安全的队列&#xff08;栈&#xff09; 在C并发编程 -2.线程间共享数据 已经介绍过构建线程安全的栈&#xff0c;现在重新回顾。 一.构建线程安全的栈 1.异常发生在接口处 有时候我们可以将对共享数据的访问和修改聚合到一个函数&#xff0c…

浏览器打印信息和自己写的console的位置不一样,一直显示 transform.js

今天菜鸟正好无事&#xff0c;记起来自己最近用浏览器调试真的很难受&#xff0c;老是打印的信息显示在 transform.js 里面&#xff0c;然后一点开就跑到很复杂的打包后的代码里面去了&#xff0c;菜鸟是百思不得其解&#xff0c;代码不管怎么改都是这样&#xff01; 具体提示…

C++ · 代码笔记3 · 引用

目录 前言011引用初探_引用与普通变量012引用初探_引用作为函数参数013引用初探_引用作为函数返回值014引用初探_引用返回局部函数造成的错误015引用初探_多级引用020引用与指针递增的区别030const与引用040使用const限定的函数形参引用 前言 本笔记所涉及到的编程环境与 《C …

还为没有本科学历发愁?2024年成人学历提升最全攻略

如果你因为学历低而处处不顺的时候&#xff0c; 请记着&#xff0c; 我们还有提升学历的机会&#xff01; 成人想提升学历有三大方式&#xff1a;成人高考&#xff0c;自学考试&#xff0c;开放大学。 通过这几种途径获得的文凭都是国家承认&#xff0c;学信网可查的&#x…

不知开关电源是否短路?这三种测试方法教您判断

开关电源短路是常见的一种故障&#xff0c;容易烧毁元器件、损毁设备&#xff0c;因此在开关电源设计制造过程中常需要对其短路保护功能进行测试。那么要如何判断开关电源是否短路呢&#xff1f;影响开关电源短路的因素有哪些呢&#xff1f; 检测开关电源是否短路的方法 一、用…

信号处理--基于单通道脑电信号EEG的睡眠分期评估

背景 睡眠对人体健康很重要。监测人体的睡眠分期对于人体健康和医疗具有重要意义。 亮点 架构在第一层使用两个具有不同滤波器大小的 CNN 和双向 LSTM。 CNN 可以被训练来学习滤波器&#xff0c;以从原始单通道 EEG 中提取时不变特征&#xff0c;而双向 LSTM 可以被训练来将…

[BUUCTF]-Reverse:SimpleRev解析(字符串比较)

查壳 看ida 这里的中心就是两个字符串和一个计算式子&#xff0c;textkillshadow和str2adsfkndcls&#xff0c;计算式子str2[v2] (v1 - 39 - key[v3 % v5] 97) % 26 97 完整exp&#xff1a; #include<stdio.h> int main() {char key[11]"adsfkndcls";char…

crossover2023激活码分享crossover免费升级2024 crossover24更新内容

CrossOver是一款功能强大的系统兼容软件&#xff0c;它能够让Mac和Linux用户在不安装Windows操作系统的情况下直接运行Windows应用程序。以下是关于CrossOver的更多信息&#xff1a; 一、工作原理 CrossOver的工作原理是通过在Mac或Linux系统上模拟Windows应用程序所需的运行…

私域做不下去的三大因素

私域运营是近年来的一大热门话题&#xff0c;从线下门店到日常外卖、线上购物&#xff0c;几乎所有的企业都在借助微信等社交媒体平台进行推广。然而&#xff0c;据统计&#xff0c;近90%的私域运营最后都不了了之。 原因1&#xff1a;在于企业对私域的认知不足&#xff0c;营…