zl程序教程

您现在的位置是:首页 >  其他

当前栏目

[译] 研发者必知的10个操作系统的概念

2023-02-26 09:48:34 时间

大家好,我是渔夫子。今天跟大家分享一篇操作系统相关的文章,该篇文章在medium中具有高达7.7K的点赞数,所以肯定还是值得一读的,文末附原文链接。

你会用二进制吗?你能解释机器码吗?如果给你一个全是0和1的表格,你能告诉我它代表什么意思吗?如果你去了一个你从未去过的国家,你从来没有听过该国的语言,也许你听过该国的语言但你从来没有说过该国的语言,那么要和本地人交流你需要做什么呢?

你需要一个翻译器。你的操作系统就充当了你PC机的翻译器功能。它将1和0、是或否,开或关这样的值转换成你能理解的可读语言。它在一个流线型的图形用户界面(GUI)中完成了所有这些,你可以用鼠标点击东西,移动它们,看到它们在你眼前发生。

了解操作系统的工作原理对于软件开发人员来说都是基础和关键。我们不应该试图绕开操作系统的知识,任何告诉你没必要的人都应该忽略。虽然知识的广度和深度可能会受到质疑,但了解基础知识之外的知识对于程序运行的好坏甚至其结构和流程至关重要。

为什么呢?当你编写一个程序并且运行的较慢,但从代码中又找不到任何问题时,那么你还会从哪里去寻找解决方案。如果你不了解操作系统是如何工作的,你又是如何调试问题的?你访问的文件太多了?内存不足,swap占用率高?但你甚至不知道什么是swap!或者是因为I/O阻塞?

下面将介绍操作系统中的10个关键的概念,如果你想成为一个更好的软件研发者,你必须要学会这些概念。

首先,我们定义操作系统是什么。操作系统(OS)是一个管理计算机硬件并提供驱动服务的软件集合。具体来说,它隐藏了硬件的复杂性,管理计算资源,并提供隔离和保护。最重要的是,它具有直接访问底层硬件的权限。文件系统、调度器和设备驱动器是操作系统的主要组成部分。

在一个操作系统中有3个关键的元素:抽象(进程,线程,文件,套接字和内存)、机制(create、schedule、open、write、allocate)和策略(LRU,EDF)。

对于操作系统有2个设计原则:(1)机制和策略分离,以使用灵活的机制来支持策略(2)针对各种情况进行优化:操作系统将在哪里使用?用户想在那台机器上执行什么?负载要求是什么?

概念一:进程和进程管理

一个进程就是一个正在执行的程序。一个进程的执行必须按顺序的方式进行处理。简单地说,我们把计算机程序写在一个文本文件中,当我们执行这个程序时,它变成了一个执行程序中提到的所有任务的过程。当一个程序被加载到内存中并成为一个进程时,它可以分为四个部分——堆栈、堆、代码和数据。下图显示了主内存中进程的简化布局:

  • 栈(Stack):进程栈空间包含的是临时数据,像方法或函数参数,返回地址以及本地变量等。
  • 堆(Heap):进程在运行过程中动态分配的内存。
  • 代码:可执行的程序代码,这包括由程序计数器的值和处理器寄存器的内容表示的当前活动,即当前应该执行的代码指令。
  • Data:数据段包含全局变量和静态变量。

当一个进程执行时,它会在不同的状态间进行转换。在不同的操作系统中,这些转换状态可能不同,同时进程的状态名称也不是标准的。通常来说,一个进程在同一时间会是下面5种状态中的一种:

  • 开始状态:当一个进程被启动或创建时的初始状态
  • 就绪状态:该进程正在等待被分配给一个处理器。就绪状态的进程必须等待操作系统分配处理器以便能够执行。进程可以从开始状态转换到就绪状态,或者由于CPU被分配给其他进程而从运行状态被中断后转换到就绪状态。
  • 运行状态:一旦通过OS的调度给进程分配了一个处理器,该进程就变成了正在运行的状态,同时处理器就会执行该进程的各种指令。
  • 等待状态:如果进程需要等待另外一个资源,就会转为等待状态。例如,等待用户输入,或者等待一个文件变为可用等。
  • 终止或退出状态:一旦进程完成运行,或被操作系统终止,就会从转为终止状态以便从内存中移除。

