【操作系统学习笔记】进程与线程(二)调度程序激活机制与上行调用

简介: 本文为个人学习操作系统的笔记。
本文首发于稀土掘金。该平台的作者 逐光而行 也是本人。

调度程序激活机制(scheduler activation)

  • 目标:为线程包提供用户态才能实现的高性能和灵活性,并模拟内核态的功能。
  • 实现:内核给每个进程安排一定数量的虚拟处理器,用户态运行时系统将线程分配到处理器上。进程可申请更多处理器,用时分配,不用时退回。

上行调用

  • 概念:内核了解到一个线程被阻塞后,通知该进程的运行时系统,并在堆栈中以参数形式传递有问题的线程编号和所发生事件的描述。这种通过在已知起始地址启动运行时系统并发出通知的机制被称为上行调用,这是对信号的一种粗略模拟。

(读者注:我个人读完原著相关文字描述之后,觉得上行调用并不是仅指我加粗字上的那回事。上行调用应该是一个抽象的概念,而这里列举出的概念只是一个示例而已。上行调用应该词如其名,即逆常规。比如在金字塔结构中,下层为上层提供服务,上层是下层的抽象,但是上行调用则相反,它往上调用,让上层为下层提供服务)

两者关系

调度程序激活机制的目标是作为上行调用的信赖基础。

使单线程代码多线程化会遇到的问题及解决方案

全局变量的冲突

  • 问题:对线程而言是全局变量,但对整个程序而言并不是。
  • 解决方案:

    • 全面禁止全局变量(不太现实)
    • 为每个线程赋予其私有的全局变量

    (即线程的属性中可增加一个全局变量)

访问私有全局变量

  • 为全局变量分配一块内存,并以额外参数的形式传到线程中的每个过程。
  • 引入新的库过程,该调用会在堆或转为线程保留的特殊区域上为指针分配存储空间,只有线程才可访问其全局变量。及时另一个线程创建了同名全局变量,也会因为地址不同无法访问。

库过程的不可重入性

例子1:

网络发送消息,线程切换后,缓冲区被覆盖

例子2:

内存分配,指针指向不定,容易导致空指针异常引起程序崩溃

可能的解决方案:

为每个过程提供一个包装器,其中设置二进制标志位标志正在使用,在此期间其他线程无法占用。

哪个线程应该捕捉信号

堆栈的管理

线程有自己的堆栈,一个进程有多个线程。因为多线程的存在,内核无法掌握所有的堆栈情况,分配的堆栈跟不上实际需要的堆栈,会导致栈出错。

总结

给已有系统进入线程而不实质性重新设计系统是不可行的!

进程间通信

要解决的问题

  • 规定进程如何将信息传递给另一个。
  • 如果保证两个或多个进程在关键活动中不会出现交叉。
  • 上述问题也适用于线程。(个人理解:因为本质上线程是进程的一部分)

竞争条件

两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序。

临界区

对共享内存进行访问的程序片段。

解决方案:使两个进程不同时处于临界区中,即能避免竞争条件。

进阶要求:保证并发正确且高效

  • 不事先对CPU的速度和数量作任何假设
  • 临界区外的进程不得阻塞其他进程
  • 不得使进程无限制等待进入临界区

实现互斥

屏蔽中断

单处理系统中,使每个进程在刚进入临界区后立即屏蔽其所有中断。

  • 缺点:这相当于是把中断权利交给用户进程,屏蔽中断对操作系统本身而言合适,但对用户进程来说并不是。若某个进程屏蔽中断后不再打开,将可能导致整个系统终止;且若数据状态不一致时就发生中断,将导致竞争条件。

设置变量表示锁

每个进程进入临界区前读取锁变量的值,若为0则进入并设其为1,;若为1则等待

  • 缺点:两个进程依然可能 同时 访问临界区,造成竞争条件。(因为它们可能同时读取到了锁的0状态,即使修改值前再次确认值也有相同的问题,有可能a再次确认之后b就去改)

严格轮换法

字面意思,严格按照指定的规则顺序轮换。

  • 缺点:违反了进阶要求的第2条(临界区外的进程不得阻塞其他进程)

