【调度算法】进程调度算法、内存页面置换算法、LRU算法、LFU算法、磁盘调度算法等重点知识汇总

news2024/12/28 18:24:37

目录

进程调度算法

内存页面置换算法

LRU算法实现

LFU算法实现

磁盘调度算法


进程调度算法

当 CPU 空闲时,操作系统就选择内存中的某个「就绪状态」的进程,并给其分配 CPU。

什么时候会发生 CPU 调度呢?通常有以下情况:

  1. 当进程从运行状态转到等待状态;「非抢占式调度」
  2. 当进程从运行状态转到就绪状态;「抢占式调度」
  3. 当进程从等待状态转到就绪状态;「抢占式调度」
  4. 当进程从运行状态转到终止状态;「非抢占式调度」

非抢占式和抢占式:

  • 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。
  • 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。

1、先来先服务调度算法

最简单的一个调度算法,就是非抢占式的先来先服务算法了。

顾名思义,先来后到,每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。

但是当一个长作业先运行了,那么后面的短作业等待的时间就会很长,不利于短作业。

2、最短作业优先调度算法

最短作业优先调度算法优先选择运行时间最短的进程来运行,这有助于提高系统的吞吐量。

这显然对长作业不利,很容易造成一种极端现象。

比如,一个长作业在就绪队列等待运行,而这个就绪队列有非常多的短作业,那么就会使得长作业不断的往后推,周转时间变长,致使长作业长期不会被运行。

3、高响应比优先调度算法

高响应比优先 调度算法主要是权衡了短作业和长作业

每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行,「响应比优先级」的计算公式:

从上面的公式,可以发现:

  • 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行;
  • 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等待的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会;

4、时间片轮转调度算法

每个进程被分配一个时间段,称为时间片,即允许该进程在该时间段中运行。

  • 如果时间片用完,进程还在运行,那么将会把此进程从 CPU 释放出来,并把 CPU 分配另外一个进程;
  • 如果该进程在时间片结束前阻塞或结束,则 CPU 立即进行切换;
  • 如果时间片设得太短会导致过多的进程上下文切换,降低了 CPU 效率;
  • 如果设得太长又可能引起对短作业进程的响应时间变长。

通常时间片设为 20ms~50ms 通常是一个比较合理的折中值。

5、最高优先级调度算法

调度程序能从就绪队列中选择最高优先级的进程进行运行,这称为最高优先级调度算法

该算法也有两种处理优先级高的方法,非抢占式和抢占式:

  • 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。
  • 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。

但是依然有缺点,可能会导致低优先级的进程永远不会运行。

6、多级反馈队列调度算法

多级反馈队列调度算法是「时间片轮转算法」和「最高优先级算法」的综合和发展。

  • 「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短
  • 「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;

  • 设置了多个队列,赋予每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短
  • 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;
  • 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;

可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也会更长了,所以该算法很好的兼顾了长短作业,同时有较好的响应时间。

Linux操作系统采用的是多级反馈队列调度的调度方式。该调度算法将就绪队列分成多个优先级队列,每个队列具有不同的优先级,并且可以采用不同的调度策略。

在Linux中,CFS是一种实现该调度策略的具体实现。CFS旨在提供公平性和响应性,确保所有进程都有公平的机会获得CPU时间片。每个进程都被赋予一个权重,CFS会根据进程的权重来分配CPU时间片,以确保进程获得的CPU时间与其权重成比例。

CFS是Linux的默认进程调度器,用于普通进程(非实时进程)。对于实时进程,Linux还提供了一个实时调度器,如先进先出(FIFO)和循环调度(RR),用于满足实时任务的需求。


以下是CFS的一些关键特点和工作原理:

公平性:CFS的核心目标是实现公平的CPU分配。每个进程都被赋予一个权重(weight),CFS根据进程的权重来分配CPU时间片,以确保进程获得的CPU时间与其权重成比例。这意味着高权重进程会获得更多的CPU时间,低权重进程会获得更少的CPU时间。

