原文

你会说二进制吗?你能理解机器代码吗?如果我给你一张满是 0 和 1 的表,你能告诉我这是 什么意思吗?如果你要去一个从未去过的国家/地区,使用的语言是您从未听说过的,或者也许您听说过但实际上却没有讲过的语言,那么在那里您需要什么来帮助您与当地人沟通?

您需要翻译。您的操作系统的功能在PC中充当的翻译器。它转换这些 0 和 1,是/否,开/关的值成你可以理解的可读的语言。它通过简化的图形用户界面或GUI来完成所有这些工作,您可以通过鼠标单击内容完成四处移动,然后在眼前看到它们的发生。对于认真的软件开发人员而言,了解操作系统的工作原理至关重要。请勿试图绕开它,任何告诉您没有必要的人都应该被忽略。虽然知识的扩展性和深度可能会受到质疑,对基本知识的了解对于您的程序运行状况甚至其结构和流程至关重要。

为什么?当您写一个程序,它运行非常的慢,但是你看到代码没有任何问题,您将在哪里寻找解决方案。如果您不知道操作系统如何工作,你将如何调试您的程序?您是否访问了太多的文件?内容不足和交换被频繁调用?但是你甚至不知道什么是交换!或是否 I/O 阻塞?

你希望和其他机器交互,您如何在本地或通过互联网进行此操作?区别在哪儿?为什么有些程序员更喜欢某个操作系统而不是另一个?

为了成为一个严格的程序员,我最近参加了佐治亚理工学院的课程“操作系统简介”。它讲授了基本的OS抽象,机制以及它们的实现。课程的核心包括并发程序(线程和同步),进程间通信和分布式操作系统简介。这篇文章用来分享课程总结,如果您想精通软件开发,则需要学习的10个关键操作系统概念

首先,我们来定义什么是操作系统。操作系统(OS)是一个软件的集合,这些软件管理计算机硬件并为程序提供服务。具体来说,它隐藏了硬件复杂性,管理计算机资源,并提供了隔离和保护。最重要的是,它有访问底层硬件的权限。操作系统的组件是文件系统,调度和设备驱动。你可能之前已经使用了桌面(Windows,Mac 和 Linux )和嵌入式(Android,iOS)操作系统。

操作系统有3个基本元素:(1) 抽象(进程,线程,文件,套接字,内存),(2) 机制(创建,调度,打开,写入,分配) 和 (3) 策略(LRU,EDF)。

有两个操作系统设计原则:通过实现灵活的机制来支持策略的机制与策略分隔针对常见情况进行优化:操作系统将在哪里使用?用户想要在机器上运行什么?工作量有哪些需求?

当今常用的3种操作系统:(1)单片操作系统,整个操作系统都在内核空间中运行,并且在超级用户模式下单独运行。(2)模块化操作系统:系统核心的某些部分位于被称为模块的独立文件中,可以在运行时将其添加到系统中。(3)微操作系统:将内核分解为单独的进程,称为服务器。一些服务器在内核空间中运行,而一些则运行在用户空间中。

1 - 进程与进程管理

进程就是运行中的程序。进程的执行必须按顺序进行。简单地说,我们将计算机程序写在一个文本文件中,当我们执行该程序时,它成为一个执行程序中提到的所有任务的过程。

当 程序加载到内存中,它成为了进程,它可以被分为四个部分:堆(stack),栈(heap),文本(text)和数据(data)。下面的图展示了内存中主进程的简单分层。

process-four-sections

  • **Stack:**进程的堆包含临时数据,例如方法/函数参数,返回地址和局部变量。
  • **Heap:**进程运行时动态分配的内存。
  • Text: 它包括由程序计数器的值和处理器的寄存器的内容表示的当前活动。
  • Data: 这个部分包括管理变量和静态变量。

当进程运行时,它会经历不同的状态,这些状态在不同的操作系统上可能会不同,这些状态的名字也不是标准的。一般来说,进程一次可以有下面五个状态中的一个。

process-five-stages-table

  • Start: 这是当程序首次启动/创建的初始状态。
  • Ready: 进程等待被指定给处理器。准备就绪的进程下在等待被操作系统分配给处理器,以便它们可以 运行。进程在Start状态后可能进入此状态或者被调度程序中断以将CPU分配给其他进程。
  • Running: 一旦进程被操作系统的调度器分配给一个处理器,进程状态被设置为运行状态,处理器执行其指令。
  • Waiting: 当进程需要等待一个资源时它进入等待状态,例如等待用户输入,或等待一个文件可用。
  • **中止或退出:**一旦进程完成了它的执行,或它被操作系统中止,它被移到终止状态,等待从主存储器中删除。