三种解法(因其本质一样,效果又比前几种好所以合在一块说)

  • 方法:Peterson解法、TSL指令、XCHG指令
  • 本质:检查想进入临界区的进程是否被允许进入,若不允许,则等待,直到被允许
  • 缺点:忙等待,可能导致优先级反转问题

Peterson解法

#define FALSE 0
#define TRUE 1
#define N 2  //进程数量
int turn;//当前轮到的进程
int interested[N];//当前某个进程是否想进入临界区,想为1,不想为0,初始化为0
void enter_region(int process){
    int other=1-process;//另一个进程的状态
    interested[process]=TRUE;
    turn=process;
    while(turn==process&&interested[other]==TRUE) ;//当前进程执行且另一个进程等待中
}

void leave_region(int process){
    interested[process]=FALSE;
}

注:以下两种需要硬件支持

TSL指令(test and set lock)

将一个内存字lock读到寄存区器RX中,并在该内存地址上存一个非零值。执行TSL的CPU将锁住内存总线,以禁止其他CPU在本指令结束之前访问内存。

注:锁住内存总线并不等于屏蔽中断。

XCHG

本质上和TSL差不多,字面意思:原子性地交换两个位置的内容。

producer-consumer(bounded-buffer)

有可能引发竞争条件的情况:count=0时,调度程序暂停consumer(未睡眠),producer将count增为1后尝试发送wakeup信号唤醒consumer(因为之前count为0,误认为consumer已睡眠);wakeup信号丢失,consumer再次运行时,读取的是先前读到的count=0的值,于是睡眠;producer不断产生新数据,直到缓冲区满,producer最终也陷入睡眠。

  • 解决方案:加一个唤醒等待位(wakeup信号的小仓库),但治标不治本,因为该数量随进程数量增加。

信号量(semaphore)

用一个整型变量累计唤醒次数

信号量如何解决producer-consumer问题

注:代码中的up和down对应wakeup和sleep

#define N 100 //缓冲区中的槽数目 
typedef int semaphore;  
semaphore mutex=1; //对临界区的访问
semaphore empty=N; //缓冲区的空槽数目
semaphore full=0;//满槽数目

void producer(void){
    int item;
    while(1){
        item=produce_item();//该函数没有具体写出,表示产生数据
        down(&empty);//空槽数目减1
        down(&mutex);//进入临界区
        insert_item(item);//表示插入数据到缓冲区中
        up(&mutex);
        up(&full);//满槽数目+1 
    }
} 

void consumer(void){
    int item;
    while(1){
        item=produce_item();//该函数没有具体写出,表示产生数据
        down(&full);//空槽数目减1
        down(&mutex);//进入临界区
        item=remove_item();//表示插入数据到缓冲区中
        up(&mutex);
        up(&empty);//空槽数目+1 
        consume_item(item);//处理数据项 
    }    
}

互斥量

处于加锁或解锁状态,用一个整型表示,0为解锁,其他值为加锁。

Q:如果进程有不连续的地址空间,在Peterson算法、信号量、公共缓冲区中,如何共享turn变量?

  • 共享数据结构
  • 让进程与其他进程共享部分地址空间(若没有,就共享文件)

如果共享大部分地址空间,进程和线程之间的差别就变得模糊起来。不过,区分它们的点依然在于前述提到的进程和线程拥有的属性。

管程(monitor)

管程的示例代码如下:

monitor example
    integer i;
    condition c;
    
    procedure producer();
    ...
    end;
    
    procedure consumer();
    ...
    end;
end monitor;
  • 进程可在任何需要的时候调用管程中的过程,但它们不能在管程之外声明的过程中直接访问管程内的数据结构。
  • 任一时刻管程中只能有一个活跃进程。
  • Java中的同步方法与其他经典管程有本质区别:Java没有内嵌的条件变量。

避免锁

最快的锁是没有锁。

  • 在某些情况下,可以允许 写操作 来更新数据结构,即使还有其他的进程正在使用它。

参考书籍

《现代操作系统》 Andrew S.Tanenbaum,Herbert Bos著,陈向群,马洪兵等译

