张德长

导航

操作系统(学习笔记)

操作系统(学习笔记)

 

术语

PCB=process control block=进程控制块,用于存储进程相关信息,以便进程切换;

GDT=global descriptor table=全局描述符表,用于存储系统进程描述信息和LDT信息;

LDT=local descriptor table=局部描述符表,用于存储用户进程描述信息;

GDTR=global descriptor table register=全局描述符表寄存器,用于存储全局描述符表在内存中的地址;

LDTR=local descriptor table register=局部描述符表,用于存储局部描述符表在内存中的地址;

TLB=Translation Look-aside Buffer=地址变换高速缓存=快表,实质上它是一种cache;

MMU=Memory Management Unit的=内存管理单元,有时称作分页内存管理单元;

DPL=descriptor privilege level=门或段特权级

CPL=current privilege level=当前程序的特权级

RPL=request privilege level=进程对段访问的请求权限

semaphore英 /ˈseməfɔː(r)/ 美 /ˈseməfɔːr/n. 信号量、信号标、信号对象

mutex  英[m'juːteks]美[m'juːteks] n. 互斥;互斥元,互斥体;互斥量

信号量计数:可用资源的数量;
wait()使用资源,减少信号量计数
signal()释放资源,增加信号量计数
信号量计数为0,所有资源都被使用,可用资源为0;

block()阻塞、挂起进程
wakeup()唤醒、重新启动进程
FIFO队列 FIFO(First Input First Output)队列,即先入先出队列

 

外部碎片和内部碎片

区别

外部碎片(未分配)

内部碎片(已分配)

性质不同

是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域

是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间

存储块不同

是出于任何已分配区域或页面外部的空闲存储块

是处于区域内部或页面内部的存储块

状态不同

这些存储块的总和可以满足当前申请的长度要求,但是由于它们的地址不连续或其他原因,使得系统无法满足当前申请

占有这些区域或页面的进程并不使用这个存储块。而在进程占有这块存储块时,系统无法利用它。直到进程释放它,或进程结束时,系统才有可能利用这个存储块

内存管理

重定位

让内存用起来:将程序放到内存中,让程序跑起来,PC指向开始地址,内存就用起来了;

地址重定位:编译时重定位(效率更高)、载入时重定位(灵活性好);

编译时重定位:程序只能放在内存固定位置;

载入时重定位:程序一旦载入,内存就不能动了;

重定位:可变的基准地址+不变的偏移地址;

程序载入后也要移动;

运行时重定位:程序换入时,进行重定位;

程序换入时,将新的基地址写入PCB,以便完成重定位;

程序不是常驻内存,而是将执行的程序换入内存,而将不执行的程序换出内存;

PCB的基址放入基址寄存器,和程序中的偏移地址相加计算重定位地址,然后再翻译成物理地址;

PCB→基址寄存器→偏移地址→重定位→物理地址;

分段

内存分段管理:每个段有各自的特点、用途;

代码段、数据段、堆栈段;

分段管理符合用户观点:可以独立考虑每个段(分治)

如何定位具体指令(数据)<断号,段内偏移>mov [es:bx],ax

操作系统也是一个进程,操作系统的描述符表,称为GDT=globle descriptor table;

每一个用户程序也是一个进程,也有各自的描述符表(段表),称为LDT=local descriptor table

堆栈段

 

1. 为函数内部的局部变量提供存储空间。

2. 进行函数调用时,存储“过程活动记录”。

3. 用作暂时存储区。如计算一个很长的算术表达式时,可以将部分计算结果压入堆栈。

数据段(静态存储区)

包括BSS段(Block Started by Symbol)的数据段。BSS段存储未初始化或初始化为0的全局变量、静态变量,具体体现为一个占位符,并不给该段的数据分配空间,只是记录数据所需空间的大小。数据段存储经过初始化的全局和静态变量。

代码段