虚拟运行时间:CFS使用虚拟运行时间来衡量每个进程已经使用的CPU时间。虚拟运行时间越小的进程被认为更"饥饿",因此它们会在调度时获得更高的优先级,以获得更多的CPU时间。

红黑树:CFS使用红黑树来管理就绪队列。每个进程都在红黑树上维护一个节点,节点按照虚拟运行时间排序。这样,CFS可以以O(log n)的时间复杂度找到具有最小虚拟运行时间的进程。

时间片分配:CFS不像一些传统的调度器那样使用固定的时间片(例如,10毫秒)。相反,它动态计算每个进程的时间片,以适应不同的权重和进程的需求。

动态调整权重:CFS支持动态调整进程的权重,以允许管理员或应用程序根据需要调整进程的调度优先级。

CFS的设计使其成为一个高度公平和响应性的调度器,适用于多用户和多任务环境。CFS确保不会发生某些进程长期霸占CPU资源的情况,从而提高了系统的整体性能和用户体验。这使得CFS成为Linux默认的普通进程调度器。

内存页面置换算法

在了解内存页面置换算法前,我们得先谈一下缺页异常(缺页中断)

当 CPU 访问的页面不在物理内存时,便会产生一个缺页中断,请求操作系统将所缺页调入到物理内存。

我们来看一下缺页中断的处理流程:

  1. 在 CPU 里访问一条 Load M 指令,然后 CPU 会去找 M 所对应的页表项。
  2. 如果该页表项的状态位是「有效的」,那 CPU 就可以直接去访问物理内存了,如果状态位是「无效的」,则 CPU 则会发送缺页中断请求。
  3. 操作系统收到了缺页中断,则会执行缺页中断处理函数,先会查找该页面在磁盘中的页面的位置。
  4. 找到磁盘中对应的页面后,需要把该页面换入到物理内存中,但是在换入前,需要在物理内存中找空闲页,如果找到空闲页,就把页面换入到物理内存中。
  5. 页面从磁盘换入到物理内存完成后,则把页表项中的状态位修改为「有效的」。
  6. 最后,CPU 重新执行导致缺页异常的指令。

上面所说的过程,第 4 步是能在物理内存找到空闲页的情况,那如果找不到呢?

页面置换算法的功能是,当出现缺页异常,需调入新页面而内存已满时,选择被置换的物理页面也就是说选择一个物理页面换出到磁盘,然后把需要访问的页面换入到物理页。

那其算法目标则是,尽可能减少页面的换入换出的次数,常见的页面置换算法有如下几种:

1、最佳页面置换算法

最佳页面置换算法基本思路是,置换在「未来」最长时间不访问的页面

所以,该算法实现需要计算内存中每个逻辑页面的「下一次」访问时间,然后比较,选择未来最长时间不访问的页面。

很理想但是实际系统中无法实现,我们是无法预知每个页面在「下一次」访问前的等待时间。

2、先进先出置换算法

既然我们无法预知页面在下一次访问前所需的等待时间,那我们可以选择在内存驻留时间很长的页面进行中置换,这个就是「先进先出置换」算法的思想。

3、最近最久未使用的置换算法LRU

发生缺页时,选择最长时间没有被访问的页面进行置换,也就是说,该算法假设已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。

这种算法近似最优置换算法,最优置换算法是通过「未来」的使用情况来推测要淘汰的页面,而 LRU 则是通过「历史」的使用情况来推测要淘汰的页面。

4、时钟页面置换算法

时钟页面置换算法跟 最近最久未使用的置换算法 近似,又是对 先进先出置换算法 的一种改进。

该算法的思路是,把所有的页面都保存在一个类似钟面的「环形链表」中,一个表针指向最老的页面。

当发生缺页中断时,算法首先检查表针指向的页面:

  • 如果它的访问位位是 0 就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置
  • 如果访问位是 1 就清除访问位,并把表针前移一个位置,重复这个过程直到找到了一个访问位为 0 的页面为止;

5、最不常用算法LFU

当发生缺页中断时,选择「访问次数」最少的那个页面,并将其淘汰