操作系统通过一个叫做进程控制块(Process Control Block,简称PCB)的数据结构来管理每一个进程。每个PCB都有一个唯一的数字标识,叫做进程ID(PID)。一个PCB包含以下信息以便跟踪一个进程:

  • 进程状态:进程的当前状态,例如进程是否是就绪状态、运行状态、等待状态或其他状态
  • 进程权限:这个是对于是否允许访问系统资源所必需的字段
  • 进程ID:在操作系统中,每个进程唯一的标识
  • Pointer:指向父进程的指针
  • Program Counter:该字段是一个指针,指向该进程下一个要执行的指令的地址
  • CPU Registers:各种CPU寄存器,进程需要在其中存储以执行运行状态。
  • CPU Scheduling Information(CPU调度信息):进程优先级和其他调取该进程的调度信息
  • 内存管理信息(Memory Management Information):该项包含页表信息,内存限制,内存分段表信息。
  • 账号信息(Accounting Information):包含用于进程执行的CPU数量,时间限制和执行的ID信息等
  • IO Status Information:包含分配给该进程的I/O设备列表

概念二:线程和并发

线程是通过进程的代码执行的流程,它有自己的用于跟踪下一步要执行的指令程序计数器,,用于保存当前工作变量的系统寄存器,包含执行历史的堆栈。

同一个进程下可以有多个线程,线程之间共享一些像代码段,数据段和打开的文件信息等。当一个线程将一个代码段的内存项更改了之后,其他的线程也会看到这个改变。

线程也被称为轻量级的进程。线程通过并行的方法提供了一种提高进程性能的方式。线程是一种通过减少开销来提高操作系统性能的软件方法,线程相当于一个经典进程。

每个线程附属于一个进程,进程之外不能存在任何线程。每个线程代表了一个独立的控制流程。线程被成功的应用于网络服务和web服务。它们还提供了应用程序在共享内存的多处理器上并行执行的环境。

线程的优点:

  • 线程可以最小化上下文切换时间
  • 通过多线程的使用提供了一个进程内并发执行的可能。
  • 提高了通信的效率
  • 线程的创建和上下文切换比进程更轻量、消耗CPU更低
  • 线程允许更大规模、更高效地利用多处理器体系结构

有以下2种方式实现线程:

  • 用户态线程:用户状态管理线程
  • 内核态线程:操作系统在内核中管理线程
  • 用户态线程

在这种情况下,线程管理内核不知道线程的存在。线程库(thread library)包含用于创建和销毁线程、在线程之间传递消息和数据、调度线程执行以及保存和恢复线程上下文的代码。应用程序从一个线程开始。

用户态线程的优点:

  • 线程的切换不需要内核模式权限
  • 用户态线程能在任何操作系统中运行
  • 用户模式下的线程是由特定的调度程序进行调度的
  • 用户模式下的线程的创建和管理是非常快的

用户态线程的缺点:

  • 在通常的操作系统下,很多系统调用是要被阻塞的
  • 多线程应用程序不能利用多处理的优势
  • 内核态线程

在这种情况下,线程管理由内核完成。应用程序中没有线程管理代码。内核线程由操作系统直接支持。任何应用程序都可以编程为多线程。

内核维护整个进程以及进程中各个线程的上下文信息。内核的调度是在线程的基础上完成的。内核在内核空间中执行线程创建、调度和管理。内核线程的创建和管理通常比用户线程慢。

优点:

  • 内核可以在多个进程上同时调度来自同一进程的多个线程。
  • 如果进程中的一个线程被阻塞,内核可以调度同一进程的另一个线程
  • 内核例程本身可以是多线程的

缺点:

  • 内核线程的创建和管理通常比用户线程慢
  • 在同一进程中,控制权从一个线程转移到另一个线程需要切换到内核的模式

概念三:调度

进程调度是进程管理器的活动,它处理从 CPU 中移除正在运行的进程并根据特定策略选择另一个进程。

进程调度是多任务操作系统的重要组成部分。这样的操作系统允许一次将多个进程加载到可执行存储器中,并且加载的进程使用时分多路复用来共享CPU。

操作系统在进程调度队列中维护所有进程控制块 (PCB)。操作系统为每个进程状态维护一个单独的队列,所有处于相同执行状态的进程的 PCB 都放在同一个队列中。当一个进程的状态改变时,它的 PCB 会从它当前的队列中断开并移动到它的新状态队列中。

