C++linux高并发服务器项目实践 day9

news2024/12/23 13:56:59

C++linux高并发服务器项目实践 day9

  • 信号集
    • 信号集相关函数
      • 以下信号集相关的函数都是对自定义的信号集进行操作
      • sighandler_t函数
      • sigaction函数
    • SIGCHLD信号
      • 共享内存
      • 共享内存使用步骤
      • 共享内存操作函数

信号集

  • 许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为sigset_t
  • 在PCB中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为“未决信号集”。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对PCB中的这两个信号集进行修改
  • 信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的这一段时间
  • 信号的“阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生
  • 信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作

在这里插入图片描述

信号集相关函数

以下信号集相关的函数都是对自定义的信号集进行操作

int sigemptyset(sigset_t *set);

  • 功能:清空信号集中的数据,将信号集中的所有标志位置为0
  • 参数:set,传出参数,需要操作的信号集
  • 返回值:成功返回0,失败返回-1

int sigfillset(sigset_t *set);

  • 功能:将信号集中的所有标志位置为1
  • 参数:set,传出参数,需要操作的信号集
  • 返回值:成功返回0,失败返回-1

int sigaddset(sigset_t *set , int signum);

  • 功能:设置信号集中的某一个信号对应的标志位为1,表示阻塞这个信号
  • 参数:
    • set:传出参数,需要操作的信号集
    • signum:需要设置阻塞的那个信号
  • 返回值:成功返回0,失败返回-1

int sigdelset(sigset_t *set , int signum);

  • 功能:设置信号集中的某一个信号对应的标志位为0,表示不阻塞这个信号
  • 参数:
    • set:传出参数,需要操作的信号集
    • signum:需要设置阻塞的那个信号
  • 返回值:成功返回0,失败返回-1

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

  • 功能:判断某个信号是否阻塞
  • 参数:
    • set:需要的信号集操作
    • signum:需要判断的那个信号
  • 返回值:
    1. signum被阻塞
    2. signum不是一个成员
      -1. 失败
#include <signal.h>
#include <stdio.h>

int main(){

    //创建一个信号集
    sigset_t set;

    //清空信号集的内容
    sigemptyset(&set);

    //判断SIGINT是否在信号集set里
    int ret = sigismember(&set,SIGINT);
    if(ret == 0){
        printf("SIGINT 不阻塞\n");
    }else if(ret == 1){
        printf("SIGINT 阻塞\n");
    }

    //添加几个信号到信号集中
    sigaddset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);

    //判断SIGINT是否在信号集set里
     ret = sigismember(&set,SIGINT);
    if(ret == 0){
        printf("SIGINT 不阻塞\n");
    }else if(ret == 1){
        printf("SIGINT 阻塞\n");
    }

    //判断SIGQUIT是否在信号集set里
     ret = sigismember(&set,SIGQUIT);
    if(ret == 0){
        printf("SIGQUIT 不阻塞\n");
    }else if(ret == 1){
        printf("SIGQUIT 阻塞\n");
    }


    //从信号集中删除一个信号
    sigdelset(&set , SIGQUIT);

    //判断SIGQUIT是否在信号集set里
     ret = sigismember(&set,SIGQUIT);
    if(ret == 0){
        printf("SIGQUIT 不阻塞\n");
    }else if(ret == 1){
        printf("SIGQUIT 阻塞\n");
    }


    return 0;
}

sighandler_t函数

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

  • 功能:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)
  • 参数:
    • how:如何对内核阻塞信号集进行处理
      SIG_BLOCK:将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变
      假设内核中默认的阻塞信号集是mask, mask | set
      SIG_UNBLOCK:根据用户设置的数据,对内核中的数据进行解除阻塞
      mask &= ~set
      SIG_SETMASK:覆盖内核中原来的值

    • set:已经初始化好的用户自定义的信号集

    • oldset:保存设置之前的内核中的阻塞信号集的状态,可以是NULL

  • 返回值:
    成功:0
    失败:-1
    设置错误号:EFAULT、EINVAL

int sigpending(sigset_t *set);

  • 功能:获取内核中的未决信号集
  • 参数:set,传出参数,保存的是内核中的未决信号集中的信号
//编写一个程序,把所有的常规信号(1-31)的未决状态打印到屏幕
//设置某些信号是阻塞的,通过键盘产生这些信号

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