它的实现方式是,对每个页面设置一个「访问计数器」,每当一个页面被访问时,该页面的访问计数器就累加 1。在发生缺页中断时,淘汰计数器值最小的那个页面

LRU算法实现

选择最长时间没有被访问的页面进行删除

"最近常用"的元素是位于双向链表的尾部,

"最久未使用"的元素是位于链表的头部。

get方法:当调用get方法获取一个键的值时,如果键存在于缓存中,会将该键移到链表尾部,表示它是最近使用的元素。这是通过makeRecently方法实现的,其中删除了原来的位置并将键添加到链表尾部。

put方法:当调用put方法插入一个新的键值对时,新元素被插入到链表尾部,表示它是最近使用的元素。如果缓存已满,会移除链表头部元素,即最久未使用的键,以腾出空间。

以下是为什么设置这几个成员变量以及代码的核心思路:

int cap(缓存容量):这个成员变量表示LRU缓存的容量,即缓存可以存储的键值对的最大数量。它是必需的,因为它决定了缓存的大小,当缓存达到容量上限时,需要淘汰最久未使用的元素,以便为新元素腾出空间。

std::unordered_map<int, int> cache(存储键值对的哈希表):这个哈希表用于实际存储缓存中的键值对。其中,键是缓存中的键,值是键对应的值。这个数据结构用于快速查找和更新缓存中的元素,以便在get和put操作中高效地访问和修改缓存。

std::list<int> lruList(存储最近使用的键的双向链表):这个双向链表用于跟踪键的使用顺序。最近使用的键会被添加到链表的尾部,而最久未使用的键位于链表的头部。这个链表是LRU缓存的核心,它帮助我们维护键的访问顺序,以便在淘汰元素时能够轻松地选择最久未使用的键。

void makeRecently(int key)(辅助函数):这个函数用于将指定的键标记为最近使用,即将其从链表中删除,然后添加到链表的尾部。这是确保最近使用的键总是在链表尾部的关键操作。

LRU缓存的核心思路是,通过双向链表来维护键的使用顺序,最近使用的键位于链表尾部,最久未使用的键位于链表头部。当执行get或put操作时,会调用makeRecently函数来确保访问的键被

#include <iostream>
#include <unordered_map>
#include <list>

class LRUCache {
public:
    LRUCache(int capacity) : cap(capacity) {}

    // 获取键对应的值
    int get(int key) {
        // 如果键不存在于缓存中,返回-1
        if (cache.find(key) == cache.end()) {
            return -1;
        }
        // 将 key 变为最近使用,即更新其在链表中的位置
        makeRecently(key);
        return cache[key]; // 返回键对应的值
    }

    // 向缓存中插入键值对
    void put(int key, int val) {
        // 如果键已存在于缓存中
        if (cache.find(key) != cache.end()) {
            // 修改键的值
            cache[key] = val;
            // 将 key 变为最近使用,即更新其在链表中的位置
            makeRecently(key);
            return;
        }

        // 如果缓存已满
        if (cache.size() >= cap) {
            // 移除链表头部元素,即最久未使用的键
            int oldestKey = lruList.front();
            lruList.pop_front(); // 从链表中移除
            cache.erase(oldestKey); // 从缓存中移除
        }

        // 将新的键值对插入到链表尾部,即最近使用的位置
        lruList.push_back(key); // 添加到链表尾部
        cache[key] = val; // 添加到缓存
    }

private:
    int cap; // 缓存容量
    std::unordered_map<int, int> cache; // 存储键值对的哈希表
    std::list<int> lruList; // 存储最近使用的键的双向链表

    // 辅助函数,将 key 变为最近使用
    void makeRecently(int key) {
        // 从链表中删除 key
        lruList.remove(key);
        // 添加到链表尾部,表示最近使用
        lruList.push_back(key);
    }
};

