Operation System

操作系统理论

操作系统(Operation System)是配置在计算机硬件上的第一层软件,是对硬件系统的首次扩充。为用户和应用程序提供一个简单的接口,便于用户使用。是最基本和最重要的系统软件。

  • Windows是典型的单用户多任务
  • DOS是典型的单用户单任务
  • LINUX是典型的多用户多任务

操作系统的特征


操作系统可以管理处理机、存储器、IO设备以及文件(数据和程序)

"单道批处理"(Single Job Batch Processing)和"多道批处理"(Multi-Job Batch Processing)是与计算机处理任务和作业调度相关的两种不同的处理方式,通常用于描述操作系统或计算机系统的工作方式。

  1. 单道批处理(Single Job Batch Processing):
    • 单道批处理是一种早期的计算机处理方式,通常用于早期的批处理系统。在单道批处理中,计算机系统一次只能执行一个作业(任务)。
    • 用户必须将作业提交给计算机操作员,然后操作员将作业加载到计算机中并运行。一旦作业完成,操作员可以将下一个作业加载并运行。
    • 这种方式的主要特点是,计算机系统只处理一个作业,直到它完成,然后才处理下一个作业。这种方式适用于早期计算机系统,它们通常没有多任务处理的能力。
  2. 多道批处理(Multi-Job Batch Processing):
    • 多道批处理是一种现代计算机系统的处理方式,其中计算机可以同时处理多个作业。
    • 用户可以将多个作业提交给系统,这些作业被放入一个作业队列中。操作系统负责按照一定的调度算法选择并执行队列中的作业。
    • 多道批处理的主要优点是提高了计算机资源的利用率,因为多个作业可以并行运行,而不需要等待一个作业完成才能开始下一个。
    • 这种方式适用于现代多任务操作系统,可以同时运行多个应用程序和作业。

需要注意的是,多道批处理和单道批处理是两种截然不同的处理方式,分别用于不同类型的计算机系统和应用场景。多道批处理是现代计算机系统的主要方式,而单道批处理在早期计算机系统中使用较多,现在已经不太常见。

特点

并行与并发

是操作系统最重要的特征,其他三个特征都已并发特征为前提

并行性:多个事件在同一时刻同时发生
并发性:宏观上在同一时间段内同时运行,微观上交替执行
并发特征是OS最重要的特征

共享

系统中的资源可供内存中多个并发执行的进程共同使用。

互斥共享方式

在一段时间内只允许一个进程访问资源

临界资源(独占资源):在一段时间内只允许一个进程访问的资源

同时访问方式

  • 微观上是轮流交替访问的,不是同时的
  • 宏观上在一段时间内允许多个进程“同时”访问某些资源
  • CPU/RAM/Storage、可重入代码

虚拟

8G(RAM)+100G(虚拟内存)不算108G内存

分时复用技术

  • 虚拟处理机技术
  • 虚拟设备技术

空分复用技术

  • 虚拟磁盘技术
  • 虚拟存储器技术

异步

在多程序环境下,系统允许多个进程并发执行;

进程与程序


区别
  • 进程是动态的,程序是静态的。
  • 进程是暂时的,程序的永久的
  • 进程是一个独立的运行单位,能与其它进程并行(并发)活动。而程序则不是。
  • 进程与程序的组成不同
联系
  • 通过多次执行,一个程序可对应多个进程。
  • 通过调用关系,一个进程可包含在多个程序内。
  • 进程是资源分配单位,线程是调度和执行单位。
  • 一个进程内的线程共享分配给该进程的资源。

例如,Chrome和Firefox是两个独立进程,Firefox进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。

  1. 就绪状态(Ready):等待被调取,当一个进程已准备好运行,但操作系统还没有为它分配CPU时间时,它处于就绪状态。这就像是一群运动员站在起跑线上,准备好开始比赛,但比赛裁判还没有发出起跑信号。
  2. 运行状态(Running):一旦操作系统分配了CPU时间给进程,它就会进入运行状态。这时,进程的代码正在执行,它在CPU上运行并完成任务,就像一名运动员正在比赛中奔跑一样。
  3. 阻塞状态(Waiting):等待资源,有时进程需要等待某些事件发生,例如等待数据从硬盘读取完毕或等待用户输入。在这种情况下,进程会进入阻塞状态,暂时停止执行,直到等待的事件发生。这就像一名运动员因为某种原因暂停比赛,直到问题解决。

阻塞状态是一种单向转换。当进程在运行状态需要某些资源时,它会进入阻塞状态,等待资源准备好。然后,一旦资源准备好,它会回到就绪状态,等待调度程序再次将其选为下一个要执行的进程。重要的是,阻塞状态不会直接转换为运行状态,因为缺少CPU时间。

进程

进程的组成
  1. 一个可执行的程序
  2. 该程序所需的相关数据(变量、工作空间,缓冲区域)
  3. 该程序的执行上下文(context)

进程就是可并发执行的程序在一个数据集合上的运行过程

进程是计算机操作系统中的重要概念,具有以下特征:

独立性:进程是独立运行的基本单位。每个进程都有自己的内存空间、代码、数据和系统资源。这意味着一个进程的错误或崩溃通常不会直接影响其他进程,提高了系统的稳定性。
并发性:计算机可以同时执行多个进程,每个进程都在自己的时间片内执行。这种并发性使得多任务处理成为可能,让用户感觉好像多个程序在同时运行。
独立地址空间:每个进程有自己的地址空间,不同进程的内存彼此隔离,防止一个进程访问或破坏其他进程的内存。
上下文切换:当操作系统切换CPU的执行从一个进程到另一个进程时,需要保存和恢复进程的上下文信息,包括程序计数器、寄存器等。这被称为上下文切换。
资源拥有:每个进程可以拥有自己的文件、网络连接、设备访问权限等资源,这些资源的管理和隔离是操作系统的职责。
通信和同步:不同进程之间可能需要通信和同步,操作系统提供了各种机制,如进程间通信(IPC)和同步原语,来实现这些需求。
动态性:进程的创建和销毁是动态的。操作系统可以根据需要创建新进程,而且进程可以在任何时候结束或终止。因创建而产生,因调度而执行,因得不到资源而阻塞,由撤销而消亡。
有状态:进程可以具有不同的状态,如就绪、运行、阻塞等,这些状态反映了进程在执行过程中的状态变化。

