<Linux系统复习>信号

news2025/2/27 8:25:36

一、本章重点

1、什么是信号?

2、查看信号列表

3、信号捕捉

4、信号产生的5种方式

5、介绍CoreDump

6、信号处理的方式

7、如何理解信号产生到处理的过程

8、sigpending、sigprocmask、sigaction函数的使用

9、信号处理的时机

10、SIGCHLD信号

11、可重入函数

01 什么是信号?

生活中的信号:红绿灯、下课铃声、闹钟铃声等等。

当信号出现的时候,我们之前就知道信号该如何处理,并且不被要求立即处理该信号,因为有时我们正在处理重要的事情。(类比闹钟铃声)

技术应用角度的信号:当进程执行除0代码的时候是如何被操作系统终止的?本质是因为进程收到了信号,在合适的时机处理该信号的时候被操作系统终止。

02 查看信号列表

如何查看Linux中信号列表?

使用命令:kill -l

 需要知道的是没有32、33号信号,总共就62个信号。其中1-31是普通信号,34-64是实时信号。

03 信号捕捉

1、什么是信号捕捉?

简单来说就是改变信号的默认处理方式

介绍signal函数

 功能:对特定信号进行捕捉

使用方式:signal(要捕捉的信号编号,要执行的处理方式)

04 信号产生的5种方式

1、代码异常(如:除零错误、对野指针解引用)

