【单例模式】—— C++设计模式【附百度Apollo单例模式详细解读】

参考资料:
(1)单例模式—— 代码随想录
(2)我给面试官讲解了单例模式后,他对我竖起了大拇指!
(3)C++ 单例模式详解
(4)单例模式之C++实现,智能指针,线程安全版本
(5)深入探索单例设计模式:以百度 Apollo 为例

1 单例模式

单例模式:创建型设计模式
核心思想:保证一个类只有一个实例,并提供一个全局访问点来访问这个实例

  • 一个实例:在整个应用程序中,只存在该类的一个实例对象,而不是创建多个相同类型的对象
  • 全局访问点:为了让其他类能够获取到这个唯一实例,该类提供了一个全局访问点(通常是一个静态方法),通过这个方法就能获得实例

单例模式的类型:

  • 懒汉式:在真正需要使用对象时才去创建该单例类对象
  • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用

2 使用单例模式的需求

  1. 全局控制:保证只有一个实例,严格的控制客户怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问
  2. 节省资源:避免多次创建了相同的对象,从而节省了系统资源,而且多个模块还可以通过单例实例共享数据
  3. 懒加载:只有在需要时才进行实例化

3 实现单例模式的步骤

  • 私有的构造函数:防止外部代码直接创建类的实例
  • 提供一个公有的静态方法:通过公有的静态方法来获取类的实例
  • 在类中定义一个私有静态指针,指向本类的变量的静态变量指针:保存该类的唯一实例

4 懒汉式创建单例对象

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。
在这里插入图片描述
适用于单线程场景的懒汉式单例

class Singleton {
public:static Singleton* getInstance() {if (instance_ = NULL) {instance_ = new Singleton();	}return instance_;}
private:Singleton() = default;static Singleton* instance_;
};
// 初始化静态成员变量
Singleton* Singleton::instance_ = NULL;

懒汉式单例模式存在内存泄漏的问题:

  • 使用智能指针
class Singleton {
public:static shared_ptr<Singleton> getInstance() {if (instance_ == NULL) {lock_guard<mutex> lock(mutex_);if (instance_ == NULL) {instance_ = shared_ptr<Singleton>(new Singleton());	}}	return instance_;}
private:Singleton() = default;static shared_ptr<Singleton> instance_;static mutex mutex_;
};
shared_ptr<Singleton> Singleton::instance_ = NULL;
mutex Singleton::mutex_;
  • 使用静态的嵌套类对象
class Singleton {
public:static Singleton* getInstance() {if (instance_ = NULL) {instance_ = new Singleton();	}return instance_;}
private:Singleton() = default;static Singleton* instance_;class Deletor {public:~Deletor() {if (Singleton::instance_ != NULL)delete Singleton::instance_;}};static Deletor deletor;
};
// 初始化静态成员变量
Singleton* Singleton::instance_ = NULL;

Meyers 单例:静态局部变量的懒汉单例(C++11线程安全)

C++11规定了local static在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。《Effective C++》使用函数内的 local static 对象,这样,只有当第一次访问getInstance()方法时才创建实例。
如果多个线程同时尝试初始化相同的静态局部变量,初始化动作只会发生一次,这个内部特性通常也是通过双检锁模式实现的。

class Singleton {public:static Singleton& getInstance() {static Singleton instance_;return instance_;	}private:Singleton() {};~Singleton() {};Singleton(const Singleton&);Singleton& operator=(const Singleton&);	
};

多线程安全的懒汉式单例:双检锁 + 原子变量实现
(1) 使用双检锁确保性能:针对单检锁方法中存在的性能问题,有一种所谓的双检锁模式(Double-Checked Locking Pattern,DCLP)优化方案,即在 GetInstance 中执行锁操作前,在最外层额外地进行一次实例指针的检查操作(“双检”的体现),这样可以保证实例指针完成内存分配后,单纯的实例访问操作不会再附带锁操作带来的性能开销

