分布式系统概念和设计-操作系统中的支持和设计

分布式系统概念和设计

操作系统支持

中间件和底层操作系统的关系,操作系统如何满足中间件需求。

中间件需求:访问物理资源的效率和健壮性,多种资源管理策略的灵活性。

任何一个操作系统的目标都是提供一个在物理层(处理器,内存,通信设备和存储介质)之上的面向问题的抽象。

比如:

  • 操作系统提供的套接字形式的接口而不是磁盘块或者原始网络访问
  • 操作系统管理某结点的物力资源并通过系统调用接口抽象表示资源

中间件和网络操作系统:

事实上除了UNIX,MacOS,Windows,Linux这些网络操作系统外,没有普遍应用的分布式操作系统

  1. 用户过于注重满足他们当前需要的应用软件,如果新的高效率的操作系统不能满足这些需求,则不会称为选择。
  2. 用户即使在一个小的单位里也愿意独立的管理自己的机器。重要因素是性能体验。
操作系统层

只有当中间件和操作系统的联合系统运行的性能足够好,用户才会买单。

中间件运行在分布式系统上每个结点的操作系统和硬件涉笔结合的平台上。

运行在结点上的操作系统都有其内核和相关的用户级服务(库,进程,存储,通信的抽象支持),中间件将这些局部的资源联合起来以实现在结点的对象进程之间提供远程调用机制。

在这里插入图片描述

内核和服务器进程用于管理资源和为客户提供资源接口的组件,特点:

  • 封装:必须提供一个有用的服务接口以访问物力资源。提供的操作集必须满足客户需求,隐藏像管理内存和管理实现资源设备的细节。
  • 保护:资源需要被保护防止被非法访问。(文件读写执行权限,设备注册信息的验证)
  • 并发处理:客户应该可以共享和并发的访问资源。资源管理器负责实现并发透明性。

客户访问资源可以通过远程方法调用访问服务器对象或者系统调用内核。

访问一个已封装资源的方式为调用机制,而不用处理内部如何实现。

库,内核,服务器的联合系统可以实现如下任务:

  • 通信:资源管理器在网络上或计算机内接收操作参数并返回结果
  • 调度:当调用一个操作,在内核服务器上调度其操作。

在这里插入图片描述

操作系统的核心组件:

  • 进程管理器:负责进程的创建和操作。一个进程是一个资源管理单位,其中包括一个地址空间和一个或多个线程。
  • 线程管理器:负责线程的创建,同步和调度。线程调度活动和进程相关。
  • 通信管理器:负责同一计算机上不同进程中的线程之间的通信。根据内核是否支持远程通信进行附加支持。
  • 内存管理器:负责管理物理内存和虚拟内存。
  • 监督器:负责处理中断,系统调用陷阱和其它异常,同时控制内存单元和硬件缓存以及处理器和浮点寄存器的操作。在WINDOWS NT中,监督器就是硬件上的抽象层。
保护

文件非法访问:

  • 无权访问文件操作。分布式资源保护子问题需要运用密码学控制。
  • 错误的执行了资源不能提供的操作。
内核和保护

内核是一个计算机程序,特点是时刻保持运行并且对主机的物理资源有完全的访问权限,特别是它可以控制内存管理单元并设置处理器和寄存器。这样保证了,其他程序代码除非通过某种可接受的方式,否则不能访问机器的物理资源。

处理器有对硬件模式的寄存器的设计,用来决定能否执行特权指令。

  • 例如有些寄存器决定内存管理单元当前采用哪一个保护表。
  • 当内核进程执行时,处理器处于管理模式,而内核安排其他进程在用户模式下运行。

内核也建立地址空间来保护自己和其他进程以防止其他异常进程的访问,同时也给正常进程提供他们所需要的虚拟内存。

一个地址空间是若干虚拟内存区域的集合,其中每一个区域都被赋予特定的内存访问权限(读或读写)

