【Linux】信号保存{sigset_t/sigpending/sigprocmask/bash脚本/代码演示}

news2025/2/27 9:07:47

文章目录

  • 1.信号相关常见概念
  • 2.管理信号的数据结构
  • 3.初识sigset_t
  • 4.信号集操作函数
    • 4.1sigpending
    • 4.2sigprocmask
    • 4.2代码测试
      • 1.测试1
      • 2.测试2
      • 3.测试3
    • 4.3bash 脚本文件


在这里插入图片描述

1.信号相关常见概念

信号相关动作:产生 发送 接收 阻塞 递达(处理)
实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态 称为信号未决(Pending)
进程可以选择阻塞 (Block )某个信号
信号产生后未被递达前 或 被阻塞的信号 将保持在未决状态 直到进程解除对此信号的阻塞 才执行递达的动作

阻塞信号和忽略信号的区别

阻塞和忽略是不同的 只要信号被阻塞就不会递达 而忽略是在递达之后可选的一种处理动作
忽略: 对信号已经进行了处理,处理方式是不做处理
阻塞:压根就没有对信号进行处理。

2.管理信号的数据结构

在这里插入图片描述
在这里插入图片描述

  1. 在进程PCB中,信号的pending位图、block位图和handler函数指针数组是用于管理信号处理的数据结构。
  2. 每个信号都有两个标志位分别表示阻塞(block)和未决(pending) 以及一个函数指针数组表示处理动作
  3. 信号产生未被递达时 内核在进程控制块中设置该信号的未决标志 直到信号递达才清除该标志
  4. a. 上图SIGHUP信号未阻塞也未产生过 当它递达时执行默认处理动作
    b. SIGINT信号产生了 但正在被阻塞 暂时不能递达 虽然它的处理动作是IGN(忽略) 但在没有解除阻塞之前 这个信号是不能进行处理即去忽略的 【在解除阻塞前进程仍有机会改变处理动作】
    c. SIGQUIT信号未产生过 一旦产生SIGQUIT信号将被阻塞 它的处理动作是用户自定义函sighandler

如果在进程解除对某信号的阻塞之前 这种信号产生过多次 将如何处理?

POSIX.1允许系统递送该信号一次或多次。
Linux是这样实现的:常规信号在递达之前产生多次只计一次 而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。

解读相关数据结构

在这里插入图片描述

在Linux下,PCB(进程控制块)的pending位图与信号的处理紧密相关。pending位图主要表示已经收到但是还没有被递达的信号。换句话说,当一个进程接收到一个信号,但这个信号尚未被进程处理(例如,因为进程当前正在执行某个不允许被中断的操作,或者信号的处理函数尚未被执行),那么这个信号的状态就会被记录在pending位图中。

pending位图的作用在于跟踪哪些信号已经到达进程但尚未被处理。这允许操作系统在适当的时机递达这些信号,比如当进程处于可以安全处理信号的状态时。通过这种方式,pending位图确保了信号的可靠传递和处理,防止了信号的丢失或重复处理。

在更深入地理解pending位图时,还需要考虑其他与信号相关的PCB属性,如block位图(表示哪些信号不应该被递达,直到解除阻塞)和信号屏蔽字(handle函数指针数组,用于定义每个信号的处理方式)。这些属性与pending位图一起,共同构成了Linux中进程信号处理的复杂而精细的机制。

总的来说,pending位图是Linux进程控制块中用于跟踪和管理已接收但尚未处理信号的重要数据结构,它确保了信号的可靠性和高效性。
Pending信号集:每个进程都有一个pending位图,用于记录当前已经被该进程接收但尚未处理(递达)的信号(未决信号)。当一个信号被接收时,对应的位会被设置为1,表示该信号已产生处于未决状态,直到信号递达(处理)才清除该标志。进程可以通过检查pending位图来确定是否有已产生未处理的信号。在Linux中,可以使用sigpending函数来获取当前进程的pending位图。

Block信号屏蔽字:每个进程都有一个block位图,用于指定当前被阻塞的信号。当一个信号被阻塞时,对应的位会被设置为1,表示该信号被阻塞,暂时不会被递达。进程可以通过设置block位图来控制哪些信号被阻塞,以避免进程在关键时刻被中断。在Linux中,可以使用sigprocmask函数来设置和获取当前进程的block位图。