class LazySingleton
{
private:static LazySingleton *pinstance_;static std::mutex mutex_;private:LazySingleton() {}LazySingleton(const LazySingleton &) = delete;LazySingleton &operator=(const LazySingleton &) = delete;public:~LazySingleton() {}public:static LazySingleton *GetInstance();
};LazySingleton *LazySingleton::pinstance_{nullptr};
std::mutex LazySingleton::mutex_;LazySingleton *LazySingleton::GetInstance()
{if (nullptr == pinstance_){std::lock_guard<std::mutex> lock(mutex_);if (nullptr == pinstance_){pinstance_ = new LazySingleton;}}return pinstance_;
}

双检锁方法初衷虽好,但却破坏了多线程场景下的安全性,这是由动态内存分配时 new 底层操作的非原子性导致的,执行 pinstance_ = new LazySingleton; 语句时,底层其实对应了三个步骤:

  • 向系统申请分配内存,大小为 sizeof(LazySingleton)
  • 调用 LazySingleton 的默认构造函数在申请的内存上构造出实例
  • 返回申请内存的指针给 pinstance_

根本问题在于上面的这三个步骤无法确保执行顺序。例如,出于优化的原因,处理器很可能调整步骤 3 和步骤 2 的执行顺序(按照 1、3、2 的顺序执行)。

假设,现在某个线程执行到了 pinstance_ = new LazySingleton; 语句,底层操作完成了内存申请(步骤 1)和实例指针赋值(步骤 3),但尚未完成申请内存的构造(步骤 2),意即,现在 pinstance_ 指向的是一片脏内存。此时,另一个线程恰好执行到双检锁的最外层检查,该线程发现 pinstance_ 非空(发生了脏读),检查为 false,因而直接取走了尚未完成构造的实例指针(return pinstance_;),从而可能诱发程序未定义行为(undefined behavior)。

(2) 使用原子变量确保多线程安全性
可以通过封装一个单例指针类型的 std::atomic 原子对象,将单例指针的读写操作转化为对原子对象的操作,以此来确保双检锁实现的懒汉式单例的多线程安全性。
std::atomic 是 C++11 定义于 <atomic> 中的新特性,每个 std::atomic 模板的实例化和全特化定义一个原子类型,若一个线程写入原子对象,同时另一线程从它读取,则行为良好定义。另外,对原子对象的访问可以建立线程间同步,并按 std::memory_order 枚举类型中的枚举常量对非原子内存访问定序:

typedef enum memory_order {memory_order_relaxed,memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel,memory_order_seq_cst
} memory_order;

下面给出经典的基于双检锁 + 原子变量的懒汉式单例实现:

class LazySingleton
{
private:static std::atomic<LazySingleton *> ainstance_;static std::mutex mutex_;private:LazySingleton() {}LazySingleton(const LazySingleton &) = delete;LazySingleton &operator=(const LazySingleton &) = delete;public:~LazySingleton() {}public:static LazySingleton *GetInstance();
};std::atomic<LazySingleton *> LazySingleton::ainstance_;
std::mutex LazySingleton::mutex_;LazySingleton *LazySingleton::GetInstance()
{LazySingleton *tmp = ainstance_.load(std::memory_order_acquire);if (nullptr == tmp){std::lock_guard<std::mutex> lock(mutex_);tmp = ainstance_.load(std::memory_order_relaxed);if (nullptr == tmp){tmp = new LazySingleton;ainstance_.store(tmp, std::memory_order_release);}}return tmp;
}
  • load:原子性地加载并返回原子变量的当前值,类似读操作。唯一形参类型为std::memory_order,默认值为 memory_order_seq_cst
  • store:根据第一实参原子性地替换原子变量的当前值,类似写操作。第二形参类型为 std::memory_order,默认值为 memory_order_seq_cst

上面这种原子变量的使用方式称为 Acquire-Release Semantic 内存模型,如果保持 load 和 store 的 std::memory_order 参数缺省,则成为 Sequential Consistency 内存模型,性能会稍有损失。

5 饿汉式创建单例对象

在这里插入图片描述
在main函数之前初始化,所以没有线程安全的问题。但是潜在问题在于no-local static对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。就是说说,static Singleton instance_;static Singleton& getInstance()二者的初始化顺序不确定,如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。