int main() {
    // 创建容量为2的LRU缓存
    LRUCache lruCache(2);

    // 插入键值对 (1, 1) 和 (2, 2)
    lruCache.put(1, 1);
    lruCache.put(2, 2);

    // 获取键 1 对应的值,输出 1
    std::cout << lruCache.get(1) << std::endl;

    // 插入键值对 (3, 3),此时缓存已满,会移除键 2
    lruCache.put(3, 3);

    // 获取键 2 对应的值,输出 -1,因为键 2 已被移除
    std::cout << lruCache.get(2) << std::endl;

    // 插入键值对 (4, 4),此时缓存已满,会移除键 1
    lruCache.put(4, 4);

    // 获取键 1 对应的值,输出 -1,因为键 1 已被移除
    std::cout << lruCache.get(1) << std::endl;

    // 获取键 3 对应的值,输出 3
    std::cout << lruCache.get(3) << std::endl;

    // 获取键 4 对应的值,输出 4
    std::cout << lruCache.get(4) << std::endl;

    return 0;
}

LFU算法实现

选择「访问次数」最少的那个页面并删除

LFUCache类:这是LFU缓存的主要类。它包含了以下成员变量和方法:

cap:表示缓存的容量,即最多可以存储多少个键值对。

minFreq:表示缓存中最低的使用频率。初始值为0。

cache:使用std::unordered_map来存储缓存的键值对,其中键是键值对的键,值是一个std::pair,包含值和频率。

freqList:使用std::unordered_map来存储不同频率的键的集合,其中频率是键,值是一个std::list,表示具有相同频率的键的链表。

get方法:用于获取指定键的值。如果键不存在于缓存中,返回-1。如果存在,则更新键的频率信息(通过updateFreq方法),然后返回键对应的值。

put方法:用于插入新的键值对或更新现有键的值。首先检查缓存是否已满,如果满了则需要淘汰一个元素。然后,检查键是否已存在于缓存中。如果键已存在,则更新值并更新频率信息。如果键不存在,则插入新的键值对,频率初始化为1。如果缓存已满,会淘汰最低频率的键,即在freqList中最靠后的频率。

updateFreq方法:用于更新指定键的频率信息。它会获取键的当前频率,增加频率,然后将键从旧的频率列表中移除,并添加到新的频率列表的头部。如果更新后的频率列表为空且更新的频率等于最低频率,会更新最低频率。

main函数:在main函数中,我们创建了一个LFU缓存对象,并演示了如何使用该缓存对象来插入、获取和移除键值对,以及处理缓存容量不足的情况。通过这些操作,我们可以观察LFU缓存的行为。

以下是我在设计LFU缓存时的思路和这些成员变量的作用:

cap(缓存容量):cap成员变量表示缓存的最大容量,即缓存可以存储的键值对的数量。这个成员变量很重要,因为它决定了缓存的大小,当缓存容量达到上限时,需要淘汰元素来为新元素腾出空间。

minFreq(最低频率):minFreq成员变量用于跟踪缓存中最低的使用频率。初始时,它被设置为0,表示缓存中还没有任何元素被访问过。随着操作的进行,minFreq可能会不断更新,因为频率较低的元素被淘汰后,可能会影响到最低频率。

cache(缓存数据结构):cache是一个哈希表,用于存储缓存中的键值对。键是缓存中的键,值是一个std::pair,其中包含值和频率信息。这个哈希表用于快速查找缓存中的键值对,以及更新键的值和频率。

freqList(频率信息数据结构):freqList也是一个哈希表,用于存储不同频率的键的集合。每个频率对应一个链表,链表中包含了具有相同频率的键。这个数据结构用于管理键的频率信息,以及在淘汰元素时找到最低频率的键。

#include <iostream>
#include <unordered_map>
#include <list>
#include <map>

class LFUCache {
public:
    LFUCache(int capacity) : cap(capacity), minFreq(0) {}

    int get(int key) {
        if (cache.find(key) == cache.end()) {
            return -1;
        }

        // 更新频率信息
        updateFreq(key);

        return cache[key].first;
    }