这些特征使进程成为操作系统中的重要抽象,使计算机能够有效地管理和调度多个任务,并提供隔离和资源管理。

进程 = 程序+数据+PCB
线程 = 程序 + PCB
PCB是进程存在的唯一标识
程序和数据集合是进程的实体

进程控制

功能:

完成进程状态的转换。

原语(primitive):

由若干条指令构成的“原子操作(atomic operation)“过程,作为一个整体而不可分割

进程同步

制约关系
  1. 直接制约关系(进程间的合作)——同步
  2. 间接制约(进程见互斥地共享资源)

互斥:指的是对某个系统资源,一个进程正在使用时,另一个进程必须等待前一个进程使用结束后才能使用。(类似于:打印机,磁带机,共享变量等)

临界资源:要求互斥共享的资源。

临界区:访问临界资源的代码

remainder section1
- entry section - ←使用同步规则
critical section
- exit section - ←使用同步规则
remainder section2

同步机制应遵循的准则

  • 空闲让进。 有效地使用临界资源
  • 忙则等待。 互斥地使用临界资源
  • 有限等待。 避免“死等“
  • 让权等待。

空闲让进

不允许强制交替执行
是系统在资源分配和任务调度方面对所有任务或进程都公平公正的原则。在一个公平的系统中,每个任务都有机会访问系统资源,并按照一定的规则合理地分享这些资源。

1. 售货员会尽量公平地为每位顾客提供服务,轮流为每个人提供冰淇淋。
2. 他不会一直为同一个人服务,而是会在不同顾客之间切换,以确保每个人都有机会买到冰淇淋。
3. 如果有一个顾客需要更多时间来挑选口味或支付,售货员也会耐心等待,而不是一直忽视其他人。
4. 这种方式保证了每位顾客都能够在合理的时间内获得他们想要的冰淇淋,而不会让任何一个人长时间等待或饥饿。

忙等待(Busy Waiting):你可以选择站在桌子旁边,不停地盯着那张桌子,等待那个人用完并离开。这样,你会一直占用你自己的时间,不断地检查桌子是否空了,直到它变得可用。这种方式浪费了你的时间和精力,而且可能让你感到焦虑和疲惫。

让权等待(Let-It-Wait):相反,你可以选择坐在餐馆的等候区,让其他人也能够在空闲的时间里坐在桌子旁边等待用餐。这样,你不需要一直盯着桌子,而是可以做一些其他事情,如和朋友聊天、阅读菜单等。当桌子空了,餐馆会通知你,然后你才会去坐在桌子上用餐。这种方式更加高效,因为你不会浪费时间在无用的等待上。

权(Resource):

硬件资源:这些资源包括中央处理器(CPU)、内存、硬盘驱动器、网络适配器、打印机、键盘、鼠标等物理设备。多个任务或进程可能需要竞争访问这些硬件资源。
软件资源:软件资源包括操作系统本身提供的服务和功能,例如文件系统、网络协议栈、数据库管理系统等。它们也可以包括由应用程序或进程创建的临时文件、套接字连接等。
系统服务:系统服务是操作系统提供的一种资源形式,例如系统时钟、定时器、随机数生成器等。应用程序和进程可以使用这些系统服务来执行特定的任务。
许可证和权限:在一些软件应用中,资源可以指许可证或权限,用于控制谁可以访问特定的功能或数据。

为什么 signal(S) 是<=0,而 wait(S) 是<0?

wait(S) 中使用了 <0 来检查资源是否不足;
signal(S) 使用了 <=0 来检查是否有等待的进程。
场景模拟
想象你在一家餐厅吃饭,餐厅有一定数量的餐桌,每张桌子可以容纳两位客人。这里的餐桌就相当于资源,而记录型信号量就相当于餐厅的主机。

wait(S)(P操作):在你到达餐厅时,你向餐厅的主机询问是否有空桌子(资源)。主机会查看计数器(信号量 S)上的数字。如果数字大于等于1,表示有空桌子,主机告诉你可以入座,然后将计数器减一。如果数字等于0,表示没有空桌子,主机会告诉你需要等待。你等待在等候区,直到有人离开一张桌子,主机会通知你可以入座。
signal(S)(V操作):当你吃完并离开餐桌时,你告诉主机你已经离开了,主机会将计数器加一,表示现在有一张空桌子可用。如果在你离开前等候区有其他人在等待,主机会通知他们现在可以入座。

这个比喻中的主机就像记录型信号量,它维护着可用资源的数量,并根据情况告知客人是否可以使用资源。记录型信号量允许多个进程或线程在资源不足时等待,然后在资源可用时继续执行,以协调对共享资源的访问。这种同步机制在并发编程中经常用于控制资源分配和管理。
typedef struct{
	int value;    /*信号量的值*/
	PCB * L;      /*进程阻塞队列队首指针*/
} semaphore;

/* value:初始化为一个非负整数值 */
进程同步的应用

进程同步在操作系统中有许多应用,包括:

  • 保护临界资源:确保多个进程不会同时访问共享资源,如文件、数据库、打印机等。
  • 避免竞态条件:防止多个进程同时执行某个操作,从而导致不确定的结果。
  • 实现互斥访问:确保某个资源一次只能被一个进程访问,以防止数据损坏或不一致。
  • 实现协作任务:多个进程之间可以协作执行任务,例如生产者-消费者问题、读者-写者问题等。
公交车问题
Semaphore a=b=c=d=0;
打开车门(){signal(票)}
关闭车门(){wait(票);signal(车)}
启动车辆(){wait(车)}
到站停车(){signal(票),signal(门)}
生产者消费者问题

设缓冲区大小为 n,且缓冲区初始为空。

semaphore mutex = 1;    // 缓冲区互斥信号量
semaphore empty = n;    // 缓冲区空闲位置数量
semaphore full = 0;     // 缓冲区物品数量

cobegin
Process producer() {
    while(1) {
        生产一个物品;
        P(empty);    // 请求缓冲区一个空位
        P(mutex);
        往缓冲区中放入一个物品;
        V(mutex);
        V(full);    // 缓冲区物品数量加一
    }
}
Process consumer() {
    while(1) {
        P(full);    // 请求缓冲区一个物品
        P(mutex);
        从缓冲区中取出一个物品;
        V(mutex);
        V(empty);    // 缓冲区空位数量加一
        消费一个物品;
    }
}
coend