#include <iostream>using namespace std;class Singleton {
public:static Singleton* getInstance() {return instance_;}
private:Singleton() {cout << "a" << endl;};~Singleton() {};Singleton(const Singleton&);Singleton& operator=(const Singleton&);
private:static Singleton* instance_;
};// initialize defaultly
Singleton* Singleton::instance_ = new Singleton();
int main()
{cout << "we get the instance" << endl;Singleton* a1 = Singleton::getInstance();cout << "we destroy the instance" << endl;system("pause");return 0;
}

运行结果:

a
we get the instance
we destroy the instance

6 百度 Apollo 中的懒汉式单例:once_flag & call_once 实现

一个普通的 SensorManager 类经宏定义 DECLARE_SINGLETON(SensorManager) 修饰成为单例类:

class SensorManager {// ...//// other code//// ...DECLARE_SINGLETON(SensorManager)
};

DECLARE_SINGLETON(classname) 定义在 apollo/cyber/common/macros.h中:

#ifndef CYBER_COMMON_MACROS_H_
#define CYBER_COMMON_MACROS_H_#include <iostream>
#include <memory>
#include <mutex>
#include <type_traits>
#include <utility>#include "cyber/base/macros.h"DEFINE_TYPE_TRAIT(HasShutdown, Shutdown)template <typename T>
typename std::enable_if<HasShutdown<T>::value>::type CallShutdown(T *instance) {instance->Shutdown();
}template <typename T>
typename std::enable_if<!HasShutdown<T>::value>::type CallShutdown(T *instance) {(void)instance;
}// There must be many copy-paste versions of these macros which are same
// things, undefine them to avoid conflict.
#undef UNUSED
#undef DISALLOW_COPY_AND_ASSIGN#define UNUSED(param) (void)param#define DISALLOW_COPY_AND_ASSIGN(classname) classname(const classname &) = delete;    classname &operator=(const classname &) = delete;#define DECLARE_SINGLETON(classname)                                      public:                                                                  static classname *Instance(bool create_if_needed = true) {              // 提供对唯一实例的全局访问点static classname *instance = nullptr;                                 if (!instance && create_if_needed) {                                  static std::once_flag flag;                                         std::call_once(flag,                                                [&] { instance = new (std::nothrow) classname(); }); }                                                                     return instance;                                                      }                                                                       static void CleanUp() {                                                 auto instance = Instance(false);                                      if (instance != nullptr) {                                            CallShutdown(instance);                                             }                                                                     }                                                                       private:                                                                 classname();                                                            DISALLOW_COPY_AND_ASSIGN(classname)#endif  // CYBER_COMMON_MACROS_H_

DECLARE_SINGLETON(classname) 在预处理阶段会被替换为:
(1)静态方法 Instance
(2)私有的泛化默认构造函数和嵌套的宏定义 DISALLOW_COPY_AND_ASSIGN
(3) 静态方法 CleanUp

7.1 泛化的单例

DECLARE_SINGLETON(classname)如何将任意一个类修饰为单例类的:
(1)提供对唯一实例的全局访问点

static classname *instance = nullptr;

实例访问点的全局性通过静态方法 Instance 实现。

(2)多线程安全
实现方式的多线程安全性由 std::once_flagstd::call_once 保证,两者都是 C++11 定义于<mutex>中的新特性,配合使用可以确保多线程场景下可调用对象的唯一执行。
std::once_flagstd::call_once 的辅助结构体,在 GNU 中的实现如下:

struct once_flag
{
private:typedef __gthread_once_t __native_type;__native_type  _M_once = __GTHREAD_ONCE_INIT;public:/// Constructorconstexpr once_flag() noexcept = default;/// Deleted copy constructoronce_flag(const once_flag&) = delete;/// Deleted assignment operatoronce_flag& operator=(const once_flag&) = delete;template<typename _Callable, typename... _Args>friend voidcall_once(once_flag& __once, _Callable&& __f, _Args&&... __args);
};

call_once 被声明为 once_flag 的友元函数,为的是 call_once 可以修改 once_flag 中的 _M_once 成员(可调用对象的调用状态)。