int main(){

    //设置2、3号信号阻塞
    sigset_t set;
    sigemptyset(&set);
    //将2号和3号信号添加到信号集中
    sigaddset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);

    //修改内核中的阻塞信号集
    sigprocmask(SIG_BLOCK,&set,NULL);

    int num =0;

    while(1){
        num++;
        //获取当前的未决信号集的数据
        sigset_t pendingset;
        sigemptyset(&pendingset);
        sigpending(&pendingset);

        //遍历前32位
        for(int i = 1;i <= 32 ;i++){
            if(sigismember (&pendingset,i) == 1){
                printf("1");
            }else if(sigismember (&pendingset,i) == 0){
                printf("0");
            }else{
                perror("sigismember");
                exit(0);
            }
        }

        printf("\n");
        sleep(1);
        if(num == 10){
            //解除阻塞
            sigprocmask(SIG_UNBLOCK,&set,NULL);
        }
    }

    return 0;
}

sigaction函数

在这里插入图片描述
#include <signal.h>
int sigaction(int signum, const struct siaction *act,struct sigaction *oldact);

  • 功能:检查或者改变信号的处理。信号捕捉
  • 参数:
    • signum:需要捕捉的信号的编号或者宏值(信号的名称)
    • act:捕捉到醒后之后的处理动作
    • oldact:上一次对信号捕捉相关的设置,一般不使用,传递NULL
  • 返回值:
    成功返回0,失败返回-1

struct sigaction {
//函数指针,指向的函数就是信号捕捉到之后的处理函数
void (*sa_handler)(int);
//函数指针,不常用
void (*sa_sigaction)(int, siginfo_t *, void *);
//临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号
sigset_t sa_mask;
//使用哪一个信号处理对捕捉到的信号进行处理
//这个值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigaction
int sa_flags;
//被废弃掉了
void (*sa_restorer)(void);
};

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void myalarm(int num){
    printf("捕捉到了信号的编号是:%d\n",num);
    printf("XXXXXXXXXX\n");
}

//过3秒以后,每隔2秒定时一次
int main(){

    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = myalarm;
    sigemptyset(&act.sa_mask); //清空临时阻塞信号集

    //注册信号捕捉
    sigaction(SIGALRM,&act,NULL);

    struct itimerval new_value;

    //设置间隔的时间
    new_value.it_interval.tv_sec = 2;
    new_value.it_interval.tv_usec = 0;

    //设置延迟的时间,3秒之后开始第一次定时
    new_value.it_value.tv_sec = 3;
    new_value.it_value.tv_usec = 0;

    int retur = setitimer(ITIMER_REAL ,&new_value,NULL);//非阻塞
    printf("定时器开始了...\n");

    if(retur == -1){
        perror ("setitimer");
        exit(0);
    }

    while(1);

    return 0;
}

SIGCHLD信号

SIGCHLD信号产生的条件:

  • 子进程终止时
  • 子进程从接收到SIGSTOP信号停止时
  • 子进程处在停止态,接受到SIGCONT后唤醒时

以上三种条件都会给父进程发送SIGCHLD信号,父进程默认会忽略该信号

可以用这个信号来解决僵尸进程

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

void myFun(int num ){
    printf("捕捉到的信号:%d\n",num);
    //能捕捉到17号信号,可以通过kill -l来查看
    //回收子进程PCB的资源
    // while(1){
    //     wait(NULL);
    // }

    while (1)
    {
        int ret = waitpid(-1,NULL,WNOHANG);
        if(ret > 0){
            printf("child die,pid = %d\n",ret);
        }else if (ret == 0)
        {
            //说明还有子进程
            break;
        }else if (ret == -1)
        {
            /*没有子进程*/
            break;
        }
        
        
    }
    
}

int main(){
    //提前设置好阻塞信号集,阻塞SIGCHLD,因为有可能子进程很快结束,父进程还没有注册完信号捕捉
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGCHLD);
    sigprocmask(SIG_BLOCK,&set,NULL);

    //创建一些子进程
    pid_t pid;
    for(int i = 0;i < 20;i++){
        pid = fork();
        if(pid == 0){
            break;
        }
    }

    if(pid > 0){
        //父进程

        //捕捉子进程死亡时发送的SIGCHLD信号
        
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);
        sigaction(SIGCHLD,&act,NULL);

        //注册完信号捕捉后,解除阻塞
        sigprocmask(SIG_UNBLOCK,&set,NULL);


        while (1)
        {
            printf("parent process pid :%d\n",getpid());
            sleep(2);
        }
        
    }else if(pid == 0){
        //子进程
        printf("child process pid :%d\n",getpid());
    }
    


    return 0;
}

共享内存

共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种IPC无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用

与管道等要求发送进程将数据从用户控件的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种IPC技术的速度更快

