【Linux】进程通信 — 信号(下篇)

news2025/1/15 16:40:32

文章目录

  • 📖 前言
  • 1. 阻塞信号
    • 1.1 信号其他相关常见概念:
    • 1.2 sigset_t:
      • 1.2 - 1 信号集操作函数
    • 1.3 sigprocmask:
    • 1.4 sigpending:
  • 2. 进程处理信号
    • 2.1 内核页表和用户页表:
    • 2.2 内核态和用户态:
    • 2.3 信号检测过程:
      • 2.3 - 1 便捷记忆图
    • 2.4 sigaction:
  • 3. volatile关键字
  • 4. 子进程给父进程发信号

📖 前言

上一篇我们讲述了信号的基本概念和相应系统接口的使用,本章我们想更深入的学习信号发送的一系列过程,目标已经确定,接下来就要搬好小板凳,准备开讲了…🙆🙆🙆🙆


1. 阻塞信号

1.1 信号其他相关常见概念:

  • 实际执行信号的处理动作称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block)某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在这里插入图片描述
pending表可以理解为是一张位图,32个比特位。

  • 比特位为0/1,表示哪一个信号是否收到。

handler表可以理解为是一个函数指针数组。

  • 对应的该信号也有对应的方法。

block叫做阻塞信号集。阻塞信号集(Block Signal Set)和信号屏蔽字(Signal Mask)是指相同的概念。

  • 拦不住发信号,但是可以拦得住递达这个信号。
  • 有些信号我们不想处理,但是防不住别人发,这个block也是一个位图,位图结构和pending位图是一模一样的。
  • 第几个比特位就代表着是几号信号,不一样的是,比特位的内容,在pending表里代表的是,是否收到内容,在block表中代表是否阻塞该信号。

block位图对应的比特位,为1的时候会拦截对应的信号去执行对应的方法。即使pending收到了该信号,只要是block位图对应的比特位为1,那么这个信号就无法去递达。

阻塞和忽略有什么区别呢?

忽略信号是处理信号的一种,只不过处理的方式是忽略它。(就是什么都不做,将pending位图由1置0就完了)

补充:

  • 在Linux中, 普通信号(非实时信号)多次发送并不会被记录多次。当同一个信号被多次发送给进程时,操作系统只会在进程的信号处理程序中记录次,而不会累积多个相同的信号。
  • 当进程接收到一个信号时,操作系统会将该信号标记为已挂起,直到进程处理完当前正在处理的信号或者通过信号处理程序返回后,才会再次传递给进程。
  • 这意味着,如果进程在处理信号期间接收到了多个相同的信号, 那么只有一 个信号会被记录和传递给进程的信号处理程序。
    例如:多次发送二号信号,只有一个会被递达,多余发出的信号被丢弃掉了。

1.2 sigset_t:

sigset_t是操作系统专门针对信号所构建的用户级的数据类型。

sigset_ t类型称之为信号集。

  • 可以表示每个信号的有或者无这样的概念。
  • 在阻塞信号集中表示有没有被阻塞这样的概念。
  • 在未决信号集中表示有或者没有被pending起来,或者未决起来。
  • sigset_t不能手动修改进制位图,要用对应的接口。

1.2 - 1 信号集操作函数

#include <signal.h>

int sigemptyset(sigset_t *set);对信号集做清空,可以理解为全清零。

int sigfillset(sigset_t *set);对信号集全置1。

int sigaddset (sigset_t *set, int signo);在特定的信号集当中,将特定的信号加进来。

int sigdelset(sigset_t *set, int signo);在特定的信号集当中,将特定的信号去掉。

int sigismember(const sigset_t *set, int signo);判断特定的一个信号,是否在该集合当中。

这一批接口,就是针对于位图结构天然设计好的各种各样的增删差改的操作。

1.3 sigprocmask:

sigprocmask: signal - 信号,process - 进程,mask - 掩码的意思。

  • 可以更改或者获取特定调用进程的信号屏蔽字。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
// 返回值:若成功则为0,若出错则为-1

可以理解成old或者是output,将老的信号屏蔽字返回出来,目的是为了将来的恢复需求。

这个函数第一个参数就决定了要做什么操作:

在这里插入图片描述

1.4 sigpending:

在这里插入图片描述

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

综上几个接口,我们用代码演示一下:

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

using namespace std;

void handler(int signo)
{
    // sleep(1);
    cout << "我是一个进程,刚刚获取了一个信号: " << signo << endl;
    // exit(1);
}

static void showPending(sigset_t* pendings)
{
    for (int sig = 1; sig <= 31; sig++)
    {
        // 检测这31个信号是否在这个集合里
        if (sigismember(pendings, sig))
        {
            cout << '1';
        }
        else
        {
            cout << '0';
        }
    }
    cout << endl;
}

int cnt = 0;

int main()
{
    // 3. pending收到信号很快就递达了,所以先block,这样就能看到pending表里的信号了
    // 屏蔽二号信号
    sigset_t bsig, obsig;
    sigemptyset(&bsig);
    sigemptyset(&obsig);
    
    // 3.1 添加2号信号到信号屏蔽字中
    sigaddset(&bsig, 2);

    // 3.2 设置用户级的信号屏蔽字到内核中,让当前进程屏蔽掉2号信号
    sigprocmask(SIG_SETMASK, &bsig, &obsig);

    // 2. signal将二号信号进行自定义捕捉
    signal(2, handler);

    // 1. 不断获取当前进程的pending信号集
    // 表示当前进程的所有pending信号
    sigset_t pendings;

    while (true)
    {
        // 1.1 清空信号集
        sigemptyset(&pendings);

        // 1.2 获取当前进程(谁调用,获取谁)的pending信号集
        if (sigpending(&pendings) == 0)
        {
            // 获取成功
            // 1.3 打印一下当前进程的pending信号集
            showPending(&pendings);
        }
        sleep(1);
        
        // 先跑十秒钟,再解除屏蔽
        cnt++;
        if (cnt == 10)
        {
            cout << "解除对所有信号的block...." << endl;
            sigset_t sigs;
            sigemptyset(&sigs);
            sigaddset(&sigs, 2);
            // 只对2号解除屏蔽
            // sigprocmask(SIG_UNBLOCK, &sigs, nullptr);

            // 解除全部信号屏蔽
            sigprocmask(SIG_SETMASK, &obsig, nullptr);
        }
    }

    return 0;
}

演示结果:

在这里插入图片描述


2. 进程处理信号

2.1 内核页表和用户页表:

进程收到了信号该如何处理呢?

进程处理信号,不是立即处理的:

  • 而是在合适的时候处理的。

信号可能不是立即处理的,可能当前进程做着更重要的事情。 是在合适的时候处理的。

具体在什么时候处理呢?

  • 当当前进程从内核态,切换回用户态的时候,进行信号的检测与处理!

每一个进程都有一个内核空间,用于内核级页表的映射:
在这里插入图片描述
内核页表:

  • 负责3G到4G之间数据的映射。
  • 所有进程共享的,只有一份内核级页表。
  • 前提是你得有权利访问!

用户级页表:

  • 每一个进程,都有一份,而且大家的用户级页表都是不一样的!

2.2 内核态和用户态:

内核态vs用户态:

  • 内核态可以访问所有的代码和数据 —— 具备更高权限
  • 用户态只能访问自己的

OS在不在内存中被加载呢??在!

  • 无论进程怎么切换,我们都可以找到内核的代码和数据,前提是你只要能够有权力访问!

当前进程如何具备权力,访问这个内核页表,乃至访问内核数据呢?

  • 要进行身份切换。
  • 进程如果是用户态的 —— 只能访问用户级页表。
  • 进程如果是内核态的 —— 访问内核级和用户级的页表。
  • 进程也有用户态和内核态的差别。

我怎么知道我是用户态的还是内核态的呢?

  • CPU内部有对应的状态寄存器CR3,有比特位标识当前进程的状态:0是内核态3是用户态

普通用户的身份是无法访问到操作系统中的任何数据的。