std::call_once 是一个可变参数模板函数
可变参数经完美转发传入可调用对象,具体到 Apollo 中,可调用对象指的是为实例指针分配动态内存的 lambda 表达式:

[&] { instance = new (std::nothrow) classname(); }

std::call_once通过间接调用 pthread_once 函数来确保传入的可调用对象即使在多线程场景下也只能被执行一次

(3) 防止私自创建实例

#define DISALLOW_COPY_AND_ASSIGN(classname) classname(const classname &) = delete;    classname &operator=(const classname &) = delete;

分析CleanUp静态方法,该方法允许用户调用时执行一些自定义的清理工作:

static void CleanUp() {auto instance = Instance(false);if (instance != nullptr) {CallShutdown(instance);}
}

CallShutdown 模板函数包含两个经类型萃取(type traits)进行重载的实现

template <typename T>
typename std::enable_if<HasShutdown<T>::value>::type CallShutdown(T *instance) {instance->Shutdown();
}template <typename T>
typename std::enable_if<!HasShutdown<T>::value>::type CallShutdown(T *instance) {(void)instance;
}

DEFINE_TYPE_TRAIT(HasShutdown, Shutdown)

#define DEFINE_TYPE_TRAIT(name, func)                     \template <typename T>                                   \struct name {                                           \template <typename Class>                             \static constexpr bool Test(decltype(&Class::func)*) { \return true;                                        \}                                                     \template <typename>                                   \static constexpr bool Test(...) {                     \return false;                                       \}                                                     \\static constexpr bool value = Test<T>(nullptr);       \};                                                      \\template <typename T>                                   \constexpr bool name<T>::value;

DEFINE_TYPE_TRAIT(HasShutdown, Shutdown) 的具体含义是:创建类型萃取模板类 HasShutdown,HasShutdown 可检查模板类型参数 T 中是否包含 Shutdown 方法。若是,则执行下面语句版本的 CallShutdown 会被 CleanUp 调用:

instance->Shutdown();

否则,执行下面语句版本的 CallShutdown 会被 CleanUp 调用:

(void)instance;
7.2 封装与验证

singleton.h

#ifndef SINGLETON_SINGLETON_H
#define SINGLETON_SINGLETON_H#include <iostream>
#include <memory>
#include <mutex>
#include <type_traits>
#include <utility>#define DEFINE_TYPE_TRAIT(name, func)                     \template <typename T>                                   \struct name {                                           \template <typename Class>                             \static constexpr bool Test(decltype(&Class::func)*) { \return true;                                        \}                                                     \template <typename>                                   \static constexpr bool Test(...) {                     \return false;                                       \}                                                     \\static constexpr bool value = Test<T>(nullptr);       \};                                                      \\template <typename T>                                   \constexpr bool name<T>::value;DEFINE_TYPE_TRAIT(HasShutdown, Shutdown)template <typename T>
typename std::enable_if<HasShutdown<T>::value>::type CallShutdown(T *instance) {instance->Shutdown();
}template <typename T>
typename std::enable_if<!HasShutdown<T>::value>::type CallShutdown(T *instance) {(void)instance;
}// There must be many copy-paste versions of these macros which are same
// things, undefine them to avoid conflict.
#undef UNUSED
#undef DISALLOW_COPY_AND_ASSIGN#define UNUSED(param) (void)param#define DISALLOW_COPY_AND_ASSIGN(classname) \classname(const classname &) = delete;    \classname &operator=(const classname &) = delete;#define DECLARE_SINGLETON(classname)                                      \public:                                                                  \static classname *Instance(bool create_if_needed = true) {              \static classname *instance = nullptr;                                 \if (!instance && create_if_needed) {                                  \static std::once_flag flag;                                         \std::call_once(flag,                                                \[&] { instance = new (std::nothrow) classname(); }); \}                                                                     \return instance;                                                      \}                                                                       \\static void CleanUp() {                                                 \auto instance = Instance(false);                                      \if (instance != nullptr) {                                            \CallShutdown(instance);                                             \}                                                                     \}                                                                       \\private:                                                                 \classname();                                                            \DISALLOW_COPY_AND_ASSIGN(classname)#endif //SINGLETON_SINGLETON_H