进程不能访问其地址空间外的内存。

用户进程或用户级进程:在用户模式下执行并拥有用户级地址空间的进程(相对于内核对地址空间的访问,这些进程有受限的内存访问权力)

当一个进程执行应用程序代码时,在用户级地址空间执行;而当这一进程执行内核代码,在内核地址空间内执行。通过中断或系统调用陷阱(这种资源调用机制由内核管理)。这一进程可以安全地在用户级地址空间和内核地址空间内切换。

一次系统调用陷阱由一个机器级的TRAP指令实现,它将处理器切换为管理模式并将地址空间切换为内核地址空间。当执行TRAP指令时,计算机硬件强制处理器执行内核提供的处理函数以保证没有其他进程获得对硬件的控制。

因为保护机制,系统在执行程序时会导致额外的开销,主要原因是地址空间之间的切换会占用很多的处理器周期,并且系统调用陷阱也比简单的过程和方法调用更耗费处理器资源。

进程和线程

传统的操作系统概念是一个进程执行一个单独的活动,不能胜任分布式系统的原因是进程使相关活动的共享变得困难且开销大。

增强进程的概念使能与多个活动联系起来。

一个进程包括一个执行环境或一个或多个线程。

一个线程是一个活动的操作系统的抽象(操作系统执行线)

  • 操作系统执行线是操作系统的核心设计思路之一,也被称为进程或线程。它的主要目的是将计算机的资源进行有效地分配和利用,以便多个程序可以同时运行,提高计算机的效率和性能。
  • 操作系统执行线的设计思路是在操作系统内部创建一个或多个执行线程,每个线程都可以独立执行一个程序或一组程序。每个线程都拥有自己的执行上下文,包括程序计数器、寄存器和堆栈等,这样可以保证每个线程的执行状态都是独立的。同时,操作系统可以根据不同的需求,对执行线程进行优先级调度,以保证高优先级的任务能够尽快得到处理,提高系统的响应速度。

一个执行环境是一个资源管理单元,也就是进程的线程能访问的由内核管理的本地资源,一个执行环境包括:

  • 一个地址空间
  • 线程同步和通信资源,如信号量和通信接口(套接字)
  • 高级资源,打开文件和窗口、

创建和管理执行环境都需要耗费系统资源,但是多个多个线程可以共享一个执行环境。

可以共享执行环境中的可用资源。一个执行环境表示允许线程在其中执行的保护域。

线程可以按需自动创建和终结。

多线程执行的主要目的是最大化操作间的并发程度,这样可以将计算机和输入输出同时执行,同时也可以支持在多个处理器上并发执行。

当多个并发的客户请求可能降低服务器的速度使其称为瓶颈,多线程尤其明显。

一个执行环境提供一种资源保护以防止外部线程访问,这样的执行环境内的数据和其它资源在默认情况下是不能被其他执行环境中的线程访问的。

某些内核允许可控制共享资源,比如共享一个计算机上不同执行环境间的物理内存。

地址空间

在这里插入图片描述

图是基于页的而不是段的设计。区域和段不同,当区域扩展大小时,区域最终可能重叠。

区域之间留有空隙,用于区域的增长。

这种由若干不相交区域的地址空间表示对UNIX地址空间的一种概括。

UNIX地址空间包含了三个区域:

  • 固定不变的正文区包含程序代码
  • 一个堆,其中一部分由存储在程序的二进制文件中的值初始化,并且这个区域可以向更高的虚拟地址空间扩展
  • 一个栈,能够向更低的虚拟地址空间扩展

一个地址空间是一个进程的虚拟内存的管理单元。

可以拥有一个或多个区域,这些区域被不可访问的虚拟内存分割。

一个区域是一个可以被本地进程的线程访问的连续的虚拟内存,区域之间不重叠。

区域性质:

  • 范围(最低的虚拟内存地址和区域大小)
  • 对本进程的线程的读写执行权限
  • 是否能够向上或向下扩展