相关文章
|
6天前
|
算法 调度 UED
探索操作系统核心:进程管理与调度
【9月更文挑战第28天】在数字世界的心脏跳动着无数进程,它们像是细胞一样构成了操作系统的生命体。本文将深入探讨操作系统中进程管理与调度的奥秘,揭示如何通过精心设计的数据结构和算法来维护系统的稳定性和效率。我们将从进程的基本概念出发,逐步解析进程状态转换、进程同步机制,以及进程调度策略,旨在为读者呈现一幅清晰、生动的操作系统内部工作机制图景。
|
6天前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
2天前
|
算法 调度 UED
探索操作系统的心脏:进程调度算法
【9月更文挑战第32天】在数字世界的每一次心跳中,都隐藏着一个不为人知的英雄——进程调度算法。它默默地在后台运作,确保我们的命令得到快速响应,应用程序平稳运行。本文将带你走进操作系统的核心,一探进程调度的奥秘,并通过代码示例揭示其背后的智慧。准备好跟随我一起深入这趟技术之旅了吗?让我们开始吧!
|
5天前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
4天前
|
算法 Linux 调度
深入理解操作系统的进程调度
【9月更文挑战第30天】本文将带你进入操作系统的核心—进程调度。我们将探讨其工作原理,分析几种常见的调度算法,并通过实际代码示例来揭示这些理论是如何在真实系统中实现的。无论你是初学者还是有经验的开发者,这篇文章都能帮助你更好地理解操作系统的这一关键组成部分。
|
4天前
|
消息中间件 算法 调度
探索操作系统核心:进程管理与调度策略
【9月更文挑战第30天】在数字化时代的心脏,操作系统扮演着至关重要的角色。本文将深入探讨操作系统的基石之一——进程管理,以及如何通过调度策略优化系统性能。我们将从进程的基本概念出发,逐步解析进程状态、进程控制和进程间通信等关键要素。同时,我们会探讨几种常见的进程调度算法,并分析它们的优缺点。最后,文章将展示一个简单的代码示例,以加深对理论部分的理解和应用。
|
5天前
|
算法 调度 UED
探索操作系统的心脏:进程管理与调度
【9月更文挑战第29天】在数字世界的海洋中,操作系统是支撑软件与硬件和谐共舞的桥梁。本文将深入探讨操作系统的核心功能—进程管理及其调度机制,揭示它们是如何影响计算机性能和用户体验的。通过浅显易懂的语言和生动的比喻,我们将一起遨游在进程的生命周期、调度算法以及优先级等概念之间,旨在为读者呈现一个清晰的操作系统内部运作图景。
17 6
|
4天前
|
算法 调度 开发者
深入理解操作系统之进程管理与调度
【9月更文挑战第30天】本文旨在通过浅显易懂的语言和具体代码示例,带领读者探索操作系统中进程管理的奥秘。我们将从进程的生命周期出发,逐步解析进程调度的核心概念,并通过实例展示如何实现简单的进程调度算法。无论你是初学者还是有一定基础的开发者,都能在这篇文章中找到有价值的信息,帮助你更好地理解和掌握进程管理与调度的知识。
14 4
|
6天前
|
算法 调度
操作系统的心脏:深入解析进程调度算法
本文旨在深入探讨现代操作系统中的核心功能之一——进程调度。进程调度算法是操作系统用于分配CPU时间片给各个进程的机制,以确保系统资源的高效利用和公平分配。本文将详细介绍几种主要的进程调度算法,包括先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)以及优先级调度(PS)。我们将分析每种算法的基本原理、优缺点及其适用场景。同时,本文还将讨论多级反馈队列(MFQ)调度算法,并探讨这些算法在实际应用中的表现及未来发展趋势。通过深入解析这些内容,希望能够为读者提供对操作系统进程调度机制的全面理解。
|
7天前
|
算法 调度 UED
探索操作系统中的进程调度
【9月更文挑战第27天】操作系统是计算机的灵魂,而进程调度则是其跳动的心脏。本文将深入浅出地探讨进程调度机制,从理论到实践,带你领略这一技术的魅力和复杂性。我们将通过代码示例,揭示调度算法如何影响系统性能和用户体验。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇理解操作系统深层工作原理的大门。
17 6
下一篇
无影云桌面