又称为文本段。存储可执行文件的指令;也有可能包含一些只读的常数变量,例如字符串常量等。

  .rodata段:存放只读数据,比如printf语句中的格式字符串和开关语句的跳转表。也就是你所说的常量区。例如,全局作用域中的 const int ival = 10,ival存放在.rodata段;再如,函数局部作用域中的printf("Hello world %d\n", c);语句中的格式字符串"Hello world %d\n",也存放在.rodata段。

但是注意并不是所有的常量都是放在常量数据段的,其特殊情况如下:

1)有些立即数与指令编译在一起直接放在代码段。

2)对于字符串常量,编译器会去掉重复的常量,让程序的每个字符串常量只有一份。

内存分段(一个进程)

栈stack

由编译器自动分配、翻译。存放函数的参数值和局部变量值、操作方式类似于数据结构中的栈;

堆heap

由程序员分配、释放。如果程序员不释放,程序结束时有可能由操作系统释放。它和操作系统中的堆是两回事,操作方式类似于链表;

BSS

存放未初始化的全局变量和静态变量;

数据段data

存放初始化之后的全局变量和静态变量;

代码段code

程序代码主体,函数主体等。为二进制格式;

栈、堆辨析:

1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap):由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

项目

栈stack

堆heap

申请方式

系统自动分配

程序员分配,需指明大小

系统响应

只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出;

操作系统有一个记录空闲内存地址的链表,系统收到申请时,会遍历链表,找到第一个空间大于申请的堆结点;然后将该结点从空闲结点链表中删除,并将该结点分配给程序;

操作系统会在首地址处记录本次分配的大小,这样,代码中的delete语句才能正确释放内存;

由于找到的堆结点大小不一定正好等于申请大小,系统会将多余部分重新放入空闲列表;

申请大小

编译程序时,由编译器确定,是一个常数,Windows中一般为2M;因此在栈中获得的空间比较小;

系统用链表存储空闲内存地址,自然时不连续的,链表的遍历也是从低到高;堆的大小受限于计算机系统中有效的虚拟内存;

扩展方向

向低地址扩展

向高地址扩展

申请效率

系统自动分配,速度较快;程序员无法控制;

程序员分配内存,一般速度比较慢,而且容易产生内存碎片,不过用起来方便;

存储内容

函数调用时,第一个进栈的是下一条指令的地址,然后是函数参数;

大多数C编译器中,参数是从左到右入栈,然后函数中的局部变量入栈;

静态变量是不入栈的;

当函数调用结束,局部变量先出栈,然后是参数,最后 栈顶指针指向最快开始存的地址,也就是主函数中的吓一跳指令,程序由该点继续运行;

一般在堆的头部用一个字节存放堆的大小;

堆中具体内容由程序员安排;

GDT+LDT+偏移;

 

 

分页

内存如何分割:固定分区(等分)、可变分区(不等分);

以分配分区表,空闲分区表;需要多少,就割多少;

内存分配:首先适配(随机)最佳分配(挑最小的分配,产生细小空间),最差分配(挑最大的分配,分配比较均匀);

分页:解决内存分区导致的内存效率问题;

可变分区造成的问题:产生内存碎片;

解决:将空闲分区合并,需要移动一个段(复制内存)内存紧缩;

内存紧缩需要花费大量时间,不可取;

不产生面包碎屑;将面包切成片,将内存分成页;

将每个段内存请求,系统一页一页的分配给这个段;

内存需要分页,程序需要分段;最多浪费一页4k;

通过页表到内存中找;MMU内存管理单元,通过逻辑地址,寻找内存物理地址;

每个段都打散成很多段,相应的,内存也分割成很多页;

 

多级页表

为了提高内存空间利用率,页应该小,但是页小了,分的页就多了,页表就大了;

页表的作用:逻辑页(页号)→物理页(页框号)

页面尺寸通常4K,地址32位4G内存,有2^20个页面;