补充:

  • 当我们想调用某些系统调用的时候,这些系统调用的代码,实际上在执行时,除了要跳转到目标函数之外,还要陷入内核就是通过计算机帮我们直接去执行某些寄存器操作,将CR3寄存器权限标志位由3 (用户态)改为0(内核态),操作系统当在进行身份认证的时候,发现是0就有权访问,否则就不能访问。
  • 当把操作系统的代码执行完,准备返回的时候,返回时CPU内的级别再由0被改成了3再返回代码处继续执行。

达成的共识:

  • 地址空间分为用户空间和内核空间,每个用户都有自己的私有页表,但共享所有的是内核页表。CPU内有寄存器用来识别标识用户身份的。

最终的认识:

  • 无论进程再怎么切换,3~4G的内核空间是完全一样的,所以任何进程经过身份切换都可以,变成内核态去执行操作系统的代码。
  • 内核态可以访问地址空间内的所有代码和数据,任意进程。
  • 用户态只能访问自己的0~3G的数据,更高的访问不了。

补充:

我们的程序,会无数次直接或者间接的访问系统级软硬件资源(管理者是OS),本质上,你并没有自己去操作这些软硬件资源,而是必须通过OS -> 无数次的陷入内核(1.切换身份 2.切换页表) -> 调用内核的代码 -> 完成访问的动作 -> 结果返回给用户(1.切换身份 2.切换页表) -> 得到结果。

例如:

while(1); -> 必须有自己的时间片 -> 时间片到了的时候 -> 内核态,更换内核级页表 -> 保护上下文,执行调度算法 -> 选择了新的进程 -> 恢复新进程的上下文 -> 用户态,更换成用户级页表 -> CPU执行的就是新进程的代码!

什么是陷入内核:

  • 在Linux中,"陷入内核"是指用户程序或进程进入内核空间执行的一种状态。当用户程序需要执行特权操作或需要访问受限资源时,例如打开文件、创建进程等,就会触发一个系统调用来请求内核的帮助。
  • 当一个用户程序调用系统调用时,CPU会从用户态切换到内核态,进入内核空间执行相应的内核代码。在内核态下,用户程序可以访问受限资源并执行特权操作。这种切换是通过将用户程序的上下文保存起来,并加载内核的上下文来实现的。
  • 一旦用户程序陷入内核,它会执行内核提供的相关功能,并等待内核完成请求的操作后返回结果。完成后,CPU会从内核态切换回用户态,并将结果返回给用户程序继续执行。
  • 通过将用户程序和内核区分开来,Linux实现了安全性和稳定性的目标。用户程序无法直接访问和修改内核的数据结构,这样可以避免用户程序对系统造成破坏。同时,内核提供了一套系统调用接口,使得用户程序能够通过请求内核来获取系统资源和执行特权操作。

2.3 信号检测过程:

进程的生命周期中,会有很多次机会去陷入内核(中断,陷阱,系统调用,异常…),一定会存在很多次的机会进行内核态返回用户态
在这里插入图片描述

检查信号一些列的过程:
在这里插入图片描述

open调用一定会陷入内核~

调用接口执行open的代码。

  • 从磁盘当中读取文件的属性,在内核当中创建该文件的sturct file结构。
  • 该文件匹配的inode,以及该文件所对应的路径信息全部都设置好。
  • 将文件的地址填到文件描述符表的下标里,然后将下标返回进而就可以继续执行了。

实际上操作系统,在准备返回之前(open继续向后执行之前),其实不是简单的返回了,而是返回之前先查,查进程的信号列表。

  • handler中如果执行的是自定义方法时:

为什么自定义方法只能用用户身份来执行?

  • 是为了自定义方法是用户写的(用户提供的),是为了防止用户写了一段恶意代码,而内核身份权力又很大。

为什么不在自定义方法调用结束时直接返回?

  • 如果直接返回了,open的返回值没有返回,也返回不了,所以不能直接由处理信号的逻辑直接跳转过去,是不允许的,也做不到。
  • 系统调用要正常返回,是要把状态各方面要做切换,数据要返回,用户层到用户层无法做到这个工作,包括再用户层无法知道当时在哪被切到内核态的,不知道上下文数据,也没办法恢复,而且严重不推荐。

处理完走到内核当中,在内核里面特定的系统调用,特定的系统返回,把代码寄存器状态等方面恢复出来,让它继续跑到当前进程的代码里继续执行。

  • handler中如果执行的是非自定义方法时:

pending和block都为0:

  • 那么是没有信号要处理,直接返回到调用出正常运行了。

pending和block都是1:

  • 操作系统做不了任何事情,无法被处理,无法被递达,操作系统照样返回。

pending为1,block是0:(默认)

  • 去找handler表,如果是默认,一般指向操作系统中的默认处理方法,一般都是终止这个进程。
  • 把这个代码不要在CPU上跑了,然后把代码全都释放掉。
  • 设置好之后保留PCB,设置僵尸状态,将PCB内的信号编号填充成收到的信号编号。
  • 此时退出码已设置,进程状态设置成Z状态,此时这个进程就退出了,也不需要返回了。

pending为1,block为0:(忽略)

  • 去找handler表,发现handler表是SIG_IGN,就是忽略。
  • 直接将该信号由1置为0,然后就处理完了这个信号,然后直接返回就好了。
  • 返回对应处完成对应处理。

2.3 - 1 便捷记忆图

在这里插入图片描述
中间交点一定要在横线以下。

2.4 sigaction:

这个函数除了能处理普通信号,实时信号也能处理。

在这里插入图片描述
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。

我们不考虑实时信号,所以有些字段我们不考虑:

在这里插入图片描述
基本使用:

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

using namespace std;

void handler(int signo)
{
    cout << "获取到一个信号,信号编号是: " << signo << endl;
    sigset_t pending;

    // 永远都会正在处理2号信号
    while (true)
    {
        cout << "." << endl;
        sigpending(&pending);
        for (int i = 1; i <= 31; i++)
        {
            if (sigismember(&pending, i))
                cout << '1';
            else
                cout << '0';
        }

        cout << endl;
        sleep(1);
    }
}

int main()
{
    struct sigaction act, oact;
    act.sa_handler = handler;
    // act.sa_handler = SIG_IGN;
    // act.sa_handler = SIG_DFL;

    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    
    // 三号信号拦截
    sigaddset(&act.sa_mask, 3);

    // 对二号信号的捕捉
    sigaction(2, &act, &oact);

    // sigaction的更大意义在于,当我们在做信号处理时
    // 操作系统不允许嵌套式的递归式的处理多个信号。
 
    while (true)
    {
        cout << "main running" << endl;
        sleep(1);
    }
    
    return 0;
}

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

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

原因:

  • 通过将当前信号加入信号屏蔽字,内核确保在信号处理函数执行期间,同一信号不会再次中断进程。
  • 这种机制是必要的,因为信号处理函数是在异步上下文中执行的,即当信号发生时,处理函数会立即执行,而不管进程当前正在进行什么操作。
  • 如果不使用信号屏蔽字来屏蔽同一信号的再次中断,就可能导致信号处理函数被递归调用,而且多个信号处理函数同时执行,可能会引起不可预测的行为或系统崩溃。

一直处理某个信号,查看pending表:

在这里插入图片描述


3. volatile关键字

看下面一段代码:

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

// 保持内存的可见性,每次做检测必须从内存里拿
volatile int flags = 0;

void handler(int signo)
{
    printf("更改flags: 0->1\n");
    flags = 1;
}

int main()
{
    signal(2, handler);
    while (!flags);
    printf("进程是正常退出的!\n");

    return 0;
}

如果上述代码不带上volatile,则不同编译器会有不同的结果:

在这里插入图片描述
用新一点的编译器:

gcc test.c -o test -O2
  • 有的编译器发现在main执行流里发现没有对falgs做任何修改。
  • 高级别的编译器,会将这个flags值优化到寄存器里,从此往后再做while循环检测时候,只做一件事,从这个寄存器里做数据读取,所以这个寄存器里的值永远不会被修改了。
  • 编译器只能检测语法,不能检测逻辑。

volatile关键字,告诉编译器,不准对flags做任何优化,每次CPU计算的时候,拿内存中的数据,都必须在内存中拿!!


4. 子进程给父进程发信号

  • 子进程退出的时候,不是同学们想的那样,默默的退出(X状态)
  • 子进程退出的时候,自动给父进程发送SIGCHLD信号!!