操作系统维护以下重要的进程调度队列:

  • 作业队列(Job queue) - 此队列中维护的是系统中的所有进程。
  • 就绪队列(Ready queue) - 此队列保存的是一组驻留在主内存中的所有就绪状态和等待状态的进程。一个新进程总是放在这个队列中。
  • 设备队列(Device queue) - 由于 I/O 设备不可用而被阻塞的进程被保存在该队列

操作系统可以使用不同的策略来管理每个队列(FIFO、循环、优先级等)。操作系统调度程序确定如何在就绪队列和运行队列之间移动进程,这些队列在系统的每个处理器核心只能有一个进程在CPU上执行;在上图中,它已与 CPU 合并。

进程的两态模式:运行和非运行状态:

  • 运行状态:创建新进程时,它会以运行状态进入系统
  • 非运行状态:未运行的进程保留在队列中,等待轮到它们执行。队列中的每个条目都是指向特定进程的指针。队列是使用链表实现的。调度程序的使用如下:当一个进程被中断时,该进程被转移到等待队列中。如果该进程已完成或中止,则该进程被丢弃。在任何一种情况下,调度程序都会从队列中选择一个进程来执行。

上下文切换是在进程控制块中存储和恢复 CPU 的状态或上下文的机制,以便以后可以从同一点恢复进程执行。使用这种技术,上下文切换器使多个进程能够共享一个 CPU。上下文切换是多任务操作系统功能的重要组成部分。

当调度程序将 CPU 从执行一个进程切换到执行另一个进程时,当前运行进程的状态被存储到进程控制块中。之后,从自己的PCB中加载下一次要运行的进程的状态,用于设置程序计数器(PC)、寄存器等。此时,第二个进程可以开始执行。

上下文切换是计算密集型的,因为必须保存和恢复寄存器和内存状态。为了避免上下文切换时间,一些硬件系统采用两组或更多组处理器寄存器。当进程切换时,会存储以下信息以供以后使用:程序计数器、调度信息、基址和限制寄存器值、当前使用的寄存器、已更改状态、I/O 状态信息和账号信息。

概念四:内存管理

内存管理是操作系统的处理和管理主内存的功能,并在进程执行期间在主内存和磁盘之间来回移动进程。内存管理跟踪每个内存位置,无论它是分配给某个进程还是空闲的。它会检查要分配给进程多少内存。它决定了哪个进程在什么时候获得内存。每当某些内存被释放或未分配时,它都会跟踪并相应地更新状态。

进程地址空间是进程在其代码中引用的一组逻辑地址。例如,当使用 32 位寻址时,地址范围可以从 0 到 0x7fffffff;也就是说,2³¹ 个可能的数字,总理论大小为 2 GB。

操作系统在为程序分配内存时负责将逻辑地址映射到物理地址。在分配内存之前和之后,程序中使用了三种类型的地址:

  • 符号地址:源代码中使用的地址。变量名、常量和指令标签是符号地址空间的基本元素
  • 相对地址:在编译时,编译器将符号地址转换为相对地址
  • 物理地址:加载程序在程序加载到主存时生成这些地址

虚拟地址和物理地址在编译时和加载时地址绑定模式是相同的。虚拟地址和物理地址在执行时地址绑定模式是不同的。

由程序生成的所有逻辑地址的集合称为逻辑地址空间。这些逻辑地址对应的所有物理地址的集合称为物理地址空间。

概念五:进程间通信

一个进程可以有两种类型:独立进程和合作进程。一个独立的进程不受其他进程执行的影响,而一个协作的进程可以受到其他正在执行的进程的影响。尽管人们可以认为那些独立运行的进程将非常有效地执行,但实际上,在许多情况下,可以利用协作性质来提高计算速度、便利性和模块化。进程间通信(IPC)是一种允许进程相互通信并同步它们的动作的机制。这些进程之间的通信可以看作是它们之间的一种合作方式。进程可以使用以下两种方式相互通信:共享内存和消息解析。

5.1 共享内存方式

有两个进程:生产者和消费者。生产者往该区域存入信息,消费者消费该信息。这两个进程共享一个被称为缓冲区的公共空间或内存位置,生产者将信息存储在该缓冲区中,消费者在需要时从该缓冲区中消费消息。这个问题有两个版本:第一个被称为无界缓冲区问题,其中生产者可以一直往该缓冲区存储信息并且缓冲区大小没有限制,第二个被称为有界缓冲区问题,其中生产者最多可以存储一定数量的信息,然后它开始等待消费者消费它。