2^20个页表项都要存放在内存中,需要4M内存,系统并发10个进程,就需要40M内存;

实际上,很多逻辑地址根本不会用到;

将不使用的地址项从页表中删除,减小页表大小,减少内存浪费;

页表必须连续,否则要挨个查找,访问一次内存就要提前访问内存很多次,大大降低效率;

页表连续时,用页表起始页加上一个偏移量,就可以一次性查找到页表项;

既要连续,又要占用内存少;就要使用分级页表;

页目录+页表;每个页目录项对应1K个页表,需要4M内存;

多级页表,空间利用率好(节省内存),但需要增加访问次数,降低访问效率;

每增加一级,访问内存次数就要增加一次,但是内存占用会大大减少;

TLB快表,是一组相连快速存储,是一个寄存器;

记录正在使用的多个页表,再次访问时,可以一次直达对应地址;

快表相当于记住最近搜索项,或最近查找项;

快表+多级页表:节省内存、又很快;

TLB命中率有多少?要想TLB好使,命中率必须达到一定水平;

要想真正实现“近似访存1次”TLB命中应该很高;

TLB越大越好,但是TLB寄存器很贵,通常只有[64-1024];

程序地址访问存在局部性locality in space,就是说经常集中性访问一段内存;

TLB解决多级页表访问效率低的问题;

 

虚拟内存

程序分段(打散)→页表(虚拟内存)→物理地址;

虚拟内存实现了既支持分段又支持分页的内存视图;

段、页同时存在:段面向用户,页面向硬件;

虚拟内存将段和页紧密的结合在一起;

段号+偏移(CS:IP)→段表→段号+偏移→虚拟地址→页号+偏移→页表→物理页号+偏移

 

 

进程=正在进行的程序=执行中的程序

进程

程序的本质:数据和对数据的处理;

进程的本质:正在运行(执行/动态)的程序;

进程是操作系统进行资源化分配和调度的基本单位;

进程是应用程序关于某些数据集合上的一次运行活动;

进程是程序的一次执行过程;

进程是动态的,程序是动态的,执行的程序才是进程,没有执行的程序不是进程;

动态的程序才是进程,静态的程序不是进程;

每个进程拥有独立的地址空间;

进程之间的地址空间是隔离的;

一个进程崩溃不会影响另一个进程;一个进程崩溃不会影响操作系统;

地址空间包括代码段、数据段、堆栈段;

代码段:存储程序的代码;

数据段:存储程序的全局变量和动态分配的内存;

堆栈段:存储函数运行时的局部变量;

 

多进程

CPU计算速度和IO速度严重不匹配;5.7*10^5:1

导致CPU利用率很低;

多道程序,交替执行,可以大大提高CPU利用率;

一个CPU上交替执行多个程序:并发(同时出发,交替执行);

核心是如何在不同任务间进行切换;

而切换的核心就是中断技术;

切出去的时候要记录当前程序的执行状态,切回来的时候再读出当时的状态,以便接续执行;

进程:进行(执行)中的程序;

运行的程序和静态程序不一样。

需要描述这些不一样,都记录在PCB中;

PCB:process ctrl block(进程控制块),用来记录当前进程的当前状态;

在操作系统代码当中是一个结构体:struct task_struct{...}

Ø 进程有开始有结束,程序没有;

Ø 进程会走走停停,走停对程序没有意义;

Ø 进程需要记录ax、bx,程序不用;

多进程:多个程序交替执行,分别成为进行中的程序;

如何实现多进程图像:

1. 通过映射表实现内存的分离;

2. 读写PCB,OS中最重要的结构,贯穿始终;

3. 要操作寄存器完成切换;

4. 要写调度程序;

5. 要有进程同步和合作;

6. 要有地址映射(防止相互干扰);

 

进程=资源+指令执行序列;

线程:保留了并发的优点,避免了进程切换代价;

将资源和指令切换分开;