    void put(int key, int value) {
        if (cap <= 0) {
            return;
        }

        // 如果键已存在,更新值并更新频率信息
        if (cache.find(key) != cache.end()) {
            cache[key].first = value;
            updateFreq(key);
            return;
        }

        // 如果缓存已满,淘汰最低频率且最久未使用的键
        if (cache.size() >= cap) {
            int removedKey = freqList[minFreq].back();
            freqList[minFreq].pop_back();
            cache.erase(removedKey);
        }

        // 插入新键值对,频率初始化为1
        cache[key] = {value, 1};
        freqList[1].push_front(key);
        minFreq = 1;
    }

private:
    int cap; // 缓存容量
    int minFreq; // 最低频率
    std::unordered_map<int, std::pair<int, int>> cache;  // 存储缓存数据,{key, {value, frequency}}
    std::unordered_map<int, std::list<int>> freqList;    // 存储频率信息,{frequency, [keys]}

    // 辅助函数,更新键的频率信息
    void updateFreq(int key) {
        int prevFreq = cache[key].second; // 获取键的当前频率
        cache[key].second++; // 更新频率

        // 更新频率列表
        freqList[prevFreq].remove(key); // 从旧的频率列表中移除
        freqList[prevFreq + 1].push_front(key); // 添加到新的频率列表的头部

        // 如果更新后的频率列表为空且更新的频率等于最低频率,更新最低频率
        if (freqList[prevFreq].empty() && prevFreq == minFreq) {
            minFreq++;
        }
    }
};

磁盘调度算法

右边的图就是一个盘片的结构,盘片中的每一层分为多个磁道,每个磁道分多个扇区,每个扇区是 512 字节。那么,多个具有相同编号的磁道形成一个圆柱,称之为磁盘的柱面。

磁盘调度算法的目的很简单,就是为了提高磁盘的访问性能,一般是通过优化磁盘的访问请求顺序来做到的。

寻道的时间是磁盘访问最耗时的部分,如果请求顺序优化的得当,必然可以节省一些不必要的寻道时间,从而提高磁盘的访问性能。

1、先来先服务

先来先服务,顾名思义,先到来的请求,先被服务。

在寻道过程中,可能已经遇到⼀些以后可能需要访问的 磁道,但是会跳过,⽽造成 访问磁道 耗费时间较多。

2、最短寻道时间优先

最短寻道时间优先算法的工作方式是,优先选择从当前磁头位置所需寻道时间最短的请求

每次选择距离当前磁头最近的待处理请求

但这个算法可能存在某些请求的饥饿, 可能造成部分请求 “饥饿”(当某个请求的磁盘距离磁头较远,⽽⼀直有⽐其更近的请求时,这个请求⼀直⽆ 法执⾏) 这里产生饥饿的原因是磁头在一小块区域来回移动

3、扫描算法

最短寻道时间优先算法会产生饥饿的原因在于:磁头有可能再一个小区域内来回得移动。

为了防止这个问题,可以规定:磁头在一个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向,这就是扫描算法

这种算法也叫做电梯算法,比如电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向。

磁头先响应左边的请求,直到到达最左端( 0 磁道)后,才开始反向移动,响应右边的请求。

扫描调度算法性能较好,不会产生饥饿现象,但是存在这样的问题,中间部分的磁道会比较占便宜,中间部分相比其他部分响应的频率会比较多,也就是说每个磁道的响应频率存在差异。

4、循环扫描算法

扫描算法使得每个磁道响应的频率存在差异,那么要优化这个问题的话,可以总是按相同的方向进行扫描,使得每个磁道的响应频率基本一致。

循环扫描规定:只有磁头朝某个特定方向移动时,才处理磁道访问请求,而返回时直接快速移动至最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且返回中途不处理任何请求,该算法的特点,就是磁道只响应一个方向上的请求

磁头先响应了右边的请求,直到碰到了最右端的磁道 199,就立即回到磁盘的开始处(磁道 0),但这个返回的途中是不响应任何请求的,直到到达最开始的磁道后,才继续顺序响应右边的请求。

循环扫描算法相比于扫描算法,对于各个位置磁道响应频率相对比较平均。

5、LOOK 与 C-LOOK算法

我们前面说到的扫描算法和循环扫描算法,都是磁头移动到磁盘「最始端或最末端」才开始调换方向。

那这其实是可以优化的,优化的思路就是磁头在移动到「最远的请求」位置,然后立即反向移动。