在有界缓冲区问题中:首先,生产者和消费者会共享一些公共内存,然后生产者将开始往里存储信息。如果存储的信息总数等于缓冲区的大小,生产者将等待消费者消费它。同样,消费者首先检查该缓冲区的数据的可用性,如果没有可用的数据,消费者将等待生产者生产它。如果有可用的信息,消费者将消费它。

5.2 消息协议方法

在这种方法中,进程相互通信而不使用任何类型的共享内存。如果两个进程 p1 和 p2 想要相互通信,它们进行如下操作:

  • 建立通信链路(如果链路已经存在,则无需重新建立。)
  • 使用基本原语开始交换消息。我们至少需要两个原语:发送(消息,目的地)或发送(消息)和接收(消息,主机)或接收(消息)

消息大小可以是固定大小或可变大小。如果它是固定大小的,那么对于 OS 设计者来说很容易,但对于程序员来说很复杂;如果它是可变大小的,那么对于程序员来说很容易,但对于 OS 设计者来说很复杂。标准消息可以有两个部分:消息头和消息体。

消息头用于存储消息类型、目的地、来源、消息长度和控制信息。控制信息包含诸如缓冲区空间用完时如何处理、序列号、优先级等信息。通常,使用 FIFO的方式发送消息。

概念六:I/O 管理

操作系统的一项重要工作是管理各种 I/O 设备,包括鼠标、键盘、触摸板、磁盘驱动器、显示适配器、USB 设备、位图屏幕、LED、模数转换器、On/ 关闭开关、网络连接、音频 I/O、打印机等。

I/O 系统需要接收应用程序 I/O 请求并将其发送到物理设备,然后接收从设备返回的任何响应并将其发送到应用程序。I/O 设备可分为两类:

  • 块设备(Block devices) — 块设备是驱动程序通过发送整个数据块与之通信的设备。例如,硬盘、USB 相机等。
  • 字符设备(Character Devices) — 字符设备是驱动程序通过发送和接收单个字符(字节、八位字节)与之通信的设备。比如串口、并口、声卡等。

CPU 必须有办法将信息传入和传出 I/O 设备。可通过三种方式与 CPU 和设备通信:

  • 专门指令的I/O

这使用专门用于控制 I/O 设备的 CPU 指令。这些指令通常允许将数据发送到 I/O 设备或从 I/O 设备读取。

  • 内存映射I/O

使用内存映射 I/O 时,内存和 I/O 设备共享相同的地址空间。该设备直接连接到某些主内存位置,因此 I/O 设备可以在不通过 CPU 的情况下将数据块传输到内存或从内存传输数据块。

在使用内存映射 IO 时,操作系统会在内存中分配缓冲区并通知 I/O 设备使用该缓冲区向 CPU 发送数据。I/O 设备与 CPU 异步操作,完成后中断 CPU。

这种方法的优点是每条可以访问内存的指令都可以用来操作 I/O 设备。内存映射 IO 用于大多数高速 I/O 设备,如磁盘、通信接口。

  • 直接内存访问(DMA)

像键盘这样的慢速设备在传输每个字节后都会对主 CPU 产生一个中断。如果诸如磁盘之类的快速设备为每个字节生成一个中断,则操作系统将花费大部分时间来处理这些中断。因此,典型的计算机使用直接内存访问 (DMA) 硬件来减少这种开销。

直接内存访问 (DMA) 是指 CPU 授予 I/O 模块在自己不参与的情况下读取或写入内存的权限。DMA 模块本身控制主存储器和 I/O 设备之间的数据交换。CPU 仅在传输的开始和结束时参与,并且仅在传输整个块后才中断。

直接内存访问需要一个称为 DMA 控制器 (DMAC) 的特殊硬件来管理数据传输并仲裁对系统总线的访问。控制器使用源和目标指针(读取/写入数据的位置)、用于跟踪传输字节数的计数器和设置(包括 I/O 和内存类型、中断和 CPU 周期的状态)进行编程。

概念七:虚拟化