进程控制块(PCB: process control block)是一个被操作系统为每个进程维护的数据结构。PCB 由整数进程 ID(PID)标识。PCB保留跟踪进程的所有信息,如下所示:

![pcb-keep-process-all-info](https://miro.medium.com/max/546/1*iRRLvW9or49SYRAm9HvR0Q.jpeg

  • Process State: 进程的当前状态,即它是否准备就绪,运行,等待及其他。
  • Process Privileges: 允许/禁止访问系统资源是必须的。
  • **Process ID:**操作系统中每个进程的唯一标识符。
  • Pointer: 指向父进程的指针。
  • Program Counter: 程序计算器是一个指针,它指向被该进程执行的下一个指令。
  • CPU Registers: 各种CPU寄存器,需要在其中存储进程以执行运行状态。
  • CPU Scheduling Information: 调度该进程所需要的进程优先级和其他调度信息。
  • Memory Management Information: 它包括页表,内存限制,段表的信息,具体取决于操作系统使用的内存。
  • Accounting Information: 它包括进程执行的CPU数量,时间限制,执行ID等等。
  • **IO Status Information:**这包括分配给该进程的I / O设备的列表。

2 - 线程和并发

线程是通过进程代码执行的流程,它具有自己的程序计数器,该计数器跟踪下一条要执行的指令,系统寄存器将保存其当前的工作变量,以及一个包含执行历史记录的堆栈。

一个线程与其对等线程共享一些信息,例如代码段,数据段和打开文件,当一个线程改变了内存项目的代码段,其他所有的线程都可以看到。

线程也被称为轻量级进程。线程提供了一种通过并行性提高应用程序性能的方法。线程代表一种通过减少开销线程来提高操作系统性能的软件方法,该方法等同于经典过程。

每个线程严格的属于一个进程,并且没有线程能脱离进程存在。每个线程代表了控制流的一部分。线程已经被成功使用在实现网络服务器和 web 服务器中。它们还为共享内存多处理器上并行执行应用程序提供了合适的基础。

single and multi thread-process

线程的优势:

  • 线程可最大程序的减少上下文切换的时间。
  • 线程的使用提供了进程内的并发性。
  • 更有效的沟通。
  • 创建和切换上下文更经济。
  • 线程允许更大满园和更高效地利用多核处理器体系结构。

线程以下列2种方式被实现:

  • **用户级线程:**用户管理线程
  • 内核态线程: 操作系统管理线程使用于内核,操作系统核心。

用户态线程

在这个例子中,线程管理内核并不知道线程的存在。线程库包含用于创建和销毁线程,在线程之间传递消息和数据,调度线程执行以及保存和还原线程上下文的代码。应用程序以一个单一的线程启动。

user-level-thread.png

优势:

  • 线程的切换不需要内核模式特权。
  • 用户态线程可以运行在任何操作系统上。
  • 调度可以由用户态应用程序指定。
  • 用户态线程创建和管理都比较快。

劣势:

  • 在传递的操作系统中,很多系统调用被阻塞。
  • 多线程应用程序无法使用多进程的优势。

内核态线程

在这个例子中,线程的管理由内核完成。在应用程序空间中没有线程管理代码。内核线程直接被操作系统所支持。可以将任何应用程序编程为多线程。在一个进程中支持应用程序内的所有线程。

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

kernel-mode-thread

优势:

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

劣势:

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

3 - 调度

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

进程调度是多程序(Multiprogramming)操作系统的重要部分。这样的操作系统允许一次将一个以上的进程加载到可执行内存中,并且加载的进程使用分时复用来共享CPU。

操作系统在进程调度队列(Process Scheduling Queues)中维护所有的进程控制块(PCBs)。操作系统为每个进程状态维护一个单独的队列,并且处于相同执行状态的所有进程的 PCB 都放置在同一队列中。当进程的状态改变,它的 PCB 从当前的队列中取消链接,并移至新的状态队列。

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

  • Job queue - 这个队列保存系统中所有的进程。
  • Ready queue - 这个队列保存驻留在内存中的所有进程集合,准备就绪状态并等待执行。新的进程总是放在这个队列中。
  • Device queues - 由于设备 I/O 不可用而被阻塞的进程构成此队列。

process-scheduling-queues.png

操作系统可以使用不同的策略来管理每个队列(FIFO,Round Robin,Priority等等)。操作系统调度程序确定如何在就绪队列和运行队列之间移动程序,该队列在系统上每个处理器内核中只能有一个条目;在上面的一表中,它已与CPU合并。

两状态流程模型是的指运行和非运行状态:

  • Running: 当新进程被创建,它将作为运行状态进入系统。
  • Not Running: 未运行的进程被保存在队列,等待被轮到它们被执行。队列中的每个条目是一个指向具体进程的指针。队列通过使用链表实现。调度程序使用如下。当一个进程被中断,这个进程被转移到等待队列中。如果进程已经完成或中止,进程会被丢弃。无论哪种情况,调度器会从队列中选择一个进程执行。

picture-of-context-switch.jpeg

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

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

由于必须保存和恢复寄存器和内存状态,因此上下文切换是计算密集的。为了避免频繁切换上下文,某些硬件系统会使用用两组甚至更多 组处理器寄存器。当切换进程上下文时,下列信息被存储以便以后使用:程序计数器,调度信息,基本和极限寄存器值,当前使用的寄存器,修改状态,I/O状态信息和计数信息。

4 - 内存管理

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

memory-manangent.png

**进程地址空间(process address space)**是进程在其代码中引用的逻辑地址集合。例如,当使用32位寻址时,地址的范围是 0 到 0x7fffffff;它是2的31次方可能的数字,理论总大小为2GB。

操作系统负责在将内存分配给程序时将逻辑地址映射到物理地址。在内存被分配前后,有3种类型的地址被使用:

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

虚拟地址和物理地址在编译期和加载期地址绑定方案是相同的。虚拟地址和物理地址在运行期地址绑定方案不同。

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

5 - 进程间通信

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

共享内存方法

有两个进程:生产者和消费者。生产者生产一些项目,消费者消费这些项目。这两个进程共享一个称为缓冲区的公共空间或存储位置,在该位置存储生产者生产的项目,如果需要,消费者从那里消费该项目。该问题有两个版本:第一个是被称为无边界的缓冲问题,即生产者可以一直生产项目,缓冲区的大小没有限制;第二个是有边界的缓冲区,即生产者可以生产一定数量的项目,此后它会等待消费者来消费它。

shared-memory.png

在有界缓冲问题中,首先,生产者和消费者将共享一些共同的内存,然后生产者将开始生产项目,如果生产项目的总合等于缓冲区的大小,生产者将等待它们被消费者消费。同样地,消费者首先检查项目的可用性,如果没有项目可用,消费者将等待生产者生产项目。如果有项目,生产者将消费它。

消费解析方法

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

  • 建立一个通信连接(如果通信边接已经存在,不需要再次创建)。
  • 使用基本的原语交换信息。我们需要至少两个原语:send(消息,目的)或send(消息)和receive(消息,主机)或receive(消息)

big-picture.jpeg

消息的大小可以是固定的,也可以是可变的。如果它是固定大小,则对于操作系统设计人员来说很容易,但对于程序员来说则很复杂。而如果大小是可变的,则对于开发人员来说是容易的,但对于操作系统设计人员则是复杂的。标准的消息有两个部分:头部(header)和正文(body)

header 部分用来存储消息类型,目的id,源id,消息长度和控制信息。控制信息包括如果越界将做什么的信息,序列号,优先级。一般地,消息的发送是FIFO风格。

6 - I/O管理

操作系统的一个重要的工作是管理多变的 I/O 设备,包括鼠标,键盘,触摸板,显示驱动,显示适配,USB设备,位图(Bit-mapped)屏幕,LED,模数转换器,开关(On/off switch),网络连接,音频I/O,打印机等等。

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

  • 块设备 - 块设备是驱动程序通过发送整个数据块与之通信的设备。例如硬盘,USB摄像机,磁盘密钥等。
  • 字符设备 - 字符设备是驱动程序通过发送和接收单个字符(字节,八位字节)进行通信的设备。例如,串行端口,并行端口,声卡等

![memory-manage-devices.jpeg](https://miro.medium.com/max/1200/1*Ynj0zqoSq5JpbdFfARgNSg.jpeg]

CPU必须具有与I / O设备之间传递信息的方法。有三种方法可用于与CPU和设备通信。

1> 特殊指令I/O

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

2> 内存映射I/O

当使用内存映射I/O,相同的地址空间被内存和I/O设备共享。设备直接连接到确定的主内存位置,因此I / O设备可以在不经过CPU的情况下向/从内存传输数据块。

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

io-command.jpeg

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

3> 直接内存访问(Direct memory access - DMA)

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

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

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

7 - 虚拟化

虚拟化是一项可让您从单个物理硬件系统创建多个模拟环境或专用资源的技术。称为虚拟机监控程序(hypervisor)的软件直接连接到该硬件,并允许您将1个系统拆分为单独的,独立的安全环境,称为虚拟机(VM)。这些VM依靠虚拟机监控程序的能力将计算机的资源与硬件分开,并适当地分配它们。

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

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

virtualization.jpeg

虚拟化类型:

  1. 数据虚拟化: 遍布各地的数据可以合并为一个来源。数据虚拟化允许公司将数据视为动态供应 – 提供处理能力,可以将来自多个源的数据汇总在一起,轻松容纳新的数据源,并根据用户需求转换数据。数据虚拟化工具位于多个数据源的前面,并允许将它们视为单个数据源,并在适当的时间以任何形式将所需数据提供给任何应用程序或用户。
  2. 桌面虚拟化: 容易与操作系统虚拟化混淆 – 操作系统虚拟化允许您在一个机器上部署多个操作系统,桌面虚拟化允许中心管理员(或自动管理工具)一次将模拟的桌面环境部署到数百台物理机。不像传统的桌面环境是在每一个机器上物理安装,配置和更新的,桌面虚拟化允许管理员在所有虚拟桌面上执行批量配置,更新和安全检查。
  3. 服务器虚拟化: 服务器是设计为能够很好地处理大量特定任务的计算机,因此其他计算机 – 例如笔记本电脑和台式机 – 可以完成其他各种任务。虚拟化服务器可以使其执行更多特定功能,并需要对其进行分区,以便可以使用组件来提供多种功能。
  4. 操作系统虚拟化: 操作系统虚拟化发生在内核 – 操作每年中内任务管理器。这是并行运行Linux和Windows环境的有用方法。企业还可以将虚拟操作系统推向计算机。它(1)由于计算机不需要如此高的开箱即用功能,因此降低了批量硬件成本,(2)由于可以监视和隔离所有虚拟实例,因此提高了安全性,(3)限制了在软件更新等IT服务上花费的时间。
  5. 网络功能虚拟化: 网络功能虚拟化(NFV)分离了网络的关键功能(如目录服务,文件共享和IP配置),因此它们可以在环境中分布。一旦软件功能独立于它们曾经使用过的物理机器,就可以将特定的功能打包到一个新的网络中并分配给一个环境。虚拟化网络减少了创建多个独立网络所需的物理组件(如交换机,路由器,服务器,电缆和集线器)的数量,并且在电信行业中特别流行。

8 - 分布式文件系统

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

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

distrubuted-file-system.png

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

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

9 - 分布式共享内存

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

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

img

分布式共享内存的优点:

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

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

10-云计算

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

那么什么是云计算?本质上,云计算是计算机程序的一种外包。使用云计算,用户可以在由外部方托管的“云”中随时随地访问软件和应用程序。这意味着他们不必担心存储和电源,他们可以简单地享受最终结果。

传统的业务应用程序一直非常复杂且昂贵。运行它们所需的硬件和软件的数量和种类令人生畏。您需要整个专家团队来安装,配置,测试,运行,保护和更新他们。当你乘跨数十或数百个应用程序的这种努力,是不容易看出为什么最好的IT部门最大的公司都没有得到他们所需要的应用程序。中小型企业没有机会。

借助云计算,您无需管理硬件和软件,从而消除了存储自己的数据所带来的烦恼,这成为了像Salesforce和AWS这样有经验的供应商的责任。共享的基础结构意味着它像公用程序一样工作:您只需为所需的东西付费,升级是自动的,并且放大或缩小都很容易。

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

随着云计算的普及,成千上万的公司只是将其非云产品和服务重新命名为“云计算”。在评估云产品时,请务必深入了解,并牢记如果必须购买和管理硬件和软件,该怎么办?您正在查看的并不是真正的云计算,而是假云。