影响区域数目的的因素:

  • 系统需要为每一个线程提供一个独立的栈。每个线程分配一个栈使系统能检测栈的溢出并控制栈的增长。未被分配的虚拟内存在这些栈之外,访问这些内存会引发异常(页失配)另一个方法是将栈放在堆的上方,这样会使系统难于检测线程超出其栈的界限这个错误。
  • 在进程之间或进程与内核之间共享内存的需要是导致产生地址空间中额外区域的另一个因素。
  • 一片共享内存区和其他地址空间的一片或多片内存区可能由同一片物理内存区支持。
    • 库:如果将库的代码分别装载在每个使用它的进程的内存中,可能占用相当大的内存。可以将一个库的代码的拷贝映射到需要它的多个进程的地址空间的区域中,达到共享的目的。
    • 内核:通常将内核代码和数据映射到同一个位置的每个地址空间中。每个进程进行系统调用或者异常处理,系统不需要进行地址空间映射的切换。
    • 数据共享和通信:两个进程或进程和内核可能需要共享数据以达到协同工作的目的。相对于消息之间的传递而言,将共享数据映射到同在两个地址空间中的区域可以提高效率。
新进程的创建

进程的创建是一个不可再分的操作。

创建新的执行环境

选择了主机之后,新进程需要一个包含地址空间和初始化信息的执行环境。

定义和初始化新进程地址空间的方法:

  • 当地址空间是一个静态定义的格式采用第一个方法:

    • 地址空间可能包含一个程序正文区,一个堆和栈。地址空间区域根据指定了地址空间区域内容的列表创建,然后地址空间区域由一个可执行文件进行初始化或者用零填满。
  • 根据一个已存在的执行环境来定义地址空间。

    • UNIX的fork语义,新创建的子进程共享父进程的正文区。同时它的堆和栈是父进程的复制。此机制可推广成父进程的每一个区域都可以被子进程继承或者忽略。区域继承可以通过共享父进程的区域实现,也可以通过复制父进程的区域实现。当父进程和子进程共享一片区域时,属于父进程区域的页面帧同时被映射到响应的子进程区域中。

将父进程的区域复制到子进程区域的过程中采用了写时复制的优化机制。

写时复制优化技术:

写时复制(Copy-on-write,简称COW)是一种内存管理技术,它可以在需要修改共享资源时,先将原始数据复制一份,并修改复制后的数据,而不是直接修改原始数据。这种技术可以减少内存拷贝的次数,提高效率,同时避免了并发访问共享资源时的数据竞争问题。

操作系统中的写时复制机制,通常是指在进程创建子进程时,采用写时复制技术来共享父进程的内存空间。当子进程需要修改某个内存页时,先将该页复制一份,并修改复制后的页,而不是直接修改父进程的内存页。这样就可以避免父进程和子进程同时修改同一个内存页的问题,同时节省了内存空间。

具体来说,当父进程创建子进程时,操作系统会将父进程的内存页标记为只读,而不是可写。当子进程需要修改某个内存页时,操作系统会先检查该页是否为只读,如果是,则将该页复制一份,并将复制后的页标记为可写。这样,父进程和子进程就各自拥有一个独立的内存页,互不影响。
在这里插入图片描述

线程

在这里插入图片描述