虚拟化是一种允许您从单个物理硬件系统创建多个模拟环境或专用资源的技术。称为管理程序的软件直接连接到该硬件,并允许您将 1 个系统拆分为称为虚拟机 (VM) 的独立、不同且安全的环境。这些虚拟机依赖于管理程序将机器资源与硬件分离并适当分配它们的能力。

配备虚拟机管理程序的原始物理机器称为主机,而使用其资源的许多 VM 称为来宾(guest)。这些来宾(guest)将计算资源(如 CPU、内存和存储)视为可以轻松重新定位的资源库。操作员可以控制 CPU、内存、存储和其他资源的虚拟实例,因此客人(guest)可以在需要时获得所需的资源。

理想情况下,所有相关的虚拟机都通过一个基于 Web 的虚拟化管理控制台进行管理,从而加快处理速度。虚拟化让您可以决定为 VM 提供多少处理能力、存储和内存,并且环境得到更好的保护,因为 VM 与其支持硬件以及彼此分离。简而言之,虚拟化从未充分利用的硬件中创建您需要的环境和资源。

虚拟化的类型:

1、数据虚拟化:遍布各处的数据可以整合到一个单一来源中。数据虚拟化允许公司将数据视为动态供应——提供的处理能力可以汇集来自多个来源的数据,轻松容纳新的数据源,并根据用户需求转换数据。数据虚拟化工具位于多个数据源的前面,允许将它们视为单一源,在正确的时间以所需的形式向任何应用程序或用户提供所需的数据。

2、桌面虚拟化:容易与操作系统虚拟化混淆——它允许您在一台机器上部署多个操作系统——桌面虚拟化允许中央管理员(或自动化管理工具)一次将模拟桌面环境部署到数百台物理机器上。与在每台机器上物理安装、配置和更新的传统桌面环境不同,桌面虚拟化允许管理员对所有虚拟桌面执行大规模配置、更新和安全检查。

3、服务器虚拟化:服务器是旨在很好地处理大量特定任务的计算机,因此其他计算机(如笔记本电脑和台式机)可以执行各种其他任务。虚拟化服务器使其能够执行更多特定功能,并涉及对其进行分区,以便组件可用于提供多种功能

4、操作系统虚拟化:操作系统虚拟化发生在内核——操作系统的中央任务管理器。这是并行运行 Linux 和 Windows 环境的有用方法。企业还可以将虚拟操作系统推送到计算机,这:(1) 降低批量硬件成本,因为计算机不需要如此高的开箱即用功能,(2) 提高安全性,因为所有虚拟实例都可以 监控和隔离,以及 (3) 限制在软件更新等 IT 服务上花费的时间。

5、网络功能虚拟化:网络功能虚拟化 (NFV) 将网络的关键功能(如目录服务、文件共享和 IP 配置)分开,以便它们可以分布在不同的环境中。一旦软件功能独立于它们曾经存在的物理机器,特定的功能就可以打包到一个新的网络中并分配给一个环境。虚拟化网络减少了创建多个独立网络所需的物理组件(如交换机、路由器、服务器、电缆和集线器)的数量,它在电信行业特别受欢迎

概念八:分布式文件系统

分布式文件系统是一种基于客户端/服务器的应用程序,它允许客户端访问和处理存储在服务器上的数据,就好像它在他们自己的计算机上一样。当用户访问服务器上的文件时,服务器会向用户发送该文件的副本,该文件在处理数据时缓存在用户的计算机上,然后返回给服务器。

理想情况下,分布式文件系统将各个服务器的文件和目录服务组织到一个全局目录中,这样远程数据访问不是特定于位置的,而是来自任何客户端的相同。全局文件系统的所有用户都可以访问所有文件,并且组织是分层的和基于目录的。

由于多个客户端可能同时访问相同的数据,服务器必须有适当的机制(例如维护有关访问时间的信息)来组织更新,以便客户端始终接收最新版本的数据并且数据冲突 不要出现。分布式文件系统通常使用文件或数据库复制(将数据副本分布在多个服务器上)来防止数据访问失败。

Sun Microsystems 的网络文件系统 (NFS)、Novell NetWare、Microsoft 的分布式文件系统和 IBM 的 DFS 是分布式文件系统的一些示例。

概念九:分布式共享内存