//V是释放,先后关系没问题
十字路口问题
# 在一个只允许单向行驶的十字路口,分别有若干个由东向西和由南向北的车,
# 请用信号量机制实现十字路口管理。
sempahore mutex = 1;

void 南北(){
	while(1){
		wait(mutex);
		南北向过;
		signal(mutex);
	}
}

void 东西(){
	while(1){
		wait(mutex);
		东西向过;
		signal(mutex);
	}
}


cobegin
	南北();
	东西();
coend
施工问题

在橡皮泥施工中有三人,分别是小张,小王和小李,他们合作完成橡皮泥工艺的工作:小张将模型从工地搬运到1区,每次运送一个泥塑;小王将1区的泥塑临摹复制(重塑)一份到2区,再做一次临摹复制一个;小李将区2中的雕塑拍照扫描(留档)后拆卸,每次只留档拆卸一个。每个区只能存放一尊雕像。请用信号量机制实现泥塑工艺。

# 创建信号量来控制1区和2区的泥塑状态
semaphore_1 = threading.Semaphore(1)
semaphore_2 = threading.Semaphore(0)

# 模型数量
model_count = 5  # 假设有5个模型需要处理

def worker_zhang():
    for _ in range(model_count):
        # 小张将模型从工地搬运到1区
        semaphore_1.acquire()
        print("小张:搬运模型到1区")
        semaphore_2.release()

def worker_wang():
    for _ in range(model_count):
        # 小王将1区的泥塑临摹复制到2区
        semaphore_2.acquire()
        print("小王:从1区复制到2区")
        semaphore_2.acquire()
        print("小王:再次从1区复制到2区")
        semaphore_1.release()
        semaphore_1.release()

def worker_li():
    for _ in range(model_count):
        # 小李将区2中的雕塑拍照扫描后拆卸
        semaphore_2.acquire()
        print("小李:扫描并拆卸泥塑")
        semaphore_1.release()

# 创建三个线程模拟小张、小王和小李的工作
thread_zhang = threading.Thread(target=worker_zhang)
thread_wang = threading.Thread(target=worker_wang)
thread_li = threading.Thread(target=worker_li)

# 启动线程
thread_zhang.start()
thread_wang.start()
thread_li.start()

# 等待所有线程完成
thread_zhang.join()
thread_wang.join()
thread_li.join()

print("工艺完成")
水果问题

桌上有一个空盘子,只允许放一只水果。爸爸或向盘中放拍过,或放橘子,规定儿子只能吃句子,女儿只能吃苹果。请用信号量实现爸爸、女儿、儿子之间的同步。

import threading

empty = threading.Semaphore(1)
apple = threading.Semaphore(0)
orange = threading.Semaphore(0)


def dad():
    for i in range(10):
        empty.acquire()
        if i % 2 == 0:
            apple.release()
        else:
            orange.release()
    print("Dad finished")


def daughter():
    for i in range(5):
        apple.acquire()
        print("Daughter eats apple")
        empty.release()
    print("Daughter finished")


def son():
    for i in range(5):
        orange.acquire()
        print("Son eats orange")
        empty.release()
    print("Son finished \n")


dad_thread = threading.Thread(target=dad)
daughter_thread = threading.Thread(target=daughter)
son_thread = threading.Thread(target=son)

dad_thread.start()
daughter_thread.start()
son_thread.start()

dad_thread.join()
daughter_thread.join()
son_thread.join()
挑水问题

某寺庙,有小和尚、老和尚若干.庙内有一水缸,由小和尚提水入缸,供老和尚饮用。水缸可容纳 10 桶水,每次入水、取水仅为 1 桶,不可同时进行。水取自同一井中,水井径窄,每次只能容纳一个水桶取水。设水桶个数为 3个,试用信号灯和 PV 操作给出老和尚和小和尚的活动。

过桥问题

一座小桥(最多承受两个人)横跨南北两岸,任意时刻同一方向只允许一个人过桥,南侧桥段和北侧桥段较窄只能通过一个人,桥中央一处宽敞,允许两个人通过或歇息。使用信号灯和PV操作写出南北两岸过桥的同步算法。

哲学家进餐问题
semaphore fork[5] = {1,1,1,1,1};   semaphore room = 4;
void philosopher(int i)
{
	while(true)
	{
		think();
		wait(room);            // 请求进入房间进餐
		wait(fork[i]);         // 请求左边的筷子
		wait(fork[(i+1)%5]);      // 请求右边的筷子
		eat();
		signal(fork[(i+1)%5]);    //释放右边的筷子
		signal(fork[i]);       // 释放左边的筷子
		signal(room);          // 退出房间释放信号量room
	}
}
总结做题方法
  1. 确认进程数量
  2. 梳理临界资源(一般由一个信号量控制)
  3. 确定信号量
  4. 用伪代码描述
程序、线程和进程的区别?

区别:

  1. 程序(Program):
    • 程序是一组指令的集合,用于执行特定的任务或功能。
    • 它通常以可执行文件的形式存储在计算机的磁盘上,例如.exe(Windows)或.out(Linux)文件。
    • 程序是静态的,通常不具有运行状态或内部数据。它需要被加载到内存中才能执行。
    • 程序是单独运行的,没有直接的互动或通信。
  2. 进程(Process):
    • 进程是操作系统中的一个独立的执行单元,拥有自己的内存空间、文件句柄、系统资源和执行状态。
    • 每个进程都运行在独立的内存空间中,互不干扰。
    • 进程之间可以并发执行,具有独立的生命周期,可以创建、终止、暂停和恢复。
    • 进程之间的通信通常需要使用特定的机制,如管道、套接字等。
  3. 线程(Thread):
    • 线程是进程内的执行单元,多个线程可以在同一个进程中并发执行。
    • 所有线程共享相同的内存空间和系统资源,因此它们之间可以更容易地进行通信和数据共享。
    • 线程通常比进程更轻量级,因为它们不需要独立的内存空间。
    • 线程之间的切换通常比进程切换更快,因为它们共享相同的上下文。

总结: 程序是可执行代码的静态集合,进程是操作系统中的独立执行单元,线程是进程内的执行单元。进程之间相互隔离,线程之间可以更容易地共享数据和通信。线程的创建和切换通常比进程更轻量级,但也需要更谨慎地管理共享资源,以避免竞争条件和死锁等问题。线程是进程的一部分,是进程内的执行单元。