Handler函数指针数组:每个进程都有一个handler函数指针数组,用于管理信号处理函数。该数组的索引对应于信号的编号,数组的元素是函数指针,指向相应信号的处理函数。当进程接收到一个信号时,会根据信号的编号在handler函数指针数组中查找对应的处理函数,并调用该函数来处理信号。在Linux中,可以使用signal和sigaction函数来设置和获取信号处理函数。

之前我们的文章中signal函数自定义处理信号的原理:将信号处理函数的指针填入到handler数组对应信号编号的位置。
pending位图、block位图和handler函数指针数组是针对每个进程而言的,每个进程都有自己独立的位图和数组。这样可以实现不同进程对信号的独立管理和处理。

3.初识sigset_t

在这里插入图片描述

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

什么是sigset_t?

sigset_t 是 Linux 系统中用于表示信号集的数据类型。信号集是一组信号的集合,这些信号可以是未决信号(即已经发送但尚未处理的信号)或阻塞信号(即当前被阻塞不处理的信号)。sigset_t 可以看作是一个由二进制位组成的大整数,每一位对应一个信号,某一位为 1 表示信号集中包含该信号,为 0 则表示不包含。

在编程中,可以使用多种函数来操作 sigset_t 类型的信号集。例如,sigemptyset() 函数用于创建一个空的信号集,sigaddset() 函数用于向信号集中添加信号,sigdelset() 函数用于从信号集中删除信号。

在多线程编程中,pthread_sigmask() 函数特别有用,它允许你在主线程中控制信号掩码。这个函数接受三个参数:一个操作方式(SIG_BLOCK、SIG_UNBLOCK 或 SIG_SETMASK),一个指向信号集的指针(表示要设置或修改的信号集),以及一个可选的指向旧信号集的指针(用于保存之前的信号集状态)。

需要注意的是,直接修改 sigset_t 类型的内部数据可能并不安全或有效,通常建议使用专门的函数来操作信号集。同时,处理信号和信号集时应当小心谨慎,以避免潜在的竞态条件或错误。

总的来说,sigset_t 是 Linux 系统中用于处理信号的重要数据类型,它提供了一种方便的方式来表示和操作信号集。通过相关的函数,你可以轻松地创建、修改和查询信号集,以满足不同的编程需求。

计算机常识

  1. 语言会提供.h,.hpp 和自定义类型
  2. OS也会提供.h,和 OS自定义的类型(为了让用户能够用接口,接口的参数又不能是语言里的类型,或者语言里没有这种类型)
  3. 语言的一些库函数如读写函数封装了系统接口,那么读写函数的头文件中有可能就包含了系统的头文件

理解sigset_t

sigset_t:user是可以直接使用该类型 与 用内置类型 && 自定义类型 没有任何差别

4.信号集操作函数

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的

sigset_t:不允许用户自己进行位操作,OS提供了对应的操作位图的方法

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

sigset_t:一定需要对应的系统接口来完成对应的功能,其中系统接口需要的参数,可能就包含了sigset_t定义的变量或者对象 接下来的4.1/4.2即为系统接口

4.1sigpending

OS通过这个接口将set给用户
在这里插入图片描述
sigpending 是一个在 Unix 和 Linux 系统中用于获取当前进程挂起信号集的函数。挂起信号集是指那些已经发送到进程,但尚未被处理的信号。

函数的原型如下:

c
int sigpending(sigset_t *set);
参数说明:

set:指向一个 sigset_t 类型的变量,函数会将当前进程的挂起信号集存储在这个变量中。
返回值:

如果成功,则返回 0。
如果失败,则返回 -1,并设置 errno 以指示错误。
sigpending 函数允许进程查询哪些信号当前处于挂起状态,即哪些信号已经发送给进程但尚未被处理。这对于进程来说是非常有用的信息,因为它可以根据这些信息来决定如何响应这些挂起信号,或者进行其他相关操作。