接收请求和排队请求以等待线程处理

  • 服务器处理客户端多线程请求时通常会使用队列存储请求,以保证请求的执行顺序。这是因为在多线程环境下,不同的请求可能会同时进入服务器端的处理程序,如果不采用队列等数据结构进行管理,可能会导致请求之间的顺序混乱或出现不可预测的结果。因此,使用队列可以有效地控制请求的执行顺序,提高服务器的稳定性和可靠性。
  1. 线程池:服务器可以创建一个线程池,用于管理和分配处理客户端请求的线程。当客户端请求到来时,服务器从线程池中获取一个可用的线程,将请求分配给该线程处理。
  2. 消息队列:服务器可以创建一个消息队列,用于存储客户端请求。当客户端请求到来时,服务器将请求放入消息队列中,然后使用一个或多个线程从队列中取出请求并处理。
  3. 事件驱动:服务器可以使用事件驱动的方式处理客户端请求。当客户端请求到来时,服务器将其转换为一个事件,并将其放入事件队列中。服务器上的一个或多个线程监视事件队列,并在事件到来时执行相应的处理函数。
  4. 信号量:服务器可以使用信号量来控制客户端请求的并发访问。服务器可以设置一个信号量,限制同时处理请求的线程数。当客户端请求到来时,服务器先检查信号量是否可用,如果可用,则将信号量减一,并将请求分配给一个线程处理。当线程处理完请求后,将信号量加一,表示该线程可用。

服务器拥有一个包含一个或多个线程的线程池,其中每一个线程重复地从队列中取出已收到的请求并进行处理。

多线程服务器的体系结构

吞吐量是用每秒处理的请求数度量。

线程体系结构——(工作池体系结构)

  • 服务器创建一个固定的工作线程池处理请求。
  • 接收并排队标记的模块通过由一个I/O线程实现,这个线程从一组套接字或端口中接收请求,并将它们放在共享的请求队列中以便工作线程进行检索。
  • 不同优先级的处理请求,增加优先级的标记处理。工作线程降序扫描队列。
客户线程

web浏览器多线程的客户结构,浏览器必须能并发获取多个网页内容。

Web浏览器多线程的客户端结构可以被分为以下几个主要组件:

  1. 用户界面 - 这是用户与浏览器交互的部分。它包括浏览器窗口、地址栏、导航工具栏、选项卡和书签管理器等。
  2. 渲染引擎 - 渲染引擎负责解析HTML和CSS代码,并将它们转换成可视化的页面。常见的渲染引擎包括WebKit、Gecko和Trident。
  3. JavaScript引擎 - JavaScript引擎负责解释和执行JavaScript代码。常见的JavaScript引擎包括V8、SpiderMonkey和Chakra。
  4. 网络层 - 网络层负责处理网络请求和响应。它包括一个HTTP客户端和一个DNS解析器。
  5. 数据存储 - 数据存储负责存储浏览器的历史记录、Cookie和缓存数据等。
  6. 插件/扩展 - 插件和扩展可以增强浏览器的功能。常见的插件和扩展包括广告拦截器、密码管理器和截图工具等。

这些组件通常运行在不同的线程中,以提高浏览器的性能和响应速度。例如,渲染引擎和JavaScript引擎通常运行在单独的线程中,以避免页面在加载JavaScript代码时出现阻塞。同时,网络请求通常在后台线程中处理,以允许用户在页面加载期间继续进行其他操作。

线程对多进程

线程的使用可以允许计算与输入输出并行,在多处理器的情况下,还可以允许多个计算任务并行执行。

多线程模型的设计:

  • 创建和管理线程的开销比进程少。
  • 线程共享一个执行环境,线程之间比进程之间更容易共享资源。
    在这里插入图片描述

1.确定任务:确定需要执行的任务,将任务分解成多个子任务。

2.确定线程数:根据任务的复杂程度和可用的处理器数量等因素,确定需要启动的线程数。

3.分配任务:将任务分配给不同的线程进行处理,可以使用线程池或者消息队列等方式。

4.同步机制:在多线程处理中,需要使用同步机制来保证线程间的数据安全,可以使用锁、信号量等方式。

5.异常处理:在多线程模型中,由于线程之间的并发执行,可能会出现异常,需要进行异常处理。

6.性能优化:对多线程模型进行性能优化,可以使用线程池、任务队列、缓存等方式。

7.测试和调试:在设计完多线程模型之后,需要进行测试和调试,确保多线程模型的正确性和稳定性。