共享内存使用步骤

  • 调用shmget()创建一个新共享内存段或取得一个既有共享内存段的标识符(即由其他进程创建的共享内存段)。这个调用将返回后续调用中欧冠需要用到的共享内存标识符
  • 使用shmat()来附上共享内存段,即使该段成为调用进程的虚拟内存的一部分
  • 此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要使用由shmat()调用返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段的起点的指针
  • 调用shmdt()来分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步
  • 调用shmctl()来删除共享内存段。只有当当前所有附加内存段的进程都与之分离之后内存段才会销毁。只有一个进程需要执行这一步

共享内存操作函数

int shmget(key_t key,size_t size,int shmflg);
void *shmat(int shmid,const void *shmaddr ,int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
key_t ftok(const char *pathname,int proj_id);

在这里插入图片描述
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key,size_t size,int shmflg);

  • 功能:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识
    新创建的内存段中的数据都会被初始化为0
  • 参数:
    • key:key_t类型是一个整形,通过这个找到或者创建一个共享内存
      一般使用16进制表示,非0值
    • size:共享内存的大小
    • shmflg:属性
      • 访问权限
      • 附加属性:创建/判断共享内存是不是存在
        • 创建:IPC_CREAT
        • 判断共享内存是否存在:IPC_EXCL ,需要和IPC_CREAT一起使用
          IPC_CREAT | IPC_EXCL | 0664
  • 返回值:
    失败返回-1,设置错误号
    成功>0,返回共享内存的引用的ID,后面操作共享内存都是通过这个值

void *shmat(int shmid,const void *shmaddr ,int shmflg);

  • 功能:和当前的进程进行关联
  • 参数:
    • shmid:共享内存的标识(ID),由shmget返回值获取
    • shmaddr:申请的共享内存的起始地址,指定为NULL,由内核指定
    • shmflg:对共享内存的操作
      • 读:SHM_RDONLY,必须要有读权限
      • 读写:0
  • 返回值:
    成功:返回共享内存的首(起始)地址
    失败:返回(void*)-1,设置错误号

int shmdt(const void *shmaddr);

  • 功能:解除当前进程和共享内存的关联
  • 参数:
    shmaddr:共享内存的首地址
  • 返回值:成功 0,失败 -1

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

  • 功能:对共享内存进行操作。删除共享内存,共享内存要删除才会消失,创建共享内存的进行被销毁了对共享内存是没有任何影响
  • 参数:
    • shmid:共享内存的ID
    • cmd:要做的操作
      • IPC_STAT:获取共享内存的当前的状态
      • IPC_SET:设置共享内存的状态
      • IPC_RMID:标记共享内存被销毁
    • buf:结构体指针,需要设置或者获取的共享内存的属性信息
      • IPC_STAT:buf存储数据
      • IPC_SET:buf中需要初始化数据,设置到内核中
      • IPC_RMID:没有用,NULL

key_t ftok(const char *pathname,int proj_id);

  • 功能:根据指定的路径名,和int值,生成一个共享内存的key
  • 参数:
    • pathname:指定一个存在的路径
    • proj_id:int类型的值,但是这系统调用只会使用其中的1个字节
      范围:0-255 一般指定一个字符,如’a’

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main(){

    //1.创建一个共享内存
    int shmid = shmget(100,4096,IPC_CREAT|0664);
    printf("shmid = %d\n",shmid);

    //2.和当前进程进行关联
    void * ptr = shmat(shmid, NULL,0);

    char* str = "hello world";

    //3.写数据
    memcpy(ptr,str ,strlen(str)+1);

    printf("按任意键继续\n");
    getchar();

    //4.解除关联
    shmdt(ptr);

    //5.删除共享内存
    shmctl(shmid ,IPC_RMID,NULL);


    return 0;
}

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main(){

    //1.创建一个共享内存
    int shmid = shmget(100,0,IPC_CREAT);
    printf("shmid = %d\n",shmid);

    //2.和当前进程进行关联
    void * ptr = shmat(shmid, NULL,0);

    //3.读数据
    printf("%s\n",(char*)ptr);

    printf("按任意键继续\n");
    getchar();

    //4.解除关联
    shmdt(ptr);

    //5.删除共享内存
    shmctl(shmid ,IPC_RMID,NULL);


    return 0;
}

问题1:操作系统如何知道一块共享内存被多少个进程关联

  • 共享内存维护了一个结构体struct shmid_ds 这个结构体中有一个成员 shm_nattach
  • shm_nattach 记录了关联的进程个数