注意,sigpending 函数返回的是挂起信号集,而不是当前进程的信号屏蔽字。挂起信号集是那些已经发送但尚未处理的信号,而信号屏蔽字则决定了哪些信号当前被进程阻塞。因此,即使某个信号在挂起信号集中,如果它也在信号屏蔽字中,那么进程也不会立即响应它。

使用 sigpending 时,通常需要与 sigprocmask 等函数结合使用,以便进程能够更全面地控制和处理信号。

4.2sigprocmask

在这里插入图片描述
sigprocmask 是一个在 Unix 和 Linux 系统中用于检查和/或更改当前进程的信号屏蔽字的函数。信号屏蔽字是一个位掩码,用于控制哪些信号当前应该被进程阻塞(即忽略)。

函数的原型如下:

c
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数说明:

how:指定如何修改当前的信号屏蔽字。它可以是以下三个值之一:

SIG_BLOCK:将 set 所指向的信号集中的信号添加到当前的信号屏蔽字中。mask |= set
SIG_UNBLOCK:从当前的信号屏蔽字中移除 set 所指向的信号集中的信号。mask = mask &~set
SIG_SETMASK:将当前的信号屏蔽字设置为 set 所指向的信号集。mask = set
set:指向一个 sigset_t 类型的变量,该变量包含了一组信号,这些信号将根据 how 参数的值被添加到、从或从当前信号屏蔽字中移除。

oldset:如果此参数不是 NULL,则函数会将调用前的信号屏蔽字存储在 oldset 所指向的 sigset_t 变量中。

返回值:

如果成功,则返回 0。
如果失败,则返回 -1,并设置 errno 以指示错误。
sigprocmask 函数允许进程精细地控制哪些信号应被阻塞。这对于实现多线程程序中的同步机制,或者在执行某些不能被信号中断的临界区代码时特别有用。通过阻塞某些信号,进程可以确保在关键代码段执行期间不会被这些信号中断。

注意,sigprocmask 修改的是调用进程的信号屏蔽字,因此它只对调用它的进程有效。此外,阻塞的信号仍然可以被进程接收,只是它们不会中断进程的执行,直到信号屏蔽字被修改以允许这些信号为止。

4.2代码测试

1.测试1

如果我们对所有的信号都进行了自定义捕捉 我们是不是就写了一个不会被异常或者用户杀掉的进程? 并不是,OS的设计者也考虑了
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
9号信号不可被捕获 ⇒ 管理员信号

2.测试2

如果我们将2号信号block,并且不断的获取并打印当前进程的pending信号集,如果我们突然发送一个2号信号,我们就应该肉眼看到pending信号集中,有一个比特位0->1

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cassert>

static void showPending(sigset_t &pending)
{
    for (int sig = 31; sig >=1; sig--)
    {
        if (sigismember(&pending, sig))
            std::cout << "1";
        else
            std::cout << "0";
    }
    std::cout << std::endl;
}