进程和线程比较:

  • 已知的进程中创建线程的开销更小
  • 进程中不同线程切换开销小
  • 进程中线程共享资源效率高
  • 进程内线程不能防止其他线程的非法访问
  • 线程间切换的优势:在给定处理器上运行的一个新的线程以替换原来运行的线程。切换的开销非常重要,因为在线程的生存期中会经常发生。共享同一个执行环境的线程开销比不同进程之间的开销要低。切换开销主要源于调度(运行下一个将要运行的线程)和上下文切换。

线程的创建和进程的创建

  • 线程:为进程栈分配一个区域并为处理器寄存器和线程的执行状态(初始值可以是挂起或者运行)以及优先级提供的初始值。执行环境存在之后,系统需要在线程的描述符记录中放置执行环境的表示符。
  • 进程:创建一个新的执行环境,其中包括地址空间表。

处理器的上下文包括程序计数器这样的处理器寄存器的值,当前硬件保护域:地址空间和处理器保护模式(管理模式和用户模式,内核态和用户态是操作系统定义。操作系统通过硬件提供的特权级机制来实现内核态和用户态的切换。硬件提供的特权级机制包括CPU提供的指令集和操作系统管理的内存映射等。)

上下文切换是在线程切换时或一个线程进行系统调用或处理其他类型异常时发生上下文切换。

  • 保存处理器寄存器中原始状态并载入最新状态

  • 某些情况下,转换到新的保护域,即域转换。

    1. 线程需要访问受保护的资源,如操作系统的内核态资源,需要进入内核态。
    2. 线程需要执行特权指令,如访问CPU的控制寄存器等。
    3. 线程需要在不同的用户态执行不同的任务,如在用户态执行Java应用程序,然后切换到内核态执行I/O操作。
    4. 线程需要在不同的进程之间进行切换,如进程间通信时,需要将数据传递到另一个进程的地址空间中。

    域转换会带来一定的性能损失,因此应该尽可能地减少域转换的次数。在设计系统时,应该尽量将需要访问受保护资源的代码放在内核态中执行,以避免频繁的域转换。

JMM程序计数器

JMM(Java Memory Model,Java内存模型)程序计数器用于记录当前线程执行的字节码指令的地址,以便线程在下一次执行时能够从上次停止的地方继续执行。程序计数器是线程私有的,每个线程都有自己独立的程序计数器。在Java虚拟机中,程序计数器是一个非常小的内存区域,不会进行垃圾回收。程序计数器也是线程安全的,多个线程同时访问程序计数器不会出现数据竞争问题。

线程编程

竞争条件,临界区,监视器,条件变量,信号量

  1. 竞争条件 (Race condition):是多个线程或进程同时访问共享资源而引起的问题。由于多个线程或进程同时对同一共享资源进行操作,因此结果可能无法预测,甚至可能导致系统崩溃。
  2. 临界区 (Critical section):是指在程序中访问共享资源的代码段,只有一个线程或进程可以进入该代码段,保证了共享资源的安全性。
  3. 监视器 (Monitor):是一种同步机制,用于管理共享资源的访问。它包含了一组临界区、锁定机制和条件变量等,可以确保只有一个线程或进程访问共享资源,并在需要时进行等待和唤醒操作。
  4. 条件变量 (Condition variable):是一种同步机制,用于线程间的通信和协调。当某个线程需要等待某个条件满足时,它可以通过条件变量进行等待,并在条件满足时被唤醒。
  5. 信号量 (Semaphore):是一种同步机制,用于管理共享资源的访问。它可以用来限制同时访问共享资源的线程或进程数量,并在需要时进行等待和唤醒操作。信号量可以是二元信号量(只有0和1两种状态)或计数信号量(可以有多个状态)。
线程生存期

新线程和它的创建者在同一个jvm中,开始处于挂起状态。

执行了start方法后线程处于运行状态,此后,它执行在其构造函数中指定的一个对象的run方法。