#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout << "子进程退出啦,我确实收到了信号: " << signo << " 我是: " << getpid() << endl;
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t id = fork();
    if (id == 0)
    {
        while (true)
        {
            cout << "我是子进程: " << getpid() << endl;
            sleep(1);
        }
        exit(0);
    }

    // parent
    while (true)
    {
        cout << "我是父进程: " << getpid() << endl;
        sleep(1);
    }
}

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

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

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

相关文章

华为OD机试 - VLAN资源池 - 回溯、双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、核心思想2、具体解题思路 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&…

产品帮助中心对SaaS行业的作用

帮助中心是一款SaaS产品必不可少的一部分&#xff0c;为了帮助用户更好的解决产品相关问题&#xff0c;提高新用户的使用体验&#xff0c;并且引导其更好地使用产品。 所以今天我们就来谈谈帮助中心对SaaS行业的作用&#xff0c;以及制作帮助中心的方法&#xff0c;每个产品帮…

基于 OV5640 的图像采集显示系统(DVP 接口时序逻辑设计)

文章目录 前言一、DVP 接口时序逻辑设计二、基本数据流接收三、像素位置输出四、舍弃前 N 张图像五、系统异常状态恢复控制六、完整代码展示七、仿真代码展示八、仿真波形展示前言 上一节,我们已经完成了 OV5640 初始化逻辑的介绍。接下来,将要开始完成 DVP 接口的时序设计。…

Maven入门教程(一):安装Maven环境

Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的软件项目管理工具。 ​ 在项目开发中Maven可以对jar包和对工程之间的依赖关系进行管理。maven仓库中存储jar包&#xff0c;可以一次下载&#xff0c;所有项目通用。 1. 安装…

Java项目-苍穹外卖-Day07-redis缓存应用-SpringCache/购物车功能

文章目录 前言缓存菜品问题分析和实现思路缓存菜品数据清理缓存数据功能测试 SpringCache介绍入门案例 缓存套餐 前言 本章节主要是进行用户端的购物车功能开发 和redis作为mysql缓存的应用以及SpringCache的介绍 因为很多人查询数据库会导致mysql的查询效率降低&#xff0c;可…

解读亚马逊云科技语义搜图检索方案

图像检索&#xff08;包括文搜图和图搜图&#xff09;是各个行业中常见的一个应用场景。比如在电商场景中&#xff0c;基于以图搜图做相似商品查找&#xff1b;在云相册场景中&#xff0c;基于文搜图来找寻所需的图像素材。 传统基于标签的图像检索方式&#xff0c;即先使用目标…

压力传感器的性能技术指标

压力传感器采用第四代无线传输方式&#xff0c;加入用高性能的感压芯片&#xff0c;配合先进的电路处理和温度补偿技术&#xff0c;选用不锈钢外壳做隔离防腐&#xff0c;能够测量与接触部分材质相兼容的气体和液体等介质的表压和绝压。 应用场合&#xff1a;如供水、排水、消…

如何空手套白狼?一口气省7K再抓住一个7K起步的工作?

今日话题&#xff0c;教你如何省七千再得到一个七千起步的技能&#xff01;现在网络行业已经是全世界重点发展的目标&#xff0c;开发行业更是各个企业重点培养&#xff0c;但是在学校教的网络知识太基础太老掉牙&#xff1f;报班随便就是小一万该如何是好呢&#xff1f;解决方…

树莓派3b无屏幕登录

如果要无屏登录&#xff0c;烧写时最好设置&#xff0c;勾选WIFI &#xff0c;登录密码&#xff0c;和SSH 树莓派操作系统下载地址 树莓派资源下载 | 树莓派实验室 无屏幕无键盘登录&#xff1a;新版中可能要先SSH登录&#xff0c;然后才能在RASPI-CONFIG中打开串口控制台 登录…

1A快恢复整流二极管型号汇总

快恢复整流二极管是二极管中的一种&#xff0c;开关特性好、反向恢复时间短&#xff0c;在开关电源、PWM脉宽调制器、变频器等电子电路中经常能看到它的身影。快恢复整流二极管的内部结构与普通PN结二极管不同&#xff0c;它属于PIN结型二极管&#xff0c;即在P型硅材料与N型硅…