singleton_a.h

#ifndef SINGLETON_SINGLETON_A_H
#define SINGLETON_SINGLETON_A_H
#include <iostream>
#include "singleton.h"
class SingletonA
{
private:~SingletonA() = default;private:static int num;public:static void GetNum(){std::cout << "\n number of instances of SingletonA: " << num << std::endl;}DECLARE_SINGLETON(SingletonA)
};int SingletonA::num = 0;SingletonA::SingletonA()
{++num;
}
#endif //SINGLETON_SINGLETON_A_H

singleton_b.h

#ifndef SINGLETON_SINGLETON_B_H
#define SINGLETON_SINGLETON_B_H
#include <iostream>
#include "singleton.h"
class SingletonB
{
private:~SingletonB() = default;private:static int num;public:// `Shutdown` method should be declared as `public` for type traitsvoid Shutdown();static void GetNum(){std::cout << "\n number of instances of SingletonB: " << num << std::endl;}DECLARE_SINGLETON(SingletonB)
};int SingletonB::num = 0;SingletonB::SingletonB()
{++num;
}void SingletonB::Shutdown()
{auto instance = Instance(false);if (instance != nullptr){delete instance;num = 0;}std::cout << "\n SingletonB::Shutdown method was called." << std::endl;
}#endif //SINGLETON_SINGLETON_B_H

main.cpp

#include <thread>
#include "singleton_b.h"
#include "singleton_a.h"template <typename T>
void ThreadFunc()
{std::this_thread::sleep_for(std::chrono::milliseconds(1000));T *p = T::Instance();
}int main()
{std::thread tA1(ThreadFunc<SingletonA>);std::thread tA2(ThreadFunc<SingletonA>);std::thread tB1(ThreadFunc<SingletonB>);std::thread tB2(ThreadFunc<SingletonB>);tA1.join();tA2.join();tB1.join();tB2.join();SingletonA::GetNum();SingletonB::GetNum();SingletonA::CleanUp();SingletonB::CleanUp();SingletonA::GetNum();SingletonB::GetNum();return 0;
}

CMakeList.txt

cmake_minimum_required(VERSION 3.25)
project(singleton)set(CMAKE_CXX_STANDARD 17)add_executable(singleton main.cpp singleton.h singleton_a.h singleton_b.h)

运行结果:

 number of instances of SingletonA: 1number of instances of SingletonB: 1SingletonB::Shutdown method was called.number of instances of SingletonA: 1number of instances of SingletonB: 0
  • 在调用 CleanUp 方法前,虽然 SingletonA 和 SingletonB 各自被两个线程调用 Instance 方法,但默认构造均只发生了一次(实例数量均为 1),说明满足多线程安全性;
  • 分别调用 SingletonA 和 SingletonB 的 CleanUp 方法后,SingletonB 的实例数量清零,因为其 Shutdown 方法被间接调用;SingletonA 实例数量仍为 1,因为其 CleanUp 方法什么也没做。

7 单例模式总结

  • 懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题
  • 饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

如果在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;
如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题

在这里插入图片描述
Meyers 单例不仅形式优雅,效率在多线程场景下也是最优的

class Singleton {public:static Singleton& getInstance() {static Singleton instance_;return instance_;	}private:Singleton() {};~Singleton() {};Singleton(const Singleton&);Singleton& operator=(const Singleton&);	
};

至此,单例模式懒汉式和饿汉式讲解到此结束,不正之处望读者指正。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python时序预测系列】基于时域卷积网络TCN实现单变量时间序列预测(源码)

这是我的第248篇原创文章。 一、引言 TCN&#xff08;Temporal Convolutional Networks&#xff09;是一种用于时间序列数据建模的深度学习架构。与传统的循环神经网络&#xff08;RNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;不同&#xff0c;TCN利用卷积操…

java特殊文件——properties属性文件概述

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!! properties properties是一个Map集合&#xff08;键值对合集&#xff09;&#xff0c;但是一般不当作合集。而是用来代表属性文件&#xff0c;通过Properties读写属性文件里的内容 Properties调用方…