2、命令行产生(如:kill -信号编号 pidkillall -信号编号 进程名

3、键盘组合键产生(如:ctrl + c、ctrl + z、ctrl + \ ,分别对应2,20,3号信号)

4、系统调用

5、软件条件

代码演示

1、异常

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

using namespace std;


int main()
{
    int cnt = 5;
    while(true)
    {
        cout<<"I am a process"<<endl;
        if(cnt == 0)
        {
            int num = 3/0;//除零错误
        }
        cnt--;
        sleep(1);
    }
    return 0;
}

 错误信息和信号列表对比大概可以看出除零错误属于8号信号

用signal函数对8号信号进行捕捉,执行我们的自定义的处理方式

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

using namespace std;

void handler(int signo)
{
    cout<<"收到了8号信号"<<endl;
    while(1)
    {
        cout<<"I am a process"<<endl;
        sleep(1);
    }
}

int main()
{
    signal(8,handler);
    int cnt = 5;
    while(true)
    {
        cout<<"I am a process"<<endl;
        if(cnt == 0)
        {
            int num = 3/0;
        }
        cnt--;
        sleep(1);
    }
    return 0;
}

 

2、命令行产生

①kill -信号编号 pid

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

using namespace std;


int main()
{
    int cnt = 5;
    while(true)
    {
        cout<<"I am a process"<<endl;
        sleep(1);
    }
    return 0;
}

②killall -信号编号 进程名

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

using namespace std;


int main()
{
    int cnt = 5;
    while(true)
    {
        cout<<"I am a process"<<endl;
        sleep(1);
    }
    return 0;
}

3、键盘组合键产生

①ctrl + c

②ctrl + z(强制当前进程转为后台,并使之停止)

jobs可以显示后台进程,fg + 后台编号可以将进程从后台放置到前台运行。

 

 ./test是前台运行该进程,./test &代表的是后台运行该进程

在后台运行进程时,解释器还能够解释命令,但由于都向同一个显示器打印,会造成互相干扰的情况,键盘组合键只能对前台进程有效,后台进程无效。

要想杀掉后台进程有两种方式:1、先fg 1 再 ctrl +c       2、kill -2 pid

③ctrl + \

如何知道ctrl + c、ctrl + z、ctrl + \分别给进程发的是什么信号?

答:对所有信号捕捉,然后再发送信号,信号捕捉函数就可以打印发送的信号。

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

using namespace std;

void handler(int signo)
{
    cout<<"进程收到了"<<signo<<"号信号"<<endl;
    sleep(1);
}
int main()
{
    for(int i = 1; i <= 31; i++)
    {
        signal(i,handler);
    }
    while(true)
    {
        cout<<"I am a process pid:"<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

 ctrl + c:向进程发送2号信号

ctrl + z:向进程发送20号信号

ctrl + \:向进程发送3号信号(注:有的朋友可能ctrl + \没有反映,具体原因可以百度解决

上述代码,还证明了并不上所有信号都能够被捕捉,比如9号信号就不能被捕捉,因为所有信号都能够被捕捉,那么这个进程真就“刀枪不入”了,连操作系统也拿它没办法了。

4、系统调用产生

①kill

功能:向任意进程发送任意信号

 写一个kill命令 

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

using namespace std;

int main(int argc,char** argv)
{
    if(argc!=3)
    {
        cout<<"Usage: kill -信号编号 pid"<<endl;
        return 1;
    }
    int signo = argv[1][1] - '0';
    //cout<<signo<<endl;
    int pid = atoi(argv[2]);
    //cout<<pid<<endl;
    kill(pid,signo);
    return 0;
}

②raise

功能:向当前进程发送任意信号

 

③abort

功能:向当前进程发送abort信号

 

 这个函数有点特殊,因为你就算捕捉了该信号还是会终止进程。

5、软件条件产生

1、管道的读端关闭,写端会收到SIGPIPE信号,处理该信号时被操作系统杀掉。

common.h

#include<iostream>
#include<stdio.h>
#include<cstdlib>
#include<sys/types.h>
#include<sys/stat.h>
#include<cstring>
#include<fcntl.h>
#include<unistd.h>
#include<signal.h>
#define PATH "./.fifo"
using namespace std;

 server.cc

#include "common.h"
 
int main()
{
    umask(0);
    if(mkfifo(PATH,0666)!=0)
    {
        cerr<<"mkfifo error"<<endl;
        return 1;
    }
    
    int fd = open(PATH,O_RDONLY);
    if(fd < 0)
    {
        cerr<<"open error"<<endl;
        return 2;
    }
 
 
#define SIZE 1024
    char buf[1024] = {0};
    while(true)
    {
        ssize_t s = read(fd,buf,sizeof(buf)-1);
        if(s==0)
        {
            cout<<"写端关闭"<<endl;
            unlink(".fifo");
            close(fd);
            exit(0);
        }
        else if(s>0)
        {
            buf[s] = '\0';
            cout<<"client->server: "<<buf<<endl;
        }
        else
        {
            cerr<<"read error"<<endl;
            close(fd);
            exit(3);
        }
    }
    return 0;
}

client.cc

#include "common.h"
 
 void handler(int signo)
 {
    cout<<"写端收到了"<<signo<<"号信号"<<endl;
    exit(1);
 }

int main()
{
    signal(SIGPIPE,handler);
    int fd = open(PATH,O_WRONLY | O_APPEND);
    if(fd<0)
    {
        cerr<<"open error"<<endl;
    }
 
    while(true)
    {
        char msg[1024] = {0};
        cout<<"请输入信息# ";
        fflush(stdout);
        if(fgets(msg,sizeof(char)*1024,stdin)!=nullptr)
        {
            //msg为 xxxx\n\0
            //下面的write我只传了xxxx
            write(fd,msg,strlen(msg)-1);
        }
        else
        {
            exit(0);
        }
    }
    return 0;
}

2、alarm函数

功能:自定义多少秒之后向进程发送一个alarm信号,该信号的默认处理方式是终止进程。

测试1秒钟服务器能够对cnt++多少次

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

using namespace std;

int cnt = 0;
void handler(int signo)
{
    cout<<cnt<<endl;
    exit(1);
}
int main()
{
    alarm(1);
    signal(SIGALRM,handler);
    while(true)
    {
        cnt++;
    }
    return 0;
}

05 介绍CoreDump

1、什么是CoreDump?(核心转储)

coredump是指当程序出错而异常中断时,OS会把程序工作的当前状态存储成一个coredump文件。然后我们可以通过该文件来定位异常的地方。

2、一般线上生产环境都不会自动打开CoreDump,需要我们手动打开。

ulimit -c 文件大小(一般1024的整数倍)

 

 3、试验一下CoreDump

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
using namespace std;

int main()
{
    cout<<"my pid is "<<getpid()<<endl;
    int* p = nullptr;
    *p = 3;
    return 0;
}

发生段错误,生成了core.25647文件,这个25647是什么意思呢?

本质其实就是异常进程的pid

生成了core.25647,我们可以通过gdb来分析CoreDump文件,定位异常。

 使用core-file core.26293可以导入coredump文件,然后gdb就可对该文件进行分析。

分析结果:进程终止是因为收到了11号信号,异常的地方是在test.cc文件的第10行。

06 信号处理的方式

1、默认处理方式

一般是终止该进程,可通过man 7 signal查看各个信号的默认处理方式

2、忽略

什么都不处理,只是将该信号从收到设置为未收到(ignore的缩写)

3、自定义

可通过signal函数或者sigaction函数自定义处理方式。

07 信号屏蔽字&&信号未决表&&信号处理函数

task_struct中存在三张表,分别是block表、pending表、handler表。

 block是一个32位的位图结构,从上到下0代表该信号没被阻塞,1代表该信号被阻塞。

pedding也是一个32位的位图结构,从上到下0代表该信号没被收到,1代表该信号被收到。

handler是一个函数指针数组,从上到下依次为每个信号对应的处理方式,SIG_DFL代表默认处理方式,SIG_IGN代表忽略。

pedding表说白了就是记录进程收到了哪些信号。

block表为了防止信号被处理而设计的,当某个信号被阻塞后,该信号就是递达了它都不会被处理,直到它被解除阻塞才能够被处理。

解释一下,给进程test使用组合键ctrl + c产生信号,操作系统做了什么?

首先,ctrl + c产生的是2号信号,因此操作系统会修改test进程控制块中的pending位图,将2号信号由0置为1,然后在合适时机处理该信号,当合适时机到来时,进程test执行2号信号的处理函数,由于该信号的处理函数是默认处理方式,那么操作系统会将test进程改为z状态并释放部分资源,最后被父进程回收test进程。

08 sigpending、sigprocmask、sigaction函数的使用

1、sigprocmask

功能:可以读取或更改进程的信号屏蔽字

成功返回0,出错返回-1

 其中how选项为SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK

set为输入型参数,设置新的阻塞信号集。

oldset为输出型参数,获取老的阻塞信号集。

在此之前需要介绍一下sigset_t 类型

sigset_t是一个32位的位图结构

对它进行操作的常用函数有:

①sigemptyset(sigset_t* set)

②sigfillset(sigset_t* set)

③sigaddset(sigset_t* set , signo)

④sigdelset(sigset_t* set , signo)

⑤sigismember(const sigset_t* set , signo)

现在我们使用sigprocmask函数对所有信号进行屏蔽

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

using namespace std;

int main()
{
    sigset_t new_block;
    sigset_t old_block;
    sigfillset(&new_block);
    sigemptyset(&old_block);

    sigprocmask(SIG_SETMASK,&new_block,&old_block);
    while(true)
    {
        cout<<"I am process"<<endl;
        sleep(1);
    }
    return 0;
}

 可以发现,绝大多数信号可以被屏蔽,但9号信号是无法被屏蔽的

使用一下sigprocmask中老的信号集

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

using namespace std;

int main()
{
    sigset_t new_block;
    sigset_t old_block;
    sigfillset(&new_block);
    sigemptyset(&old_block);

    sigprocmask(SIG_SETMASK,&new_block,&old_block);

    int cnt = 0;
    while(true)
    {
        cout<<"I am process"<<endl;
        sleep(1);
        if(cnt == 5)
        {
            cout<<"信号屏蔽字已经恢复到原来"<<endl;
            sigprocmask(SIG_SETMASK,&old_block,nullptr);
        }
        cnt++;
    }
    return 0;
}

 从上述现象可以看出,当有多个信号同时到来的时候,先处理信号编号小的。

2、sigpending

成功返回0,失败返回-1。

功能:读取当前进程的未决信号集

代码测试:

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;

void ShowPending(const sigset_t& pending)
{
    for(int i = 1; i<=31; i++)
    {
        if(sigismember(&pending,i))
        {
            cout<<"1";
        }
        else
        {
            cout<<"0";
        }
    }
    cout<<endl;
}

int main()
{
    sigset_t block;
    sigfillset(&block);
    sigprocmask(SIG_SETMASK,&block,nullptr);//阻塞所有信号

    sigset_t pending;
    sigemptyset(&pending);

    while(true)
    {
        sigpending(&pending);
        ShowPending(pending);
        sleep(1);   
    }
    return 0;
}

3、sigaction

成功返回0,失败返回-1

功能:可以读取和修改与指定信号相关联的处理动作

 sa_handler:设置普通信号的处理方式

 sa_sigaction:实时信号的处理方式,这里我们不管。

 sa_mask:在信号处理时,设置额外需要屏蔽的信号

 sa_flags:用来处理普通信号,设置为0就行。

 sa_restirer:不管。

代码测试

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

using namespace std;

void handler(int signo)
{
    cout<<"收到"<<signo<<"号信号"<<endl;
}

int main()
{
    //signal(2,handler);
    
    struct sigaction act;

    act.sa_handler = handler;
    act.sa_flags = 0;

    sigaction(2,&act,nullptr);//相当于signal(2,handler);

    while(true)
    {
        cout<<"I am a process"<<endl;
        sleep(1);
    }
    return 0;
}

09 信号处理的时机

信号处理的时机:从内核态切换回用户态时会做信号检测,检测到有信号就会处理。

 当检测到有信号要处理时,就要处理信号。

对默认处理方式是终止进程,修改pending位图,然后返回用户态。

对忽略处理方式是修改pending位图,返回返回用户态。

对自定义的处理方式则需返回到用户态模式下处理该自定义函数,然后在返回用户态,在进行信号检测,如果没有信号,则返回用户态继续进行向下执行。

1、为什么要返回用户态默认执行自定义函数?

因为自定义函数是用户写的,必须要防止用户冒用操作系统的身份执行非法行为。

2、处理完自定义函数,为什么不能直接返回用户态继续执行剩下的代码?

因为你需要返回到内核态完成返回工作,比如你需要恢复上下文或者系统调用的返回值,这些都需要更高的权限来做,必须返回到内核态再回到用户态。

10 SIGCHLD信号

子进程退出时会给父进程发送SIGCHLD信号,对该信号的默认处理方式是忽略。

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

using namespace std;


void handler(int signo)
{
    cout<<getpid()<<"收到了"<<signo<<"号信号"<<endl;
}

int main()
{
    signal(SIGCHLD,handler);
    if(fork() == 0)
    {
        int cnt = 0;
        while(true)
        {
            cout<<"I am a child pid: "<<getpid()<<endl;
            if(cnt == 5)
            {
                exit(1);
            }
            cnt++;
            sleep(1);
        }
    }
    else
    {
        while(true)
        {
            cout<<"I am a father pid: "<<getpid()<<endl;
            sleep(1);
        }
    }
    return 0;
}

 但这种对SIGCHLD信号的处理方式在多个子进程同时退出则不能很好的处理。因为对于普通信号而言只有一个比特位来记录信号是否被收到(实时信号会被链表链接起来)。

多个子进程同时退出的现象

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<cstdlib>
#include<wait.h>
using namespace std;


void handler(int signo)
{
    cout<<getpid()<<"收到了"<<signo<<"号信号"<<endl;
    waitpid(-1,nullptr,0);
}

int main()
{
    signal(SIGCHLD,handler);
    for(int i = 0;i <= 5; i++)
    {
        if(fork() == 0)
        {
            int cnt = 0;
            while(true)
            {
                cout<<"I am a child pid: "<<getpid()<<endl;
                if(cnt == 5)
                {
                    exit(1);
                }
                cnt++;
                sleep(1);
            }
        }
    }
    
    while(true)
    {
        cout<<"I am a father pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

这个代码的弊端:

①无法处理多个子进程同时退出的情况

②当子进程永远不退出时,则会一直阻塞住,因为waitpid使用的是0,代表阻塞等待。

改进后

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<cstdlib>
#include<wait.h>
using namespace std;


void handler(int signo)
{
    while(true)
    {
        int ret = waitpid(-1,nullptr,WNOHANG);
        if(ret > 0)
        {
            cout<<"等待成功->"<<ret<<endl;
        }
        else if(ret == 0)
        {
            cout<<"有的进程还未退出"<<endl;
            break;
        }
        else if(ret < 0)
        {
            cout<<"进程已全部退出"<<endl;
            break;
        }
    }
}

int main()
{
    signal(SIGCHLD,handler);
    for(int i = 0;i <= 5; i++)
    {
        if(fork() == 0)
        {
            int cnt = 0;
            while(true)
            {
                cout<<"I am a child pid: "<<getpid()<<endl;
                if(cnt == 5)
                {
                    exit(1);
                }
                cnt++;
                sleep(1);
            }
        }
    }
    
    while(true)
    {
        cout<<"I am a father pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

最常用的做法是忽略SIGCHLD信号,当它退出时自动被操作系统回收。

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<cstdlib>
#include<wait.h>
using namespace std;



int main()
{
    signal(SIGCHLD,SIG_IGN);
    for(int i = 0;i <= 5; i++)
    {
        if(fork() == 0)
        {
            int cnt = 0;
            while(true)
            {
                cout<<"I am a child pid: "<<getpid()<<endl;
                if(cnt == 5)
                {
                    exit(1);
                }
                cnt++;
                sleep(1);
            }
        }
    }
    
    while(true)
    {
        cout<<"I am a father pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

 有人或许有疑惑,SIGCHLD信号的默认处理方式不是忽略吗?你使用SIG_IGN处理方式不还是忽略吗?为啥手动写就能让子进程自动回收呢?

可以把这里当成一种特殊的情况,暂且只能记住了,或许之后会找到更好的答案。

11 可重入函数

要理解重入函数,先得理解重入的概念。

重入:一个执行流还未执行完毕,另一个执行就开始执行。

可重入函数:当执行流重入函数时,该函数不会出现不同的现象或者任何问题,则称这个函数是可重入的,否则是不可重入的。

大多数函数都是不可重入的,比如链表的头插函数。

 

所有的STL容器都是不可重入的,要想变得可重入得加锁保护,但加锁会损害STL容器的效率,这对以效率著称的STL是破坏性的,因此STL容器没有加锁。

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

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

相关文章

Codeforces Round 836 (Div. 2) A - C

A:SSeeeeiinngg DDoouubbllee 题意&#xff1a;给定一个字符串&#xff0c;每个字符串的字符可以出现两次&#xff0c;要求通过重新排列构造一个回文串。 思路&#xff1a;直接暴力可以&#xff0c;每个字符头部一个尾部一个。 #include<cstdio> #include <iostream…

不使用实体类的情况下接收SQL查询结果、@Autowired注入为null解决

目录 一、场景 二、环境 三、使用 1、数据库表以及数据准备 2、项目导入必要依赖 3、添加连接数据库配置文件 4、编写测试方法 5、执行结果 四、将SQL单独提取出来 2.1 定义查询接口方法 2.2 测试 2.3 测试结果 五、问题记录&#xff1a; Autowired注入失败/null的…

b、B、KB、Kib、MB、MiB、GB、GiB、TB、TiB的区别

1024这个数字&#xff0c;想必计算机行业从业人员应该不会陌生&#xff0c;甚至10月24日还被当做程序员日&#xff0c;如果你问一个程序员1GB等于多少MB,他大概率会不假思索回答:1024。 没错&#xff0c;对于稍微对计算机或者网络有了解的人&#xff0c;一般都认为1024是数据容…

最短路算法 - dijkstra

最短路算法 - dijkstra1. 算法介绍2. 实战2.1 Reachable Nodes In Subdivided Graph3 参考1. 算法介绍 算法目的&#xff1a;求图中某点 s 到其余各点的最短距离 算法步骤&#xff1a; 初始化距离数组 dis 和优先级队列&#xff0c;其中 dis[i] 表示 s 点到当前 i 点的最短距…

树莓派上搭建SVN服务器

目录 一、服务端安装步骤 1.安装svn 2.创建目录 3.创建版本仓库 4.修改配置&#xff08;authz,passwd,svnserve.conf&#xff09; 5.启动服务 二、tortoisSVN客户端安装 三、结束 一、服务端安装步骤 1.安装svn sudo apt-get install subversion 2.创建目录 sudo m…

品RocketMQ 源码,学习并发编程三大神器

这篇文章&#xff0c;笔者结合 RocketMQ 源码&#xff0c;分享并发编程三大神器的相关知识点。 1 CountDownLatch 实现网络同步请求 CountDownLatch 是一个同步工具类&#xff0c;用来协调多个线程之间的同步&#xff0c;它能够使一个线程在等待另外一些线程完成各自工作之后&…

selenium--获取页面信息和截图

获取页面信息namecurrent_urltitlecurrent_window_handlewindow_handlespage_source简单用法—— 判断页面截图1.get_screenshot_as_png2.get_screenshot_as_file获取页面信息 主要方法如下图&#xff1a; 介绍一下常用的方法&#xff1a; name 获取浏览器名字 current_u…

Packet Tracer 实验 - 排除多区域 OSPFv3 故障

地址分配表 设备 接口 IPv6 全局单播地址 IPv6 本地链路地址 默认网关 ISP GigabitEthernet0/0 2001:DB8:C1:1::1/64 FE80::C1 不适用 ASBR GigabitEthernet0/0 2001:DB8:C1:1::2/64 FE80::7 不适用 Serial0/0/0 2001:DB8:A8EA:F0A::1 FE80::7 不适用 S…

如何通过 kubectl 进入 node shell

概述 假设这样一个场景&#xff1a; 生产环境中&#xff0c;Node 都需要通过堡垒机登录&#xff0c;但是 kubectl 是可以直接在个人电脑上登录的。 这种场景下&#xff0c;我想要通过 kubectl 登录到 K8S 集群里的 Node&#xff0c;可以实现吗&#xff1f; 可以的&#xff…

LinkedList与链表

目录 1.链表 2.链表的模拟实现 3.LinkedList的模拟实现 4.LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 5.ArrayList和LinkedList的区别 我的GitHub&#xff1a;Powerveil GitHub 我的Gitee&#xff1a;Powercs12 (powercs12) - Gitee.com 皮卡丘每天学…

机器人十大前沿热点领域(2022-2023)

中国电子学会嵌入式系统与机器人分会主任委员、北京航空航天大学机器人研究所名誉所长、中关村智友研究院院长王田苗博士出席大会闭幕式&#xff0c;并现场发布《机器人十大前沿热点领域(2022-2023)》。归纳出2022-2023年机器人十大前沿技术&#xff1b;并结合我国国情和机器人…

自动化项目倍加福测距仪QSM WCS RS485 与西门子S7 200通信

1、程序流程图 2、WCS位置数据处理流程 第一步&#xff1a;设置S7-200的RS485的通讯波特率19.2kbps&#xff0c;通讯格式&#xff08;8&#xff0c;1&#xff0c;E&#xff09;&#xff1b; 第二步&#xff1a;PLC向WCS发送请求码&#xff1a; A0A1为0&#xff0c;表示读码器地…

现代密码学导论-16-选择明文攻击和CPA安全

目录 PCA不可区分实验 DEFINITION 3.21 PCA安全的加密方案 LR预言机实验 DEFINITION 3.22 多明文PCA安全的加密方案 THEOREM 3.23 定义3.21和定义3.22等价 PCA不可区分实验 通过运行G(1^n)获得密钥k敌手A被给定输入1^n并拥有访问预言机Enck()的权利&#xff0c;敌手A输出一…

想要精通算法和SQL的成长之路 - 无重叠区间

想要精通算法和SQL的成长之路 - 无重叠区间前言一. 无重叠区间前言 想要精通算法和SQL的成长之路 - 系列导航 一. 无重叠区间 原题链接 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互…

神经网络和深度学习-处理多维特征的输入

处理多维特征的输入 前面有两个数据集&#xff0c;一个回归&#xff0c;一个分类。 在回归中输出y属于实数&#xff0c;而在分类中输出y属于一个离散的集合 例如在糖尿病分类的数据集中Diabetes Dataset&#xff0c;每一行作为一个sample&#xff08;样本&#xff09;&#x…

Node.js 入门教程 11 Node.js 从命令行接收参数

Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Node.js 入门教程11 Node.js 从命令行接收参数11 Node.js 从命令行接收参数 当使用以下命令调用 Node.js 应用程序时&#xff0c;可以传入任…

Android -- 每日一问:两个 Activity 之间如何传递参数?

经典回答 使用 Intent 的 Bundle 协带参数&#xff0c;就是我们常用的 Intent.putExtra 方法。 除了传递基本类型外&#xff0c;如何传递自定义的对象呢&#xff1f; 这个问题就是想引出 Android 的 Parcelable 。一般很多面试者都有用过传递实现了Serializable接口的自定义对…

JavaWeb学习-监听器

什么是监听器? 类似于前端的事件绑定,java中的监听器用于监听web应用中某些对象、信息的创建、销毁、增加&#xff0c;修改&#xff0c;删除等动作的发生&#xff0c;然后作出相应的响应处理。当范围对象的状态发生变化的时候&#xff0c;服务器自动调用监听器对象中的方法。…

Flutter高仿微信-第54篇-群聊-邀请好友

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; import package:flutter/material.dart; import package…

【JavaEE】HTML

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言一、HTML概述二、【HTML常见标签】1. 注释标签2. 标题标签&#xff1a;h1-h63. 段落标签&#xff1a;p4.换行标签&#xff1a;br5.【格式化标签】5. 图片标签&#xff1a;img 【单标签&#xff0c;没有结束标签】…