JVM和在其上的线程都是操作系统的一个进程中执行。

线程可以被赋予一个优先级,因此,支持优先级的java实现会在优先级线程之前运行高优先级线程。

当线程从run方法返回或其destroy方法被调用,线程的生命周期结束了。

JVM对于线程的处理主要分为两个方面:线程的创建和线程的执行。
线程的创建

当Java程序启动时,JVM会为主线程创建一个Java线程对象,然后通过Java线程对象的start()方法启动主线程。当Java程序需要创建一个新线程时,可以通过以下方式:

(1)继承Thread类,重写run()方法,并创建一个Thread对象。通过调用Thread对象的start()方法启动新线程。

(2)实现Runnable接口,并创建一个Thread对象,将Runnable对象作为Thread构造函数的参数。通过调用Thread对象的start()方法启动新线程。

线程的执行

当线程被启动后,JVM会将线程的代码和数据加载到线程的运行时数据区,然后开始执行线程的代码。线程的执行过程中,JVM会负责线程的上下文切换,即在不同线程之间切换执行。

线程的执行过程中,JVM会为线程分配一个独立的栈空间,并在栈空间中维护线程的运行时数据,包括方法调用栈、局部变量表、操作数栈等。线程执行完一个方法后,JVM会从方法调用栈中弹出该方法,并将控制权交给方法调用栈中的上一个方法。

JVM会根据线程的优先级和调度算法来决定线程的执行顺序,以及如何分配CPU时间片。线程的执行过程中,JVM会监控线程的状态,并在必要时进行调度,以确保线程能够按照预期的方式执行

线程同步

多线程程序设计的困难是共享对象和用于线程协调与合作机制的技术。

每一个线程的方法中局部变量是其私有的(线程有一个私有栈)。

然而,线程没有静态类变量或对象实例变量的私有拷贝。

I/O线程和工作线程在一些服务器线程体系结构中传输请求。

线程并发处理诸如队列这样的数据结构时必然会产生竞争态条件。

java中的监视器

java提供synchronized关键字以便编程人员为线程的协调指定监视器。

可以指定任意代码为某个监视器,监视器可以保证在监视器内任一时刻最多只有一个线程执行,将I/O线程或者工作线程操作串行化。在监视器中所有的访问变量操作都是互斥完成

通过任何用作条件变量的对象,java允许线程被阻塞或者唤醒。

需要阻塞等待某一个条件的线程调用该对象的wait方法。

所有的java对象都实现了这个方法,object对象中的方法,object是基类。

另一个线程调用notify方法为至多一个等待改对象的线程解除阻塞状态,也可以调用notifyAll方法为所有等待该对象的线程解除阻塞状态。

  • 例子:当一个工作线程发现没有可处理的请求,会调用队列类的wait方法。
  • 当I/O线程在队列中加入一个请求是,会调用队列的notify方法唤醒工作线程。

join方法将阻塞其调用者,直到目的线程终止。

interrupt方法用于提前唤醒等待进程。

java的监视器只应用于对象的同步代码。一个类可能会同时包含同步和非同步方法,java对象实现的监视器只包括一个隐式条件变量,通常可以包含多个条件变量

调用Tread.interrupt()方法会将被中断线程的中断标志位设置为true,如果此时线程处于阻塞状态(如调用了sleep()、wait()、join()等方法),会抛出InterruptedException异常,从而打断线程的阻塞状态。

调用Thread.join()方法会使当前线程(一般是主线程)等待被调用线程(即调用join()方法的线程)执行完毕。如果被调用线程已经执行完毕,那么join()方法会立即返回;如果被调用线程还在执行,那么调用线程会一直等待,直到被调用线程执行完毕或者等待被打断(即被调用线程执行interrupt()方法)。