问题2:可不可以对共享内存进行多次删除 shmctl

  • 可以的
  • 因为shmctl标记删除共享内存,不是直接删除
  • 什么时候真正删除呢
    当和共享内存关联的进程数为0的时候,就真正被删除
  • 当共享内存的key为0的时候,表示共享内存被标记删除了
    如果一个进程和共享内存取消关联,那么这个进程就不能继续操作这个共享内存了

共享内存和内存映射的区别

  1. 共享内存可以直接创建,内存映射需要磁盘文件(匿名映射除外)
  2. 共享内存效果更高
  3. 内存
    所有的进程操作的是同一块共享内存
    内存映射,每个进程在自己的虚拟地址空间中有一个独立的内存
  4. 数据安全
    • 进程突然退出
      共享内存还存在
      内存映射区小时
    • 运行进程的电脑宕机了
      数据存储在共享内存中,没有了
      内存映射区的数据,由于磁盘文件中的数据还在,所以内存映射区的数还在
      5.生命周期
    • 内存映射区:进程退出,内存映射区销毁
    • 共享内存:进程退出,共享内存还在,标记删除(所有的关联的进程数为0),或者关机
      如果一个进程退出,会自动和共享内存取消关联

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

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

相关文章

深度思考:在 AI 时代,你会被放大一千倍的能力是什么?

Datawhale干货 作者&#xff1a;艾芙&#xff0c;复旦大学&#xff0c;百姓AI教育负责人 前言 大家晚上好&#xff0c;我是艾芙&#xff0c;百姓 AI 的 AI 教育负责人。 先做一下自我介绍&#xff0c;我是一个在技术圈和教育圈反复横跳的斜杠中年了。大约在 5 年前&#xff0c…

C++格式输入输出

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;数据结构 &#x1f525;座右铭&#xff1a;“不要等到什么都没…

常用的数据中心部署架构

说起数据中心&#xff0c;相信大家并不陌生。随着互联网行业的蓬勃发展&#xff0c;大数据时代的快速到来&#xff0c;数据中心同我们的生活紧密联系&#xff0c;息息相关。我们日常生活中的各种数据几乎都存储在数据中心里&#xff0c;例如&#xff1a;手机照片的云端备份、放…

Python小姿势 - # Python相关技术知识点

Python相关技术知识点 标题 在Python中如何处理文件 如果你要处理文件&#xff0c;那么在Python中你需要使用到os模块中的一些方法。 首先&#xff0c;你需要使用os.path.exists方法来判断文件是否存在&#xff1a; python if os.path.exists(file.txt): print(文件存在) else:…

asp.net企业员工考勤管理系统

企业员工管理系统主要是为企业内部管理员工使用的&#xff0c;主要功能分为员工和管理员两部分&#xff0c;主要的功能有用户登录&#xff0c;管理员信息管理&#xff0c;公告信息管理&#xff0c;文件审批管理&#xff0c;员工信息管理&#xff0c;工资信息管理&#xff0c;奖…

全景丨0基础学习VR全景制作,平台篇第19章:热点功能-文本

大家好&#xff0c;欢迎观看蛙色VR官方——后台使用系列课程&#xff01; 功能说明 应用场景 热点&#xff0c;指在全景作品中添加各种类型图标的按钮&#xff0c;引导用户通过按钮产生更多的交互&#xff0c;增加用户的多元化体验。 文本热点&#xff0c;即点击热点后会弹出…

一顿饭的事儿,搞懂了Linux5种IO模型

大家好&#xff0c;我是老三&#xff0c;人生有三大难题&#xff0c;事业、爱情&#xff0c;和 ——这顿吃什么&#xff01; 人在家中躺&#xff0c;肚子饿得响&#xff0c;又到了不得不吃的时候&#xff0c;这顿饭该怎么吃&#xff1f;吃什么呢&#xff1f; Linux里有五种I…

【笔记】【HTTP】《图解HTTP》第1章 了解Web及网络基础

前言 有输入就要有产出&#xff0c;该笔记是本人看完《图解HTTP》后对每章涉及到的知识进行汇总博客将会已书的每章为一篇发布&#xff0c;下一篇博客发布时间不确定笔记中有些个人理解后整理的笔记&#xff0c;可能有所偏差&#xff0c;也恳请读者帮忙指出&#xff0c;谢谢。…

在Notion AI 中轻松打造您的AI私人助理,提供卓越的工作体验

大家好&#xff0c;我是瓜叔。 相信平时喜欢做笔记的人对notion 选应该不陌生近年来越来越多人开始把notion 选当做他们的主力笔记软件。 我自己也用了约4年的时间。如果你也是notion的爱好者但还不知道notion AI是什么。那这篇文章&#xff0c;我会分享我是如何实际操作使用技…