那针对 SCAN 算法的优化则叫 LOOK 算法,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,反向移动的途中会响应请求

而针 C-SCAN 算法的优化则叫 C-LOOK,它的工作方式,磁头在每个方向上仅仅移动到最远的请求位置,然后立即反向移动,而不需要移动到磁盘的最始端或最末端,反向移动的途中不会响应请求

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

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

相关文章

数字孪生燃气可视化系统的九问九答

关键词&#xff1a;数字孪生燃气、智慧燃气、数字孪生燃气系统、智慧燃气平台、智慧燃气场站 谈谈数字孪生燃气系统&#xff1f; 数字孪生燃气是将数字孪生技术应用于燃气运营中&#xff0c;与燃气系统物理实体、数据中心、信息系统等相结合&#xff0c;从而完成辅助管网设计…

想要在手机上查看三维模型、正射影像、激光点云?快来试试这款app

「四维轻云」是一款轻量化的地理空间数据管理云平台&#xff0c;支持地理空间数据的在线管理、编辑及分享。平台有项目管理、场景搭建、发布分享、素材库等功能模块&#xff0c;支持多用户在线协作管理&#xff0c;实现了轻量化、便捷化的空间数据应用。 很多行业用户想要在手…

Observability:使用 OpenTelemetry 自动检测 Java 应用程序

作者&#xff1a;David Hope 在快节奏的软件开发领域&#xff0c;特别是在云原生领域&#xff0c;DevOps 和 SRE 团队日益成为应用程序稳定性和增长的重要合作伙伴。 DevOps 工程师不断优化软件交付&#xff0c;而 SRE 团队则充当应用程序可靠性、可扩展性和顶级性能的管理者。…

linux使用操作[2]

文章目录 版权声明网络传输ping命令wget命令curl命令端口linux端口端口命令和工具 进程管理查看进程关闭进程 主机状态top命令内容详解磁盘信息监控 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程序员或相…

视频转码软件 EditReady mac中文版介绍

EditReady for Mac是一款专业高效的视频转码器&#xff0c;拥有快速&#xff0c;强大的特点&#xff0c;可以调整视频大小&#xff0c;对视频进行旋转&#xff0c;重新定时等功能&#xff0c;对于需要的朋友&#xff0c;还可以查看和编辑元数据&#xff0c;EditReady Mac中文版…

Ubuntu 安装 Docker 的详细步骤

文章目录 简介1.更新2.安装必要的软件包2.1 基于阿里源 3.验证 Docker 安装是否成功4.安装后的一些常规设置及常用的命令4.1 启动 Docker4.2 Docker 在系统启动时自动运行4.3 运行一个 Hello World 镜像4.4 查看docker运行状态 欢迎来到这篇关于在 Ubuntu 上安装 Docker 的教程…

【数据库——MySQL】(10)视图和索引

目录 1. 视图1.1 创建视图1.2 查询视图 2. 索引2.1 索引的分类2.2 索引的建立 参考书籍 1. 视图 1.1 创建视图 基础语法&#xff1a; CREATE [OR REPLACE] VIEW 视图名[(列名表)]ASSELECT语句[WITH CHECK OPTION]说明&#xff1a; 在默认情况下&#xff0c;将在当前数据库创…

WRF高精度气象模拟技术及在地学领域中的实践应用

随着生态文明建设和“碳中和”战略的持续推进&#xff0c;我国及全球气候变化及应对是政府、科学界及商业界关注的焦点。气候是多个领域&#xff08;生态、水资源、风资源及碳中和等问题&#xff09;的主要驱动因素&#xff0c;合理认知气候变化有利于解释生态环境变化机理及过…

一个案例熟悉使用pytorch

文章目录 1. 完整模型的训练套路1.2 导入必要的包1.3 准备数据集1.3.1 使用公开数据集&#xff1a;1.3.2 获取训练集、测试集长度&#xff1a;1.3.3 利用 DataLoader来加载数据集 1.4 搭建神经网络1.4.1 测试搭建的模型1.4.2 创建用于训练的模型 1.5 定义损失函数和优化器1.6 使…