处理机调度的三个层次
  1. 作业调度(高级调度):选择作业,调入内存,分配资源建立进程,将PCB插入就绪进程队列
  2. 进程调度(低级调度):如,进程。没有想法。
  3. 中级调度: 进程印象在内存和盘交换区间的对换操作。通过将一些进程挂起,从而释放资源以供其他进程使用,防止死锁。
  1. 非剥夺式调度(Non-preemptive Scheduling)
    • 在非剥夺式调度中,一旦进程或线程获得CPU执行时间,它将一直执行,直到自愿让出CPU或完成其任务。
    • 这意味着,一旦进程或线程开始执行,它不会被操作系统强制中断,直到它自己决定放弃CPU或者执行完毕。
    • 非剥夺式调度通常用于实时任务或任务执行时间可预测的情况,因为它可以确保任务在给定的时间内完成。
  2. 剥夺式调度(Preemptive Scheduling)
    • 在剥夺式调度中,操作系统有权强制中断正在执行的进程或线程,并将CPU时间分配给另一个进程或线程。
    • 这意味着,在剥夺式调度中,没有进程或线程可以长时间占用CPU,而可以在任何时刻被抢占。
    • 剥夺式调度通常用于多任务环境,以确保公平性、响应时间和资源共享。

比较非剥夺式和剥夺式调度的优缺点

非剥夺式调度的优点:

  • 简单,适用于任务执行时间可预测的情况。
  • 避免了上下文切换(context switching)的开销,因为进程不会频繁地被抢占。
  • 可以确保实时任务或有硬实时要求的任务按时完成。

非剥夺式调度的缺点:

  • 无法有效处理长时间运行的任务,可能导致其他任务等待时间过长,出现"饥饿"问题。
  • 无法有效应对不可预测的任务行为,如响应突发事件

剥夺式调度的优点:

  • 更好地支持多任务环境,可以确保每个任务都有机会执行。
  • 可以更好地响应紧急任务和中断,提高系统的响应性。
  • 允许操作系统有效地分配处理机时间,以平衡各种性能指标。

剥夺式调度的缺点:

  • 引入了上下文切换的开销,因为进程或线程需要频繁切换。
  • 可能导致资源争夺和竞态条件,需要进行额外的同步和互斥处理。
  1. 周转时间(Turnaround Time)
    • 周转时间是指从一个进程或任务被提交到系统开始执行,到该进程或任务完成执行并终止的总时间。
    • 周转时间包括两个关键部分:等待时间和执行时间。即周转时间 = 等待时间 + 执行时间
    • 周转时间是一个全局性能指标,它衡量了整个任务或进程的执行效率。较短的周转时间通常表示更好的性能。
  2. 等待时间(Waiting Time)
    • 等待时间是指一个进程或任务在就绪队列中等待CPU执行时间的总时间。
    • 进程在等待CPU时,它的等待时间会增加。当进程获得CPU执行时间时,等待时间会停止增加。
    • 高等待时间通常表示系统中任务排队等待CPU时间的效率较低。

先来先服务(First-Come, First-Served,FCFS):

  • 工作原理:进程按照它们进入就绪队列的顺序执行。当一个进程开始执行,它会一直运行,直到它完成或者阻塞。
  • 非抢占性:FCFS通常是非抢占性的,这意味着一旦进程开始执行,它会一直运行,直到它主动释放CPU。
  • 公平性:该算法通常具有公平性,因为它按照进程到达的顺序分配CPU时间,每个进程都有平等的机会。
  • 缺点
    1. 不适合长任务:如果队列中有一个长时间运行的任务,那么其他短任务可能要等很长时间才能获得CPU时间。
    2. 不利于响应时间:FCFS可能导致长等待时间,不适合需要快速响应的应用。
    3. 浪费CPU时间:当一个进程在执行过程中被阻塞,其他进程将无法执行,浪费CPU资源。

最短作业优先(Shortest Job First,SJF)

  • 该算法会选择下一个运行的进程,以便执行时间最短的进程优先。
  • SJF通常以非抢占方式工作,即一旦进程开始执行,它将一直运行到完成。
  • 优点
    1. SJF通常能够提供较短的平均等待时间,因为它倾向于首先执行短期任务。
    2. 它有助于最大程度地利用CPU资源,因为短期任务可以更快地释放CPU。
  • 缺点
    1. SJF需要准确估计或预测每个进程的执行时间,这可能不容易。
    2. 对于长期任务来说,可能需要等待时间很长,因为短期任务会优先执行。

时间片轮转算法(Round Robin):

主要特点是使用固定长度的时间片,每个进程被分配相等的时间,然后轮流执行。

简单轮转法

  • 简单轮转法是时间片轮转算法的最基本形式。
  • 这种方法假定所有进程是平等的,并且每个进程在队列中等待的时间都是相同的。
  • 优点是公平性,但可能导致高开销的上下文切换。

多队列轮转法

  • 多队列轮转法引入了多个就绪队列,每个队列具有不同的时间片大小或优先级。
  • 进程根据其属性或优先级分配到不同队列中。
  • 高优先级队列通常具有更短的时间片,以便快速响应重要任务。
  • 低优先级队列可能具有较长的时间片,以处理较长时间运行的任务。
  • 这种方法提高了系统的响应性和效率,适用于多种类型的任务。

如图所示,所有 5 个进程按给定顺序在时刻 0 到达,cpu 区间时间的长度都 以 ms 计:

问:
使用 FCFS、SJF(非抢占式)和 RR(时间片为 10ms)调度算法,并判断 哪个算法的平均等待时间最小?

进程区间时间
P110
P229
P33
P47
P512

答:

  1. FCFS(先来先服务):
    • 到达顺序:P1 -> P2 -> P3 -> P4 -> P5计算等待时间:
      • P1: 0msP2: 10msP3: 39msP4: 42msP5: 49ms
    平均等待时间 = (0 + 10 + 39 + 42 + 49) / 5 = 28ms
  2. SJF(非抢占式短作业优先):
    • 到达顺序:P3 -> P1 -> P4 -> P5 -> P2
    • 计算等待时间:
      • P3: 0ms
      • P1: 3ms
      • P4: 13ms
      • P5: 20ms
      • P2: 32ms
    平均等待时间 = (0 + 3 + 13 + 20 + 32) / 5 = 13.6ms
  3. RR(时间片为10ms):
    • 到达顺序:P1 -> P2 -> P3 -> P4 -> P5
    • 计算等待时间:
      • 第一轮:0,10,20,23,30
      • 第二轮: ,40-10, , ,50-10
      • 第三轮: ,40-10+2, , ,
    平均等待时间 = (0 + 32 + 20 + 23 + ) / 5 = ms