Windows terminal+wsl+ohmyzsh+powerlevel10k打造更美丽的终端

安装wsl 安装 WSL 和 Linux 的默认 Ubuntu 发行版。 了解详细信息https://learn.microsoft.com/zh-cn/windows/wsl/install。 还可以使用此命令通过运行 wsl --install 来安装其他 Linux 发行版。 若要获取发行版名称的有效列表&#xff0c;请运行 wsl --list --online。 wsl -…

AIOps探索 | 新形势下,中小银行如何学好数字化转型“必修课”?

一、强化数智驱动 推进转型升级 2023年4月21日&#xff0c;在江苏省支付清算服务协会、山东省支付清算协会的大力支持下&#xff0c;由金科创新社&#xff08;鑫知&#xff09;主办的“2023农村中小银行数字化转型研讨会”在江苏南京成功举办。 ​本次大会以“强化数智驱动 推…

深入浅出堆—C语言版【数据结构】

二叉树概念博客&#xff1a;http://t.csdn.cn/XIW84 目录 1. 了解堆 1.1 堆的概念 1.2 堆的性质&#xff1a; 1.3 堆的结构图片 1.3.1 小堆 1.3.2 大堆 2. 堆的实现 2.1 插入数据进堆 2.2 向上调整函数 2.3 堆的删除 2.4 向下调整 3. 堆的应用 3.1 建堆&#xff…

赚钱单页产品

今天写一篇文章&#xff0c;讲一个最近看的赚钱单页产品。 先下定义&#xff0c;什么是赚钱的单页产品&#xff1a; 能赚钱&#xff1a;需求切的准单页产品&#xff1a;通常只有少数几个页面就完成了产品的核心功能&#xff0c;一个程序员可以在1天左右&#xff0c;完成开发 先…

快速理解基本的cookie、session 和 redis

一、Cookie 1、什么是Cookie 1、Cookie实际上是一小段的文本信息&#xff0c;是一种keyvalue形式的字符串。客户端请求服务器&#xff0c;如果服务器需要记录该用户状态&#xff0c;就使用response向客户端浏览器颁发一个Cookie。客户端会把Cookie保存起来。 2、当浏览器再请求…

跨境电商系统开发需要注意的问题

跨境商城系统开发需要考虑许多特殊问题&#xff0c;比如涉及跨国支付、物流、法律和政策、文化差异等等。下面将列出一些重要的问题&#xff0c;来帮助您更有效地开发跨境电商系统。 1、税务问题 当涉及跨国交易时&#xff0c;涉及不同国家和地区的税收政策和税率。特别是在国…

2023亚马逊云科技中国峰会开启报名 6月27-28日上海见

2023年6月27-28日&#xff0c;2023亚马逊云科技中国峰会&#xff0c;亚马逊云科技将聚焦构建新技术加速的产品创新、新架构提升的业务弹性、云计算引领的创新模式&#xff0c;围绕价值加速实现&#xff0c;企业敏捷稳健&#xff0c;行业全新机遇等热门话题&#xff0c;携手众多…

( 位运算 ) 136. 只出现一次的数字 ——【Leetcode每日一题】

❓136. 只出现一次的数字 难度&#xff1a;简单 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量…

学习dtw-python库内容 动态弯曲距离(DTW)具体实现

文章目录 一、install 数据包二、函数功能三、函数的参数以及含义四、具体实现 一、install 数据包 简单的pip install一下就好了&#xff0c;注意最后提示Successfully installed dtw-python-1.3.0 pip install dtw-python二、函数功能 执行 DTW 算法&#xff0c;并计算两个…

网页端操作提示「msg.js」库简介

这段时间我正在完成我的第一本个人图书&#xff0c;期间做了很多的案例&#xff0c;最近需要在网页端完成一个关于「恶意文本检测」的案例&#xff0c;为了让该案例表现的更加易用简洁、对用户友好&#xff0c;我需要在页面中添加一些用户操作提示信息&#xff0c;比如「正在加…

【HMS Core】Health Kit想要查看数据是来自用户的哪个设备,如何查看?

【问题描述1】 如何查看运动健康数据是来自用户的哪个设备&#xff1f; 【解决方案】 可以通过返回的数据中携带的dataCollectorId来查询提供数据的设备信息&#xff1a; 请求示例&#xff08;以查询睡眠记录详情为例&#xff09;&#xff1a; 1、查询睡眠记录并关联睡眠状…