int main()
{
    // 1. 定义信号集对象
    sigset_t set, oldset, pending;
    // 2. 初始化
    sigemptyset(&set);
    sigemptyset(&oldset);
    sigemptyset(&pending);
    // 3. 添加要进行屏蔽的信号
    sigaddset(&set, 2 /*SIGINT*/);
    // 4. 将set添加到当前的信号屏蔽字中(内核的进程内部)
    //[默认情况进程不会对任何信号进行block]
    int n = sigprocmask(SIG_BLOCK, &set, &oldset);
    assert(n == 0);
    (void)n;

    std::cout << "block 2 signal success...., pid: " << getpid() << std::endl;
   
    // 5. 重复打印当前进程的pending信号集
    while (true)
    {
        // 5.1 获取当前进程的pending信号集
        sigpending(&pending);
        // 5.2 显示pending信号集中信号状态
        showPending(pending);
        sleep(1);
    }

    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 如果解除2号信号的阻塞 会发生什么?
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cassert>

static void handler(int signum)
{
    std::cout << "捕获信号:" << signum << std::endl;
    // exit(1); 不终止进程
}

static void showPending(sigset_t &pending)
{
    for (int sig = 1; sig <= 31; sig++)
    {
        if (sigismember(&pending, sig))
            std::cout << "1";
        else
            std::cout << "0";
    }
    std::cout << std::endl;
}

int main()
{
    // 0. 测试 捕捉2号信号 不让其执行默认终止动作
    signal(2, handler);
    // 1. 定义信号集对象
    sigset_t set, oldset;
    sigset_t pending;
    // 2. 初始化
    sigemptyset(&set);
    sigemptyset(&oldset);
    sigemptyset(&pending);
    // 3. 添加要进行屏蔽的信号
    sigaddset(&set, 2 /*SIGINT*/);
    // 4. 设置set到内核中对应的进程内部[默认情况进程不会对任何信号进行block]
    int n = sigprocmask(SIG_BLOCK, &set, &oldset);
    assert(n == 0);
    (void)n;

    std::cout << "block 2 号信号成功...., pid: " << getpid() << std::endl;
    // 5. 重复打印当前进程的pending信号集
    int count = 0;
    while (true)
    {
        // 5.1 获取当前进程的pending信号集
        sigpending(&pending);
        // 5.2 显示pending信号集中的没有被递达的信号
        showPending(pending);
        sleep(1);
        count++;
        if (count == 20)
        {
            // 默认情况下 解除2号信号的阻塞 确实会进行递达
            // 2号信号的默认处理动作是终止进程 需要对2号信号进行捕捉
            std::cout << "Unblock signal 2" << std::endl;
            int n = sigprocmask(SIG_SETMASK, &oldset, nullptr);
            assert(n == 0);
            (void)n;
        }
    }

    return 0;
}

在这里插入图片描述

在这里插入图片描述

上面0和1的顺序是可以通过输出格式控制的逆序遍历 ⇒ 正向输出

我们可以获取sigpending 貌似没有一一个接口用来设置pending位图(所有的信号发送方式,都是修改pending位图的过程)

3.测试3

如果我们对所有的信号都进行block 我们是不是就写了一个不会被异常或者用户杀掉的进程

监测指令

i=1; id=$(pidof mysignal);\
> while [ $i -le 31 ]; do echo "i: $i, id: $id";\
> let i++; sleep 1; done

i=1; id=$(pidof mysignal);\
> while [ $i -le 31 ]; do kill -$i $id; \
> echo "sending signal $i";\
> let i++; sleep 1; done

在这里插入图片描述在这里插入图片描述
9号信号也不可以被屏蔽/阻塞

SIGKILL(9)信号 和 SIGSTOP(19)信号 不能被捕捉,也不能被阻塞。20号信号默认动作是忽略(猜测)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <cassert>

static void showPending(sigset_t &pending)
{
     for (int sig = 31; sig >= 1; sig--)
    {
        if (sigismember(&pending, sig))
            std::cout << "1";
        else
            std::cout << "0";
    }
    std::cout << std::endl;
}

static void blockSig(int sig)
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, sig);
    int n = sigprocmask(SIG_BLOCK, &set, nullptr);
    assert(n == 0);
    (void)n;
}

int main()
{
    for(int sig = 1; sig <= 31; sig++)
    {
        blockSig(sig);
    }
    
    sigset_t pending;
    while(true)
    {
        sigpending(&pending);
        showPending(pending);
        sleep(1);
    }
    return 0;
}

在这里插入图片描述在这里插入图片描述

4.3bash 脚本文件

#!/bin/bash

i=1
id=$(pidof mysignal)
while [ $i -le 31 ]
do
    if [ $i -eq 9 ];then
        let i++
        continue
    fi
    if [ $i -eq 19 ];then
        let i++
        continue
    fi
    kill -$i $id
    echo "kill -$i $id"
    let i++
    sleep 1
done

#!/bin/bash
这一行称为 shebang 或 hashbang。它告诉操作系统该脚本应该使用哪个解释器来执行。在这个例子中,它指定了使用 /bin/bash,也就是 Bourne Again SHell 的路径,这是许多 Linux 和 Unix 系统中的默认 shell。当你尝试运行这个脚本时(例如,通过输入 ./scriptname.sh),操作系统会查看脚本的第一行,并使用 /bin/bash 来解释和执行脚本的内容。
2. if … then … fi