死锁

死锁的定义:

一组进程中,每个进程都无限等待被该组进程中另一进程所占有的资源,因而永远无法得到的资源,这种现象称进程死锁,这一组进程就称为死锁。

关于死锁的结论:

参与死锁的进程最少是两个 两个以上进程才会出现死锁 参与死锁的进程至少有两个已经占有资源 参与死锁的所有进程都在等待资源 参与死锁的进程是当前系统中所有进程的子集

产生死锁的原因:

  1. 竞争资源:死锁通常是由多个进程竞争有限资源而引起的。当多个进程需要某些资源,而这些资源又不足以同时满足它们时,可能会发生死锁。这种资源包括硬件资源(例如打印机、磁盘驱动器、网络端口)和软件资源(例如文件、数据库记录、锁)等。这种情况下,如果进程无法释放其当前占用的资源,那么它们可能会无限期地等待其他进程释放所需的资源,导致死锁。
  2. 进程推进顺序不当:另一个产生死锁的原因是进程推进顺序不当。如果多个进程按照不适当的顺序请求资源,可能会陷入死锁状态。这通常涉及到循环依赖,其中每个进程都在等待另一个进程持有的资源,形成了一个闭环。例如,进程A等待进程B的资源,进程B等待进程C的资源,进程C等待进程A的资源,这将导致死锁。

四个必要条件:

  1. 互斥:每个资源只能被一个进程占用,这是资源的排他性质,即一次只能有一个进程使用资源。如果多个进程都想要访问同一资源,互斥条件将阻止它们同时访问。
  2. 请求与保持:进程在请求其他资源时会继续保持它们已经获得的资源。这意味着一个进程可以持有一些资源并请求其他资源,而不会释放已经获得的资源。这导致了资源的紧缺,其他进程无法获取所需的资源。
  3. 不剥夺:资源不能被抢占,即已经分配给一个进程的资源不能被其他进程强制性地释放。只有持有资源的进程可以释放它们。这可以防止其他进程干预已经运行的进程。
  4. 环路等待:多个进程之间形成一个资源等待的环路。例如,进程A等待进程B占用的资源,进程B等待进程C占用的资源,而进程C等待进程A占用的资源。这种循环等待导致了死锁。

处理死锁的可能方式:

  1. 要求进程一次性请求所有需要的资源(效率低、需要估计所需要的资源)
  2. 一个占有资源的进程请求资源不得则释放已占有资源或操作系统要求另一个进程释放资源(需要容易保存资源状态的结构)
  3. 防止循环等待的出现
  4. 定义一个请求资源的顺序,所有进程对资源的请求必须严格按资源序号递增的顺序提出(低效)

存储器管理

程序的装入:
  1. 绝对装入方式:
    • 是一种早期的程序加载和执行方式,通常用于早期的计算机系统中。在这种方式下,程序被加载到固定的内存位置,程序中的地址都是绝对地址,不会发生变化。这意味着每个程序都需要分配固定的内存空间,且程序之间不能共享内存空间。这种方式的主要优点是简单,但缺点是浪费内存,因为不同程序之间的空间不能重复使用,且程序难以共享。
    • 早期的单用户操作系统,如DOS(Disk Operating System),通常使用绝对装入方式。在DOS中,每个程序都加载到固定的内存位置,如COM文件和EXE文件。
  2. 可重定位装入方式:
    • 是一种进一步发展的加载方式,它允许程序加载到内存的任意位置,而不受固定的内存地址限制。在这种方式下,程序中的地址通常是相对地址,而不是绝对地址。当程序被加载时,系统会执行地址重定位,将程序中的相对地址转换为实际的内存地址,以确保程序能够正确运行。这种方式允许多个程序共享内存空间,更高效地利用内存。可重定位装入方式通常需要使用链接器和加载器来完成地址重定位的过程。
    • 大多数现代操作系统和编程语言都支持可重定位装入方式。例如,在C语言中,编译器会生成可重定位的目标代码,该代码可以在加载时进行地址重定位。这允许多个程序共享内存,并支持动态链接库(DLL)等功能。
  3. 动态运行时装入方式:
    • 是一种更灵活的加载方式,程序的某些部分只在运行时才被加载到内存中,而不是在程序启动时一次性加载整个程序。这种方式通常用于支持插件或模块化系统,以减少内存占用和加快程序启动速度。在动态运行时装入方式中,只有在需要使用某个模块时才加载它,这有助于减少内存占用和提高系统的可扩展性。这通常需要使用操作系统的动态链接器来实现。
    • 浏览器中的JavaScript代码是一个常见的动态运行时装入的例子。浏览器会在运行时动态加载JavaScript代码,以响应用户交互和页面事件。
    • 操作系统内核也使用动态运行时装入方式来加载设备驱动程序,根据需要加载和卸载驱动程序,以提供对不同硬件设备的支持。