一个资源+多个指令执行序列;

线程:只切换指令,而不切换资源;

指令的切换的核心是Yield;

Create就是第一次切换时的样子;

用户级线程、内核级线程;

 

 

进程同步

同步:多个进程的推进合理有序;

进程之间的合作,合理有序的推荐,而不是任意向前推荐;

一个进程发出信号,另一个进程等到该信号,才能继续向下执行;

关键词:信号、合作、同步、等待、阻塞;

需要让进程走走停停,来保证多进程之间的合理有序;

分析走和停的实际,是多进程同步的关键;

n 同步并不是同时,而是交替执行;

n 交替执行不是随意的交替,而是合理的有序的交替;

程序可以有多个(多道),但是正在执行的程序只能有一个;

 

信号量

仅仅依靠一个counter信号,不足以支持进程的sleep和wakeup;

需要一个信息更加丰富的量,来记录信号;

通过信号量sem实现等待sleep与唤醒wakeup;

sem小于0,表示有进程在等待资源,生产者处于睡眠状态,唤醒;

信号量:1965年,由荷兰学者Dijkstra提出的一种特殊整形变量,用来sleep和wakeup;

semphore英 /ˈseməfɔː(r)/ 美 /ˈseməfɔːr/n. 信号标,信号量信号标信号对象

保护信号量:

竞争条件:race condition:和调度有关的共享数据语义错误;

错误由多个进程并发操作共享数据引起;

原子操作:一段代码只允许一个进程进入;

 

临界区:一次只允许一个进程进入的该进程的那一段代码;

找出临界区代码;

原子操作、互斥;

临界区代码保护原则:

①基本原则:互斥进入,如果一个进程在临界区中执行,其他进程不允许进入;

这些进程间的约束关系称为互斥 mutual exclusion;这保证了临界区;

好的临界区保护原则:

②有空让进:若干进程要求进入空闲临界区时,应尽快使一些进程进入临界区;

③有限等待:从进程发出进入请求到允许进入,不能无限等待;

解决竞争调价你的方法

值日算法(轮换算法)、买牛奶留标签算法(标签算法)、面包店算法(取号算法);

面包店算法的正确性:

n 互斥进入:Pi在临界区内,Pk试图进入,一定有(num[i],i)<(num[k],k),Pk循环等待;

n 有空进入:如果没有进程在临界区中,最小序号的进程一定能够进入;

n 有限等待:离开进阶区的进程再次进入一定排在最后(FIFO),所以任一个想进入进程至多等待n个进程;

软硬件协同设计;

关闭中断法:关闭中断cli=close interrupt,执行完临界区代码再开中断sti=start interrupt;

关闭中断,对于多CPU不好使;

硬件原子指令法:给一段指令上锁,锁的本质就是一个变量(整形变量),将变量赋值为某一个值,就锁住,执行完再赋值为另一个值,就解锁了;

原子指令,一条指令是原子的,要么被执行,要么没有执行,不可分割;

临界区指令:不能被中途打断的指令,

CPU调度

多个进程,应该选择哪个执行,这就是调度;

调度思想:

①按照先后顺序进行调度:

先来后到,谁先来,先执行谁;

②按照优先级进行调度:

任务短的可以适当优先;

调度目标:如何让进程满意,时间是关键,也就是快

n 尽快结束任务,周转时间短(从任务进入到任务结束)

n 用户操作尽快反应:响应时间短(从操作发生到响应)

n 系统内耗时间少:吞吐量(完成任务量)

如何做到合理?折中、综合考虑

n 吞吐量和响应时间之间有矛盾;

n 响应时间小→切换次数多→系统内耗大→吞吐量小;

n 前台任务和后台任务的关注点不同;

n 前台任务关注相应时间,后台任务关注周转时间;

n IO约束型任务和CPU约束型任务各有特点;

n IO优先级更高一些,更利于并行;