这是 Bash 中的条件语句结构。它用于根据某个条件是否满足来执行不同的代码块。

  • if [ 条件 ]:检查后面的条件是否满足。如果满足,则执行 then 后面的代码块。
  • then:表示如果前面的条件满足,则执行接下来的代码块。
  • fi:表示 if 语句的结束。
    在你的脚本中,有两个 if 语句,分别检查 $i 是否等于 9 或 19。如果等于这两个值,脚本会跳过发送信号的部分,并直接继续到下一个循环迭代。
  1. then

如上所述,then 关键字用于 if 语句中,表示如果前面的条件满足,则执行 then 后面的代码块。

为了更清楚地理解,让我们看一个简化的例子:

bash
#!/bin/bash  
  
number=5  
  
if [ $number -eq 5 ]; then  
    echo "The number is 5."  
fi

在这个例子中,脚本会检查变量 number 是否等于 5。如果是,它会输出 “The number is 5.”。fi 表示 if 语句的结束。

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

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

相关文章

vue2+vant2+Laravel7 实现多图上传到七牛云

后端接口 1、路由&#xff0c;在 routes/api.php 中 Route::resource(photos, PhotoController)->only(store);2、创建对应控制器 <?php namespace App\Http\Controllers; use Illuminate\Http\Request;class PhotoController extends Controller {/**** 上传图片* p…

面部表情参考图

创造表情形变 | Character Creator | Reallusion 皮笑肉不笑&#xff1f;读取情绪的AI说&#xff1a;我太难了_面部

HAProxy高性能负载均衡器

一、HAProxy基础知识 &#xff08;一&#xff09;HAProxy概述 HAProxy是一款基于事件驱动、单进程模型设计的四层与七层负载均衡器&#xff0c;它能够在TCP/UDP层面以及HTTP(S)等应用层协议上实现高效的流量分发。HAProxy不仅适用于Web服务器负载均衡&#xff0c;还能应用于数据…

RabbitMQ学习总结-基础篇

1..RabbitMQ 本身是一个消息中间件&#xff0c;在服务应用中&#xff0c;可解决高性能&#xff0c;高并发&#xff0c;高应用的问题&#xff0c;极大程度上解决了应用的性能问题。 2.MQ的使用分为生产者和消费者&#xff0c;生产者生产消息&#xff0c;消费者去消费消息。 3.…

cesium.js加载模型后,重新设置旋转角度属性值

// 加载模型var position Cesium.Cartesian3.fromDegrees(longitude, latitude, height);// 计算矩阵var rollAngleDegrees 15; // 设置翻滚角度var rollAngleRadians Cesium.Math.toRadians(rollAngleDegrees); // 将角度转换为弧度var orientation Cesium.Transforms.eas…

android 怎么自定义view

首先了解view的绘制流程&#xff1a; 所以onmeasure ---测量view onlayout---确定view大小----》所以继承ViewGroup必须要重写onlayout&#xff0c;确定子view 而onDraw----是继承view时候需要操作的。 所以&#xff1a;自定义ViewGroup一般是利用现有的组件根据特定的布局…

Python电梯楼层数字识别

程序示例精选 Python电梯楼层数字识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python电梯楼层数字识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

操作系统内功篇:硬件结构之如何写出让CPU执行更快的代码?

一 前言 因为CPU要操作的数据都在CPU Cache中的话&#xff0c;就不用再从内存中读取数据了&#xff0c;这样就提高了效率&#xff0c;访问的数据在CPU Cache中越多&#xff0c;有个专业名词称为缓存命中率高&#xff0c;所以说&#xff0c;缓存命中率越高&#xff0c;自然执行…

Jupyter Notebook出错提示An error occurred while retrieving package information解决办法

出错日志信息&#xff1a; To access the notebook, open this file in a browser:file:///C:/Users/colda/AppData/Roaming/jupyter/runtime/nbserver-14564-open.htmlOr copy and paste one of these URLs:http://localhost:8888/?token3c0113e5da07c0b8b8c9de74ffb453c5047…

在idea中配置tomcat服务器,然后部署一个项日