总结一下,绝对装入方式是最简单的加载方式,但内存利用不高。可重定位装入方式允许程序加载到任意内存位置,提高内存利用。动态运行时装入方式更加灵活,允许程序在运行时加载所需的模块,减少内存占用和提高可扩展性。这些加载方式根据不同的需求和系统特点来选择使用。

  1. 内存分配:使进程各得其所,互不干扰;尽可能提高内存利用率。
    1. 静态分配,动态分配
    2. 连续分配,离散分配
  2. 地址映射:地址映射是将逻辑地址(由程序员或进程使用)映射到物理地址(实际内存模块的地址)的过程。这包括使用页表或段表等数据结构来实现虚拟内存的地址映射,以便在多道程序执行时有效地管理内存。
  3. 静态重定位
    • 静态重定位是一种在编译或链接阶段进行的地址重定位技术。
    • 在静态重定位中,程序的地址引用在编译时或链接时就被确定,并且生成的可执行文件中包含了实际的物理地址。
    • 这意味着程序在加载和执行时不会有地址引用的变化,因为所有地址都已经确定。
    • 静态重定位的优点是速度快,因为没有额外的运行时开销,但它限制了程序的灵活性,因为程序必须始终加载到相同的内存地址,这对于多道程序设计和内存管理可能不太适用。
  4. 动态重定位
    • 动态重定位是一种在运行时进行的地址重定位技术。
    • 在动态重定位中,程序的地址引用是相对的,而不是绝对的。这些相对地址需要在运行时映射到实际的物理地址。
    • 动态重定位的典型实现是虚拟内存系统,其中每个程序认为它加载到内存的地址是其虚拟地址,而操作系统负责将虚拟地址映射到实际的物理内存地址。
    • 动态重定位提供了更大的灵活性,允许操作系统更好地管理内存,支持多道程序设计,以及动态加载和卸载模块。
    • 运行时的地址映射可能会引入一些性能开销,但通常可以提供更好的系统稳定性和安全性。
    动态重定位增加了一个重定位寄存器,用于存放当前进程在内存中的起始地址
  1. 内存保护:内存管理负责确保一个进程不能读取或写入其他进程的内存空间,以维护系统的安全性和隔离性。内存保护通常通过硬件和操作系统的协同工作来实现,例如使用访问权限位和特权级别。
  2. 内存扩容:内存管理可以支持动态内存扩容,以满足系统中不断增长的内存需求。这包括在需要时将新的内存模块添加到系统中,以扩展可用的物理内存空间,或通过虚拟内存技术来扩展逻辑地址空间。
连续分配方式
  1. 固定内存分配(Fixed Memory Allocation):
    • 在固定内存分配中,系统将内存分成若干个固定大小的区块,每个区块具有相同的大小。每个区块通常分配给一个进程,进程的大小必须与分配的区块大小相匹配。这种分配方式通常用于嵌入式系统等特定应用,其中内存需求相对稳定,而且需要最小的内存管理开销。
    实际案例:嵌入式系统
    • 嵌入式系统通常采用固定内存分配,因为它们的硬件资源和内存需求通常是已知的,可以在设计时分配适当的内存大小。例如,一个嵌入式控制器可以分配固定大小的内存块给不同的任务或功能模块,以确保系统可靠运行。
  2. 单一内存分配(Single Continuous Memory Allocation):
    • 单一内存分配是一种连续分配方式,其中整个进程的内存需求都分配为一个连续的内存块。这种分配方式较为简单,但要求内存块的大小必须与进程的内存需求匹配。
    实际案例:嵌入式系统固件
    • 嵌入式系统的固件通常以单一内存分配方式编写,其中整个程序都加载到一个连续的内存块中。这样可以确保程序的内存访问是简单和高效的,适用于资源有限的嵌入式环境。
  3. 动态内存分配(Dynamic Memory Allocation):
    • 动态内存分配是一种更灵活的分配方式,允许程序在运行时请求和释放内存。在这种方式下,内存管理器维护一个内存池,程序可以动态分配和释放内存块,无需事先知道内存需求的大小。这种方式通常用于通用计算机系统中,支持多道程序设计和动态内存管理。
    实际案例:C/C++语言中的mallocfree
    • C/C++语言中的标准库提供了mallocfree等函数,用于动态内存分配和释放。程序可以在运行时使用malloc函数请求内存块,并使用free函数释放内存块,从而实现动态内存管理。这种方式在许多桌面应用程序和服务器应用程序中得到广泛使用。
  • 空闲分区表(在系统中设置一张空闲分区表,用于记录每个空闲分区的情况,每个空闲分区占一个表目,表目中包括分区序号、分区始址、分区大小等)
  • 空闲分区链(为了实现对空闲分区的分配和链接,在每个分区的起始部分,设置一些用于控制分区分配的信息,以及用于链接各分区所用的向前指针;在分区尾部设置一向后指针,这样,可以将空闲分区链接成一个双向链),为了检索方便,在分区尾部重复设置状态为和分区大小表目,当分区被分配出去以后,把状态为从0改成1,此时前后指针都失去意义(已经不再空闲链表中)。
相关算法
  1. 首次适应算法(First Fit):
    • 首次适应算法要求空闲分区链按地址递增的顺序链接。在分配内存时,它从链表的开头开始顺序查找,直到找到一个足够大的空闲分区,然后将该分区分配给请求的任务。这种算法倾向于优先利用低地址部分的空闲分区,以保留高地址部分的大空闲区。然而,它可能会导致低地址空间不断被划分,留下许多难以利用的小空闲分区。
    实际案例:操作系统的内存管理
    • 操作系统通常使用首次适应算法来分配内存块给不同的进程。例如,当一个新进程请求内存时,操作系统会从内存中的空闲分区链开始查找,以找到满足需求的第一个空闲分区并分配给该进程。
  2. 循环首次适应算法(Next Fit):
    • 循环首次适应算法是首次适应算法的变种。不同之处在于,它从上次找到的空闲分区的下一个分区开始查找,而不是从链表的开头开始。这有助于减少查找空闲分区时的开销,但可能会导致缺乏大的连续空闲分区。
    实际案例:内存管理工具
    • 内存管理工具如 mallocfree库函数通常使用循环首次适应算法来分配和释放动态内存。它们会记住上次分配的位置,以加速查找空闲内存块的过程。
  3. 最佳适应算法(Best Fit)
    • 最佳适应算法总是将满足要求并且大小最接近作业需求的空闲分区分配给任务。它要求将所有空闲分区按容量从小到大排序,以便快速找到最佳匹配。尽管它看似是最佳选择,但可能会留下许多难以重复利用的小空闲分区。
    实际案例:分页内存管理
    • 分页内存管理系统可以使用最佳适应算法来选择最适合作业大小的内存页框。这有助于最大程度地减少内存浪费,但可能会导致碎片。
  4. 最坏适应算法(Worst Fit):
    • 最坏适应算法是一种内存分配算法,其主要思想是在每次分配内存时选择剩余空间最大的分区。这种方法旨在最大化每个分区的剩余空间,以便容纳可能更大的未来分配请求。当系统需要分配一块内存时,最坏适应算法会遍历所有可用分区,选择剩余空间最大的一个。
    • 虽然这种算法可以减少外部碎片,但它可能导致分配较小请求时的内部碎片。因为它倾向于选择较大的空闲分区,可能会留下较小的未使用空间,导致资源的浪费。
    实际案例:大型文件处理
    • 在处理大型文件时,最坏适应算法可以用于分配内存。由于大型文件需要连续的存储空间,选择剩余空间最大的分区可以最大限度地减少外部碎片,提高文件的读写效率。但需要注意,这种方法可能导致一些小片段的内部碎片。