折中和综合让系统变得复杂,然而系统又要尽量简单;

 

调度算法

FCFS=First Come,First Served,先来先服务;体现公平;

SJF=short job first,短作业优先;周转时间最小;

RR=round Robin按时间片轮转调度;

时间片大:响应时间太长;时间片小,吞吐量小;折中:时间片10-100ms切换时间0.1-1ms(1%);

定义前台任务和后台任务两队列,前台RR,后台SJF,没有前台任务时才调度后台任务;

后台任务优先级动态升高,防止饥饿;执行一段时间后,必须再调低,防止前台任务响应迟钝;

 

调度函数schedule()

 

fork

fork英 /fɔːk/ 美 /fɔːrk/

n. 餐叉,叉子;耙,叉;岔路,支流;叉状部件,叉状物;

v. (道路、河流等)分岔,岔开;(用餐叉)叉起;耙地,叉掘;走岔路中的一条;

 

fork进程控制函数,叉子函数、分叉函数;形象的表示进程之间的切换;

作用: 创建新进程;函数原型: pid_t fork(void);需要包含头文件 <unistd.h>

返回值:成功,两次返回(子进程返回 0,父进程返回子进程 ID);若出错,返回 -1

pid_t getpid(void);获取当前进程id;get process id

pid_t getppid(void);获取当前进程的父进程id;get parent process id

fork 的一个特性是父进程所有打开的文件描述符都被复制到子进程中。

父进程和子进程每个相同的打开描述符共享一个文件表项;

父进程和子进程共享同一个文件偏移量;

在 fork 之后处理文件描述符有以下两种常见的情况。

(1) 父进程等待子进程完成,在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它曾进行过读、写操作的任一共享描述符的偏移量已做了相应更新。

(2) 父进程和子进程各自执行不同的程序段,在这种情况下,在 fork 之后,父进程和子进程各自关闭他们不需使用的文件描述符,这样就不会干扰对方使用的文件描述符。

————————————————

fork 失败的两个主要原因是:

(1) 系统中已经有太多的进程

(2) 该实际用户 ID 的进程总数超过了系统限制,CHILD_MAX 规定了每个实际用户 ID 在任一时刻可拥有的最大进程数。

fork 的用法

(1) 一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段。

(2) 一个进程要执行不同的程序,在这种情况下,子进程从 fork 返回之后立即调用 exec 系列

 

操作系统

 

操作系统概述

 

BIOS是英文"Basic Input Output System"的缩略词,就是"基本输入输出系统"。

BIOS是个人电脑启动时加载的第一个软件。

操作系统是计算机硬件和应用之间的一层软件;

操作系统是管理硬件的软件;

方便我们使用硬件,如显存;

高效使用硬件,如开多个端口;

没有操作系统的计算机叫做裸机;因此操作系统相当于计算机的衣服;

 

CPU管理、内存管理、终端管理、磁盘管理、文件管理、网络管理、电源管理、多核管理;

操作系统

应用软件

操作系统

计算机硬件

 

启动过程

1. 刚开机时CPU处于实模式

2. 开机时,CS=0xFFFF,IP=0x0000

3. 寻址0xFFFF0(ROMBIOS映射区)

4. 检查RAM、键盘、显示器、磁盘

5. 将磁盘0磁道0扇区读入0x7c00处

6. 设置cs=0x7c00,ip=0x0000

 

操作系统接口

POSIX:Portable Operating System Interface of Unix(IEEE制定的一个标准族)

接口表现为函数调用,又由系统系统,所以称为系统调用;

分类

POSIX定义

描述

任务管理

fork

创建一个进程

execl

运行一个可执行程序

pthread_create

创建一个线程

文件系统

open

打开一个文件或目录

EACCES

返回值,表示没有权限

mode_t st_mode

文件头结构:文件属性;

 

系统调用

主动进入内核的方法:中断指令int(interrupt)int 0x80