因此,如果在调用Tread.interrupt()之后立即调用Thread.join()方法,如果被调用线程还在执行,那么调用线程会等待被调用线程执行完毕或者等待被打断。如果被调用线程已经被打断,那么调用线程会立即返回。

Thread.interrupt() 方法用于中断正在执行的线程,它会给线程发送一个中断信号,但不会真正停止线程的执行。当线程被中断后,可以通过判断线程的中断状态来决定是否继续执行线程的任务。

Thread.join() 方法可以让当前线程等待指定线程的结束,直到指定线程执行完毕后,当前线程才会继续执行。在调用 join() 方法时,如果指定的线程还没有结束执行,那么当前线程就会被阻塞,直到指定线程执行完毕。

Thread.shutdown() 方法是不存在的,可能是指 ExecutorService.shutdown() 方法。该方法用于停止一个正在执行的线程池,该方法会先停止接受新的任务,然后等待所有已提交的任务执行完毕,最后关闭线程池。

综上所述,当执行 Thread.interrupt() 方法后,可以通过判断线程的中断状态来决定是否继续执行线程的任务,如果需要等待指定线程执行完毕后再做其他操作,可以调用 Thread.join() 方法,而 Thread.shutdown() 方法用于停止一个正在执行的线程池。

线程调度

抢占式和非抢占式

  • 抢占式:线程可以在执行中的任意时刻因其他线程强占而挂起,甚至当已经强占处理器的线程正准备运行时也是如此。
  • 非抢占式:系统调度,当系统准备让某一线程退出运行兵让其他线程运行,此线程不退出。运行到当系统又一次发生了调用。

线程的抢占式和非抢占式是指在多线程操作中,系统对于线程的调度和管理策略的不同。

抢占式策略是指,系统可以在任何时候暂停当前执行的线程,并将CPU分配给另一个需要执行的线程。这种策略可以保证每个线程都有公平的执行时间,但是也可能导致线程频繁地切换,降低系统的性能。

非抢占式策略是指,系统只有在当前执行的线程主动放弃CPU的时候,才会将CPU分配给另一个线程。这种策略可以减少线程的切换,提高系统的性能,但是也可能导致某些线程长时间占用CPU,影响其他线程的执行效率。

需要注意的是,抢占式和非抢占式并不是二选一的问题,而是根据具体的应用场景和系统需求进行选择。例如,对于实时性要求较高的系统,需要使用抢占式策略,以保证及时响应;而对于一些需要高性能的应用程序,可以采用非抢占式策略,以提高系统的效率。

线程实现

当内核不提供对多线程的进程的支持时(提供线程能力需要维护线程的调度相关):

  • 难点:
    • 一个进程内的线程不能利用多处理的能力(操作系统提供并发能力)
    • 一个线程遇到页失配,会阻塞整个进程和进程内的所有线程
    • 不同处理优先级方案?可以定制化
  • 优点
    • 用户线程开销小,不需要切换态上下文,陷入内核恢复等开销
    • 对用户态需求扩展性高
通信和调用
通信原语

是中间件而不是内核提供了现有系统中所能找到的大多数高级通信方式,包括RPC,RMI,事件通知,组通信。在用户级开发复杂的通信软件比在内核内开发相对容易。

协议和开放性

操作系统提供标准的协议,这些协议帮助在不同平台的中间件实现完整互联,操作系统的要求之一。

内核只提供在本地的进程之间的消息传递机制,并将网络协议处理留给在内核上运行的一个服务器

调用性能

在分布式系统的设计中,调用性能是一个非常关键的因素。

如果设计者在地址空间之间分离的功能很多,就越依赖于远程调用。

客户和服务器在生命周期内可能会执行百万级的调用操作。
在这里插入图片描述

调用开销

调用一个传统过程或方法,进行一次系统调用,发送一条消息,远程调用和方法调用。

每种机制都导致在调用程序和对象作用域之外的代码被执行。

一般每种机制都涉及将参数传递给调用代码的通信,以及将结果返回给调用者通信。