页面与页表

分页存储管理是将一个进程的逻辑地址空间分成若干个大小相等的片,称为页面或页,并为各页进行编号,从0开始。相应地,把内存空间分成与页面相同大小的若干个存储块,称为(物理)块或者页框,也同样为它们编号,如0#块,1#块等。在未进程分配内存时,以块为单位将进程的若干个页分别装入到多个可以不相邻接的物理块中,由于进程的最后一页经常装不满一块而形成不可利用的碎片,称之为页内碎片。

在分页系统中的页面其大小应适中,页面若太大,一方面可以是内存碎片减少,有利于提供内存利用率,但是,每一个进程占用的页面较多,导致页表过长,占用太多内存,会降低页面换进换出的效率。页面若太大,可减少页表的长度,提供页面换进换出的速度,但是,内存碎片会增大,所以,也页面大小应适中,通常为512B~8K。

连续的进程分页,插入到分散的进程块中,页面一定是连续的,而内存块一定是离散有碎片的。

(1)页是信息的物理单位,段是信息的逻辑单位;
(2)页的大小固定,段的大小可变;
(3)分页系统中的逻辑地址空间是一维的,分段系统中的是二维的。

页表的功能可以由一组专门的寄存器来实现,一个页表项用一个寄存器,由于寄存器具有较高的访问速度,因而有利于提高地址变换的速度,但成本较高,且页表项一般会很多,都使用寄存器实现不太现实,因此,页表大多驻留在内存。在系统中只设置一个页表寄存器PTR(Page-Table Register),用于存放页表在内存的始址和页表的长度,平时,进程执行时,页表的始址和页表长度存放在本进程的PCB中,当调度程序调度到某进程时,将这两个数据装入页表寄存器,因此,在单处理机环境下,虽然系统中可以运行多个进程,但只需要一个页表寄存器。

上述操作中,每次存取一个数据时,都会访问内存两次,第一次是访问内存中的页表,从中找到指定页的物理块号,再将块号与页内偏移量W拼接,以形成物理地址,第二次访问时,才是从第一次所得的地址中获得所需数据,因此,这种方式会使计算机的处理速度降低一半,为了提高地址变换速度,可以在地址变换机构中增设一个具有并行查询能力的特殊高速缓冲寄存器,又称为联想寄存器或快表,用以存放当前访问的那些页表项。

分页和分段的主要区别:

固定大小 vs. 可变大小:

  • 分页: 将物理内存和逻辑内存划分为固定大小的块,称为页面。通常,页面的大小是固定的,例如4KB。
  • 分段: 将逻辑内存和物理内存划分为不同大小的段,每个段可以有不同的长度。不同的段可以有不同的逻辑和物理大小。
  1. 地址空间划分方式:
    • 分页: 逻辑地址空间和物理地址空间都被均匀地划分为页面。逻辑地址被分为页号和页内偏移。
    • 分段: 逻辑地址空间被划分为不同的段,每个段有独立的逻辑意义。逻辑地址被分为段号和段内偏移。
  2. 碎片问题的不同:
    • 分页: 可能引入内部碎片,当一个页面中的所有内存没有完全被使用时,会浪费内存。外部碎片也可能发生,因为页面的大小是固定的,不一定与程序的逻辑结构匹配。
    • 分段: 可能引入内部碎片,特别是在最后一个段中。由于段的大小可变,某些段可能会浪费一些空间,但外部碎片通常较少。
为什么使用分页和分段:

因为

  1. 地址空间划分: 分页和分段都提供了一种将逻辑地址空间划分为较小单元的方式,使得程序和数据可以被加载到离散的、不一定连续的物理内存中。
  2. 内存保护: 分页和分段都提供了内存保护的机制,通过设置页面或段的访问权限,可以防止程序越界访问。
  3. 虚拟内存: 这两种技术都是虚拟内存系统的基础,可以将部分程序或数据存储在辅助存储器(如硬盘)上,从而扩展可用的逻辑地址空间。
  4. 共享与保护: 分段更适合实现共享和保护的机制,因为每个段都有独立的属性,可以分别设置访问权限。
在段页式管理系统中,页号是从段号中产生的吗?

在段页式管理系统中,段页表结合了分段和分页的概念,以提供更灵活的内存管理机制。在这种系统中,逻辑地址通常由两部分组成:段号(Segment Number)和页号(Page Number)。

  1. 段号(Segment Number): 用于标识逻辑地址属于哪个段。每个段有一个唯一的段号,这个号码在段页表中用于查找相应的段描述符。
  2. 页号(Page Number): 用于标识逻辑地址在段内的哪一页。每个段被划分为固定大小的页面,页号表示在这个段中具体的哪一页。

这两者组合在一起形成一个完整的逻辑地址。在段页式管理系统中,段号和页号是分开的,并且在地址转换的过程中,它们分别用于查找段描述符和页表项。

所以,页号并不是直接从段号中产生的,而是在逻辑地址中分开表示的两个部分。这种组合的设计使得段页式管理系统更加灵活,能够充分结合分段和分页的优点,同时提供更高的内存管理效率。

什么是抖动?为什么会抖动?

通常指的是在进程调度或资源管理过程中出现的不稳定性和变化的现象。 内存抖动: 内存抖动是指由于系统内存不足,导致不断发生页面交换(Page Swap)的情况。当系统中的活跃进程需要更多的内存,而内存不足时,操作系统会将某些页面(页)从内存交换到磁盘上,以释放内存供其他进程使用。然而,频繁的页面交换可能导致系统性能下降,因为磁盘 I/O 操作通常比内存访问慢得多。 进程调度抖动: 进程调度抖动指的是在多任务系统中,由于频繁进行进程切换而导致的系统抖动。如果操作系统采用抢占式调度算法,那么在进程切换时,会保存当前进程的上下文并加载下一个进程的上下文。频繁的进程切换可能引起调度抖动,降低系统性能。 中断处理抖动: 中断是操作系统中响应外部事件的一种机制。如果系统中存在大量的中断事件,且中断处理程序的执行时间相对较长,就可能导致中断处理抖动。这可能影响系统对实时事件的响应性能。 资源竞争: 在多任务环境中,不同的进程或线程可能竞争有限的系统资源,如 CPU、内存、磁盘等。竞争这些资源可能导致不稳定性和变化,从而引起系统抖动。