n int指令将使CS红的CPL改成0,以进入内核

n 这是用户程序发起的调用内核代码的唯一方式;

系统调用的核心

① 用户程序红包含一段包含int指令的代码

② 操作系统写中断处理,获取想要调用程序的编号;

③ 操作系统根据编号执行相应代码

 

将内核程序和用户程序隔离

区分内核态和用户态由于CS:IP是当前指令,所以用CS最低两位来表示:0是内核态,3是用户态;

内核态可以访问任何数据,用户态不能访问内核数据;对于指令跳转也一样,实现了隔离;

n 获取特权级:访问的数据段DS的最低两位,CPL(CS)RPL(RS)DPL (privilege level)

n 特权级判定:如果DPL>=CPL DPL>=RPL才能访问;

 

操作系统历史

①IBMSYS(1955-1965)

监控系统:实现任务批处理

②OS/360:(1965-1980)

360表示全方位服务,开发周期5000人年

需要让一台计算机干多种事

多道程序multiprogramming

作业之间的切换和调度称为核心,既有IO任务又有计算任务,需要让计算机CPU忙碌

③MULTICS多人使用(1965-1980)

每个人启动一个作业,作业之间快速切换

分时系统timesharing

代表:MIT MULTICS(MULTIplexed Information and Computer Service)

核心仍然是任务切换,但是资源复用思想对操作系统影响很大,虚拟内存就是一种复用;

④Unix(1980-1990)

Unix是一个简化的MULTICS

⑤Linux(1990-2000)

多进程结构是计算机的基本图谱;

⑥DOS:CPM QDOS MSDOS

Basic解释器

FAT管理磁盘

⑦Windows 可视化

n 多进程图谱ProcessView(CPU、内存);

n 文件操作视图FileView(IO、磁盘);

特权级=PL=Privilege Level

描述符特权级(DPL,Descriptor Privilege Level)

 

实施特权级保护的第一步,是为所有可管理的对象赋予一个特权级,以决定谁能访问它们。每个 Descriptor 都具有描述符特权级(DPL,Descriptor Privilege Level)字段,Descriptor 总是指向它所“描述”的目标对象,代表着该对象,因此该字段(DPL)实际上是目标对象的特权级。

 

当前特权级(CPL,Current Privilege Level)

 

当处理器正在一个代码段中取指令和执行指令时,那个代码段的特权级叫做当前特权级(Current Privilege Level, CPL)。 正在执行的这个代码段,其选择子位于段寄存器CS中,其最低两位就是当前特权级的数值。

请求特权级(RPL,Request Privilege Level)

 

RPL也就是指请求者的特权级别(Requestor’s Privilege Level)。

特权指令(PI,Privileged Instructions)

 

不同特权级别的程序,所担负的职责以及在系统中扮演的角色是不一样的。计算机系统的脆弱性在于一条指令就能改变它的整体运行状态,比如停机指令hlt 和对控制寄存器CRO的写操作,像这样的指令只能由最高特权级别的程序来做。因此,那些只有在当前特权级 CPL 为 0 时才能执行的指令,称为特权指令(Privileged Instructions)。

输出特权级(IOPL,I/O Privilege Level)

 

在处理器的标志寄存器EFLAGS中,位13、位12是IOPL位,也就是输入/输出特权级(I/O Privilege Level),它代表着当前任务的I/O特权级别。某些指令,例如IN,OUT,CLI需要 I/O 特权,这些操作根据 IOPL 和 CPL 确定合法性。

内存分段的意义

  • 在多任务环境下,系统中有多个程序在运行;
  • 程序之间需要隔离!
  • 分段是存储管理的一种方式,为保护提供基础;
  • 不同程序在不同段中;
  • 一个程序可以包含多个段;
  • 段用于封闭具有共同属性的存储区域;

 

,,

 

posted on 2022-09-06 09:41  张德长  阅读(122)  评论(0编辑  收藏  举报