调用机制可以是异步或者同步。

调用机制与性能相关的重要的区别包括

  • 是否引起域转换(是否跨越了一个地址空间)
  • 是否涉及通信
  • 是否涉及线程切换和调度
在网络上的调用

一个空的RPC是执行一个空的过程并且无返回值的不带参数的RPC.

负责交换包括任何用户数据,只包含很少的系统数据的消息。

大多数延迟(客户调用RPC总共花费的时间)来源于操作系统内核代码和用户级RPC执行代码的执行时间的开销。

空调用开销的重要性来源于是一个度量固定开销——等待时间

在RPC的实现中,延迟并不是被关注的唯一数字,当数据量很大,RPC带宽(吞吐量)

在一个RPC内不同计算机之间传输数据率。

PRC:

  • 客户存根程序将调用参数编码为消息,并将消息发出,然后接收消息,解码消息。
  • 服务端,工作线程接收到达的请求,由一个I/O线程负责处理请求,并将其传递给工作线程。
  • 服务器存根程序将请求消息解码,调用指定程序,编码响应消息。
  • 网络传输时间之外计入远程调用延迟的主要因素:
    • 编码
      • 编码解码设计复制转换,当数据量增加,这是一个重要的开销。
    • 数据复制:编码之后rpc过程
      • 跨越用户-内核边界,客户服务器地址空间与内核缓冲区之间的复制
      • 每个协议之间IP复制数据
      • 网络接口和内核缓冲区之间复制数据
      • 网络接口和内存之间的传输通常使用DMA,其他复制由处理器处理。
      • DMA(Direct Memory Access,直接内存访问)是一种技术,允许外设直接访问主机内存而不需要经过CPU的干预,从而提高了系统的效率和速度。DMA技术可以让外设直接读写主存,避免了CPU的介入,提高了数据传输的速度。DMA技术常用于高速数据传输,例如硬盘、网卡、音频和视频等设备。在计算机中,DMA控制器负责管理DMA操作,CPU只需要在开始时设置DMA控制器的参数和访问权限即可。这种技术减轻了CPU的工作负担,提高了计算机的效率和性能。
    • 包初始化
      • 包括初始化协议头部和尾部,其中包括检验位。开销也是和数据量成正比。
    • 上下文切换和线程调度
      • 在一个RPC过程中会产生多次系统调用(上下文切换),可能多次调用内核的通信操作。
      • 调度一个或多个服务器进程
      • 如果操作系统采用一个单独的网络管理进程,每次发送消息会涉及他们之间的线程切换
    • 确认等待
      • RPC协议选项造成的延迟和数据量之间的相关
内存共享

用来在用户进程和内核之间快速通信。

通过在共享区域中读写数据实现快速通信。

高效是因为不需要经过内核地址到用户地址的空间拷贝数据。

系统调用和软件中断需要同步处理。

内核需要从其缓冲区向网络接口拷贝数据。

协议的选择

TCP可能会是性能上的阻碍。

TCP的慢启动算法具有延迟HTTP数据传输的作用,面向网络编程,这是一个悲观的现象。

一个计算机内的调用

轻量RPC

在这里插入图片描述

异步操作

异步操作为了应付长延迟的一种常用技术。

在并发调用和异步调用中。

调用并发化

中间件只提供阻塞调用,程序产生多个线程并发执行阻塞调用

异步调用

异步调用是对调用者调用异常异步执行,调用者进行非阻塞操作。

只要创建了调用请求信息并准备分发。调用结束。

一个异步调用返回一个promise对象。

当调用成功或者失败,系统将系统态和返回值放入promise中。

有promise返回调用的结果或异常信息。

ready操作可以不阻塞地测试promise,对应promise的就绪或阻塞状态分别返回true/false

持久异步调用

试图无限执行调用,直到调用成功或者失败,或者应用程序取消。

操作系统体系结构

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

P("Struggler") ?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值