总结:频繁的读取写入

虚拟存储器

虚拟存储器特征

多次性:一个作业被分成多次调入内存运行 对换性:作业在运行过程中会被换进换出 虚拟性:用户“看到”的内存容量远远大于实际内存 虚拟性(目标)是以多次性(主要特征)和对换性为基础的。

🌟 页面置换算法

要调入缺页时,若内存中已没有空闲块,则必须根据一定的策略从内存中选择一个页面换出到外存。选择换出页面的算法称为页面置换算法。

  1. 最佳页面置换算法(Optimal Page Replacement):
    • 算法思想:选择在未来最长时间内不会被访问的页面进行置换
    • 优点:理论上是最佳的算法,可以最小化缺页次数。
    • 缺点:需要未来页面访问信息,实际中难以实现;算法的实时性较差。
  2. 先进先出算法(FIFO - First-In-First-Out):
    • 算法思想:选择最早进入物理内存的页面进行置换。
    • 优点:简单易实现。
    • 缺点:可能导致Belady异常,即在物理内存增加的情况下,缺页次数反而增加。
  3. 最近最久未使用算法(LRU - Least Recently Used):
    • 算法思想:选择最长时间未被使用的页面进行置换。
    • 实现方式:可以通过计数器、栈、或近似算法(Clock算法)等方式实现。
    • 优点:相对准确地反映了页面的使用情况。
    • 缺点:实现复杂,需要维护页面访问顺序的数据结构。

设备管理

缓冲管理

(1)缓和CPU与I/O设备间速度不匹配的矛盾。
(2)减少对CPU的中断频率,放宽对CPU中断响应时间的限制。
(3)提高CPU和I/O设备之间的并行性。

设置缓冲区的原则

如果数据到达率与离去率相差很大,则可采用单缓冲方式;如果信息的输入和输出速率相同(或相差不大)时,则可用双缓冲区;对于阵发性的输入、输出,可以设立多个缓冲区。

存储设备也称为设备;输入/输出设备也称为字符设备。

磁盘

磁盘访问时间:

四种算法:

1、先来先服务算法(FCFS)First Come First Service

这是一种比较简单的磁盘调度算法。它根据进程请求访问磁盘的先后次序进行调度。此算法的优点是公平、简单,且每个进程的请求都能依次得到处理,不会出现某一进程的请求长期得不到满足的情况。此算法由于未对寻道进行优化,在对磁盘的访问请求比较多的情况下,此算法将降低设备服务的吞吐量,致使平均寻道时间可能较长,但各进程得到服务的响应时间的变化幅度较小。

先来先服务 (125)86.147.91.177.94.150.102.175.130


2、最短寻道时间优先算法(SSTF) Shortest Seek Time First

该算法选择这样的进程,其要求访问的磁道与当前磁头所在的磁道距离最近,以使每次的寻道时间最短,该算法可以得到比较好的吞吐量,但却不能保证平均寻道时间最短。其缺点是对用户的服务请求的响应机会不是均等的,因而导致响应时间的变化幅度很大。在服务请求很多的情况下,对内外边缘磁道的请求将会无限期的被延迟,有些请求的响应时间将不可预期。

最短寻道时间优先(125)130.147.150.175.177.102.94.91.86


3、扫描算法(SCAN)电梯调度

扫描算法不仅考虑到欲访问的磁道与当前磁道的距离,更优先考虑的是磁头的当前移动方向。例如,当磁头正在自里向外移动时,扫描算法所选择的下一个访问对象应是其欲访问的磁道既在当前磁道之外,又是距离最近的。这样自里向外地访问,直到再无更外的磁道需要访问才将磁臂换向,自外向里移动。这时,同样也是每次选择这样的进程来调度,即其要访问的磁道,在当前磁道之内,从而避免了饥饿现象的出现。由于这种算法中磁头移动的规律颇似电梯的运行,故又称为电梯调度算法。此算法基本上克服了最短寻道时间优先算法的服务集中于中间磁道和响应时间变化比较大的缺点,而具有最短寻道时间优先算法的优点即吞吐量较大,平均响应时间较小,但由于是摆动式的扫描方法,两侧磁道被访问的频率仍低于中间磁道。

电梯调度(125)102.94.91.86.130.147.150.175.177


4、循环扫描算法(CSCAN)

循环扫描算法是对扫描算法的改进。如果对磁道的访问请求是均匀分布的,当磁头到达磁盘的一端,并反向运动时落在磁头之后的访问请求相对较少。这是由于这些磁道刚被处理,而磁盘另一端的请求密度相当高,且这些访问请求等待的时间较长,为了解决这种情况,循环扫描算法规定磁头单向移动。例如,只自里向外移动,当磁头移到最外的被访问磁道时,磁头立即返回到最里的欲访磁道,即将最小磁道号紧接着最大磁道号构成循环,进行扫描。

循环扫描 (125)130.147.150.175.177.86.91.94.102

参考资料

  • Tanenbaum A S, Bos H. Modern operating systems[M]. Prentice Hall Press, 2014.
  • 汤子瀛, 哲凤屏, 汤小丹. 计算机操作系统[M]. 西安电子科技大学出版社, 2001.
  • 存储器管理(四) https://www.cnblogs.com/leesf456/p/5616041.html

Operation System

操作系统理论 操作系统(Operation System)是配置在计算机硬件上的第一层软件,是对硬件系统的首次 […]

基于建立私有DERP节点与私有Headscale优化Tailscale组网的实践学习

警告 本文仅用于学习讨论企业内部组网架构且不涉境外流量。本文所讲述的均为技术实践话题且实践内容仅供个人参考,任 […]

如何防止微信内置浏览器缓存

如何防止微信内置浏览器缓存 WebView 缓存策略 微信内置浏览器(微信WebView)中的缓存策略可能导致 […]

用了HTTPS 防火墙就不知道你在访问什么了吗?

什么是SNI? SNI(Server Name Indication)是TLS的扩展,用来解决一个服务器拥有多 […]

基于Clash Parsers规则预处理绕过Bing地区审核

痛点 💡 用户在使用Clash的“edit”或”edit externally”自行编辑规则后,每次自动更新订 […]

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

富强民主文明和谐