MySQL为什么会选错索引

在平时不知道一有没有遇到过这种情况&#xff0c;我明明创建了索引&#xff0c;但是MySQL为何不用索引呢&#xff1f;为何要进行全索引扫描呢&#xff1f; 一、对索引进行函数操作 假设现在维护了一个交易系统&#xff0c;其中交易记录表 tradelog 包含交易流水号(tradeid)、交…

[STM32] Keil 创建 HAL 库的工程模板

Keil 创建 HAL 库的工程模板 跟着100ASK_STM32F103_MINI用户手册V1.1.pdf的第7章步骤进行Keil工程的创建。 文章目录 1 创建相关文件夹2 创建“main.c/h”和“stm32f1xx_clk.c/h”3 复制CMSIS和HAL库4 创建新的Keil工程5 添加组文件夹和工程文件6 配置Keil设置 1 创建相关文件…

[激光原理与应用-77]:基于激光器加工板卡的二次开发软件的系统软硬件架构

目录 一、1个板卡、1个激光器、1个振镜的应用架构、1个工位 &#xff08;1&#xff09;PLC &#xff08;2&#xff09;MES &#xff08;3&#xff09;加工板卡 &#xff08;4&#xff09;激光加工板卡与激光器之间的转接卡 &#xff08;5&#xff09;DB25、DB15 &#x…

基于51单片机的厨房一氧化碳温湿度烟雾粉尘监测报警Proteus仿真

地址&#xff1a;https://pan.baidu.com/s/19tp61m5fOORP47RNh8TWGA 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectroni…

桶排序:原理、实现与应用

桶排序&#xff1a;原理、实现与应用 一、桶排序的基本原理二、桶排序的实现步骤三、桶排序的伪代码实现四、桶排序的C语言实现示例 在日常生活和工作中&#xff0c;排序是一个经常遇到的需求。无论是对一堆杂乱的文件进行整理&#xff0c;还是对一系列数据进行统计分析&#x…

基于深度学习的心律异常分类算法

基于深度学习的心律异常分类系统——算法设计 第一章 研究背景算法流程本文研究内容 第二章 心电信号分类理论基础心电信号产生机理MIT-BIH 心律失常数据库 第三章 心电信号预处理心电信号噪声来源与特点基线漂移工频干扰肌电干扰 心电信号读取与加噪基于小波阈值去噪技术的应用…

Wi-Fi 标准的演进

在数字时代的今天&#xff0c;Wi-Fi已经成为了我们生活中不可或缺的一部分&#xff0c;但这一无线通信技术的演进却是一个精彩而丰富的历程。从最初迈出的第一步&#xff0c;到如今的Wi-Fi 7高速数据传输&#xff0c;每一个Wi-Fi标准的诞生都伴随着无数创新和技术的突破。 802.…

《科学技术创新》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答&#xff1a; 问&#xff1a;《科学技术创新》期刊是哪个级别&#xff1f; 答&#xff1a;省级 主管单位&#xff1a;黑龙江省科学技术协会 主办单位&#xff1a;黑龙江省科普事业中心 问&#xff1a;《科学技术创新》期刊影响因子&#xff1f; 答&#xff1a;(2…

Mysql数据库:高级SQL语言详解

目录 前言 一、按关键字排序查询 1、单字段排序 1.1 按某一字段升序排序 1.2 按某一字段降序排序 1.3 结合where进行条件进行排序 2、多字段排序 2.1 按多字段升序排序 2.2 按多字段降序排序 2.3 案例操作 3、区间判断及查询不重复记录 3.1 区间判断 3.1.1 AND/OR…

Linux——磁盘与文件系统管理

目录 磁盘分区的表示 硬盘分区 分区类型 确认系统中的磁盘设备——fdisk 规划硬盘中的分区——fdisk 文件系统 文件系统类型&#xff1a; 在分区中创建文件系统——mkfs&#xff0c;mkswap 挂载文件系统 mount命令 umount命令 查看分区挂载情况 设置启动载入&…