Open3D点云处理简明教程

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 这是“激光雷达入门”文章的延续。 在这篇文章中&#xff0c;我们将查看用于处理点云的 python 库和 Open3D 数据结构&#xff0c;执行可视化并操作点云数据&#xff0c;以便进行后续的分析处理。 如果你需要快速预览3D点…

速卖通,获取标题,价格,品牌字段,免测

aliexpress.item_get&#xff08;获得aliexpress商品详情&#xff09; 为了进行电商平台 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个速卖通应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09…

宏基因组元素循环:碳氮循环的动态耦合分析

微生物在环境中生长并不是靠单一元素周转而存活的&#xff0c;生物地球化学元素循环&#xff0c;例如碳、氮、磷、硫等&#xff0c;存在复杂的耦合关系。 研究表明&#xff0c;陆地生态系统的碳氮耦合过程中&#xff0c;氮的输入在促进植物初级生产力和土壤碳固存的同时也增加…

【计算机网络笔记八】应用层(五)HTTPS

什么是 HTTPS HTTPS 解决了 HTTP 不安全的问题 HTTP 整个传输过程数据都是明文的&#xff0c;任何人都能够在链路中截获、修改或者伪造请求&#xff0f;响应报文&#xff0c;数据不具有可信性。 ① HTTPS 使用加密算法对报文进行加密&#xff0c;黑客截获了也看不懂 ② HTTP…

python安全工具开发笔记(五)——python数据库编程

一、Python DB API 在没有Python DB API之前&#xff1a; 有Python DB API之后&#xff1a; Python DB API包含内容 Python DB API访问数据库流程 二、Python Mysql开发环境 三、Python 数据库编程实例 数据库连接对象connection 连接对象&#xff1a;建立Python客户端…

post为什么会发送两次请求?

1 同源策略 在浏览器中&#xff0c;内容是很开放的&#xff0c;任何资源都可以接入其中&#xff0c;如 JavaScript 文件、图片、音频、视频等资源&#xff0c;甚至可以下载其他站点的可执行文件。但也不是说浏览器就是完全自由的&#xff0c;如果不加以控制&#xff0c;就会出现…

elementui引入弹出框报错:this.$alert is not defined 解决方案

1.按需引入文件element.js 注意&#xff1a;引入Message&#xff0c;MessageBox两个组件就行&#xff0c;alert包括在MessageBox里面了。 之前我引入了Alert组件&#xff0c;发现不行 2.在vue的prototype里注册伪名字 3.组件里直接调用就行了 4.实现效果 我发现elementui调用…

【实验记录】AGW | Visible-Infrared Re-ID

【RT】Visible Thermal Re-IDDeep Learning for Person Re-identification: A Survey and Outlook中提出了一个针对单/跨模态行人重识别的baseline&#xff1a;AGW 做过两次&#xff0c;在测试阶段有问题&#xff0c;现在再重做一次&#x1f914;Code RTX3090 修改数据集路…

数据中台实战(00)-大数据的尽头是数据中台吗?

除了支撑集团的大数据建设&#xff0c;团队还提供To B服务&#xff0c;因此我也有机会接触到一些正在做数字化转型的传统企业。从2018年末开始&#xff0c;原先市场上各种关于大数据平台的招标突然不见了&#xff0c;取而代之的是数据中台项目&#xff0c;建设数据中台俨然成为…

docker安装高斯数据库openGauss数据库

1.创建容器 #创建数据没有挂在的容器 docker run --name opengauss --privilegedtrue -d -e GS_PASSWORDEnmo123 -p 8090:5432 enmotech/opengauss:latest 2. 进入容器&#xff0c;并切换omm用户&#xff0c;使用gsql连接高斯数据库 [rootansible ~]# docker ps -a CONTAIN…

【Proteus仿真】【STM32单片机】多功能智能台灯

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1604液晶、按键、蜂鸣器、语音识别模块、PCF8591 ADC模块、DHT11温湿度传感器、光线传感器、台灯、人体红外传感器等。 主要功能&#xff1a…