分布式共享内存(DSM)是分布式操作系统的一个资源管理组件,它在没有物理共享内存的分布式系统中实现共享内存模型。共享内存提供了一个虚拟地址空间,在分布式系统中的所有计算机之间共享。

在 DSM 中,从共享空间访问数据的方式类似于访问虚拟内存的方式。数据在辅助存储器和主存储器之间以及不同节点的分布式主存储器之间移动。内存中页面的所有权以某种预定义状态开始,但在正常操作过程中会发生变化。当数据由于特定进程的访问而从一个节点移动到另一个节点时,会发生所有权更改。

分布式共享内存的优势:

  • 隐藏数据移动并为共享数据提供更简单的抽象。程序员不需要像使用消息传递模型时那样担心机器之间的内存传输。
  • 允许通过引用传递复杂结构,简化分布式应用程序的算法开发。
  • 通过移动包含所引用数据的整个页面而不仅仅是数据片段,从而利用“引用位置”。
  • 比多处理器系统更便宜。想法可以使用普通硬件来实现,并且不需要任何复杂的东西来将共享内存连接到处理器。
  • 通过组合所有节点的所有物理内存,程序可以使用更大的内存大小。这种大内存不会像传统分布式系统那样由于交换而导致磁盘延迟。
  • 可以使用无限数量的节点。与通过公共总线访问主存储器的多处理器系统不同,因此限制了多处理器系统的大小。
  • 为共享内存多处理器编写的程序可以在 DSM 系统上运行。

有两种不同的方式可以通知节点谁拥有哪个页面:失效和广播。失效是一种在某些进程请求对该页面的写访问权限并成为其新所有者时使页面失效的方法。这样,下次某个其他进程尝试读取或写入它认为拥有的页面副本时,该页面将不可用,并且该进程将不得不重新请求访问该页面。当进程写入内存页时,广播将自动更新内存页的所有副本。这也称为写更新。这种方法效率低得多,更难实现,因为必须发送一个新值而不是一个失效消息。

概念十:云计算

我们看到越来越多的技术转移到云端。这不仅仅是一种时尚——从传统软件模型到互联网的转变在过去 10 年中稳步增长。展望未来,云计算的下一个十年承诺通过移动设备在任何地方进行协作的新方式。

那么什么是云计算?从本质上讲,云计算是一种计算机程序的外包。使用云计算,用户可以从他们需要的任何地方访问软件和应用程序,而这些软件和应用程序由外部方托管——在“云”中。这意味着他们不必担心存储和电力等事情,他们可以简单地享受最终的结果。

传统的业务应用程序一直非常复杂和昂贵。运行它们所需的硬件和软件的数量和种类令人生畏。您需要整个专家团队来安装、配置、测试、运行、保护和更新它们。当您在数十个或数百个应用程序中增加这项工作时,就很难理解为什么拥有最好的 IT 部门的最大公司没有获得他们需要的应用程序。中小企业没有机会。

借助云计算,您可以消除存储自己的数据所带来的麻烦,因为您无需管理硬件和软件——这成为 Salesforce 和 AWS 等经验丰富的供应商的责任。共享基础设施意味着它像一个实用程序一样工作:您只需为您需要的东西付费,升级是自动的,并且扩展或缩减很容易。

基于云的应用程序可以在几天或几周内启动并运行,而且成本更低。使用云应用程序,您只需打开浏览器、登录、自定义应用程序并开始使用它。企业正在云中运行各种应用程序,例如客户关系管理 (CRM)、人力资源、会计等等。

随着云计算的普及,成千上万的公司只是将其非云产品和服务重新命名为“云计算”。在评估云产品时始终深入挖掘,并记住,如果您必须购买和管理硬件和软件,那么您所看到的并不是真正的云计算,而是假云。

总结

作为一名软件工程师,您将成为更大的计算机科学体系的一部分,其中包括硬件、操作系统、网络、数据管理和挖掘以及许多其他学科。每个学科的工程师对其他学科的了解越多,他们就越能有效地与其他学科互动。由于操作系统是管理输入、处理和输出的“大脑”,因此所有其他学科都与操作系统交互。了解操作系统的工作原理将为了解其他学科的工作方式提供有价值的见解,因为您与这些学科的交互是由操作系统管理的。

原文链接:https://data-notes.co/the-10-operating-system-concepts-software-developers-need-to-remember-480d0734d710


欢迎关注「Go学堂」,让知识活起来