1.下载tomcat Tomcat下载 点击右边的tomcat8 找到zip点击下载 下载完&#xff0c;解压到你想放置的路径下 2.配置环境变量 打开设置找到高级系统设置点击环境变量 点击新建&#xff0c;变量名输入&#xff1a;CATALINA_HOME&#xff0c;变量值就是Tomcat的安装路径&#x…

机器人阻抗控制中的transparency(透明度)

在机器人控制领域&#xff0c;transparency&#xff08;透明性&#xff09;是一个描述机器人在物理交互过程中如何响应外部力或运动的术语。透明性在这里通常意味着机器人的运动和行为对于与其交互的用户或环境来说是直观且可预测的。换句话说&#xff0c;透明性意味着机器人的…

【二分查找详细解析】 【图解】 例题【洛谷P2249 【深基13.例1】查找】

文章目录 二分查找的基础解释例题【洛谷P2249 【深基13.例1】查找】code↓ 二分查找的基础解释 二分的时间复杂度为 O ( l o g n ) O(log n) O(logn)&#xff0c;进行二分查找的序列必须满足单调性 我们可以先定义两个值 l , r l,r l,r &#xff0c;来表示查找到的左端点 l…

接口与多态

通过接口实现多态 接口中声明若干个 bstract方法&#xff1b; 方法体的内容细节由实现接口的类去完成&#xff0c;不同的类有 不同的实现方式 → 则接口变量在回调接口方法时具有多 种形态。 用接口进行程序设计的核心思想 使用接口回调技术&#xff1a;接口变量存放实现该接口…

DP最长上升子序列模型

目录 怪盗基德的滑翔翼代码实现 登山代码实现 合唱队形代码实现 友好城市问题分析代码实现 最大上升子序列和代码实现 *拦截导弹问题分析代码实现扩展 *导弹防御系统问题分析代码实现 *最长公共上升子序列问题分析代码实现 LIS 问题一般有三种解法 朴素版动态规划贪心二分树状…

测试用例要如何写

1、测试点与测试用例 测试点不等于测试用例&#xff0c;这是我们首先需要认识到的。 问题1&#xff1a;这些测试点在内容上有重复&#xff0c;存在冗余。 问题2&#xff1a;一些测试点的测试输入不明确&#xff0c;不知道测试时要测试哪些。 问题3&#xff1a;总是在搭相似…

树与二叉树(数据结构)

本篇博客讲解 树与二叉树&#xff0c;后续会继续讲解堆 —————————————————————— 1.树概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看…

CMAKE_CUDA_ARCHITECTURES set to ‘native’多版本与版本号矛盾问题,报错

CMAKE_CUDA_ARCHITECTURES set to ‘native’多版本与版本号矛盾问题&#xff0c;报错 1. 报错提醒如下图2. 原因本地安装多个cuda版本导致native寻找到多个版本&#xff0c;导致报错3. 具体配置需要根据你的显卡型号来确认 1. 报错提醒如下图 2. 原因本地安装多个cuda版本导致…

Redis - String 字符串

前言 下表中包含本博客提到的所有命令 字符串类型是 Redis 最基础的数据类型&#xff0c;关于字符串需要特别注意&#xff1a; 1&#xff09;⾸先 Redis 中所有的键&#xff08;key&#xff09;的类型都是字符串类型&#xff0c;⽽且其他⼏种数据结构也都是在字符串类似基础上…

ffmpeg 滤镜实现不同采样率多音频混音

音频混音在音视频开发中是十分重要的一个环节,所谓音频混音就是将所有需要混音的数据相加得到混音数据,然后通过某个算法进行非法数据的处理;例如相加数值超过最大值,最小值等! 在实际的音频开发中,要实现混音的流程如下: 因此我们的编码实现就分为五部分:寻找…

中经评论:“人工智能+”不止是加法

以下文章来源&#xff1a;经济日报 今年&#xff0c;“人工智能”首次被写入《政府工作报告》&#xff0c;这个新提法为发展数字经济、推进数实融合指明了新路径。值得注意的是&#xff0c;“人工智能”不是简单相加&#xff0c;而是要通过新技术催生新质生产力&#xff0c;为经…