浏览器渲染机制

学习渡一课程、参考 必须明白的浏览器渲染机制 - 掘金 渲染机制的流程 HTML解析 布局 分层 绘制 分块 光栅化 画 HTML解析 - Parse HTML 解析html会生成一个 dom树和cssom树 document.styleSheets 可以看到cssom树 渲染阻塞 在渲染的过程中&#xff0c;遇到一个scr…

并发编程01(Lock Condition 生产者消费者)详细讲解

并发 并发编程∶并发、并行 并发&#xff08;多线程操作同一个资源) CPU一核&#xff0c;模拟出来多条线程&#xff0c;天下武功&#xff0c;唯快不破&#xff0c;快速交替并行(多个人一起行走) CPU 多核&#xff0c;多个线程可以同时执行; public class QuickSort {public …

hive部署

下载hive安装包&#xff1a;https://dlcdn.apache.org/hive/hive-2.3.9/解压及环境部署 tar -zxvf apache-hive-2.3.9-bin.tar.gz mv apache-hive-2.3.9-bin hivevim /etc/profile添加至环境变量 export HIVE_HOME/usr/local/hive export PATH$PATH:$HIVE_HOME/binsource /etc…

技术分享 | RCU :内核小“马达”,让你的产品弯道超车

在上一篇文章《编程界也内卷&#xff1f;浅析“斜杠青年”RCU 》中&#xff0c;鼎道智联带着大家一起认识了并行编程&#xff0c;了解了什么是 RCU &#xff0c;相信大家已经对 RCU 的特点和如何实现 Reader 无锁有了一定的了解。 今天就带着大家继续从 RCU 的实现入手&#xf…

ESDA in PySal (2) localjoincounts

ESDA in PySal (2) localjoincounts 参考:https://blog.csdn.net/angel0929/article/details/128433265 https://blog.csdn.net/allenlu2008/article/details/49895387 PySAL有5种全局自相关检验:Gamma值、Join Count、Moran’s I、Geary’s C、和Getis and Ord’s G 在下…

CVE-2023-36874 Windows错误报告服务本地权限提升漏洞分析

CVE-2023-36874 Windows错误报告服务本地权限提升漏洞分析 漏洞简介 Windows错误报告服务在提交错误报告前会创建wermgr.exe进程&#xff0c;而攻击者使用特殊手法欺骗系统创建伪造的wermgr.exe进程&#xff0c;从而以system权限执行代码。 影响版本 Windows10 1507 * Wind…

LC1011. 在 D 天内送达包裹的能力(JAVA)

在 D 天内送达包裹的能力 题目描述上期经典算法 题目描述 leetcode 1011. 在 D 天内送达包裹的能力 难度 - 中等 传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天&#xff0c;我们都会按给出重量&#xff08;we…

java主要的垃圾回收算法

垃圾收集算法了解吗&#xff1f; 标记-清除算法 标记 : 标记出所有需要回收的对象 清除&#xff1a;回收所有被标记的对象 主要存在两个缺点&#xff1a; 执行效率不稳定&#xff0c;如果 Java 堆中包含大量对象&#xff0c;而且其中大部分是需要被回收的&#xff0c;这时必…

macOS使用命令行连接Oracle(SQL*Plus)

Author: histonevonzohomail.com Date: 2023/08/25 文章目录 SQL\*Plus安装下载环境配置 SQL\*Plus远程连接数据库参考文献 原文地址&#xff1a;https://histonevon.top/archives/oracle-mac-sqlplus数据库安装&#xff1a;Docker安装Oracle数据库 (histonevon.top) SQL*Plus…

移动电源专用的单节锂离子电池充电器和恒定 5V 升压控制器HU5715

航誉微HU5715 为一款移动电源专用的单节锂离子电池充电器和恒定 5V 升压控制器&#xff0c;充电 部分集高精度电压和充电电流调节器、预充、充电状态指示和充电截止等功能于一体&#xff0c; 可以输出最大 1A 充电电流。而升压电路采用 CMOS 工艺制造的空载电流极低的 VFM 开 关…