一,操作系统接口interface

操作系统向用户提供了“用户与操作系统的接口”,以支持用户和操作系统之间进行交互。用户与操作系统的接口通常是由“命令”和“系统调用”的形式表现出来的。接口起到连接两个层次,实现信号转换,屏蔽信息细节的作用。

操作系统学习笔记(二)_子进程

 

 操作系统学习笔记(二)_数据_02

 如此一来便实现了操作系统的使用,这些重要的函数由操作系统提供,接口表现为函数调用,又称为系统调用。系统调用就是操作系统接口。比如以下函数都是操作系统接口:

操作系统学习笔记(二)_子进程_03

 操作系统的接口函数有很多,没必要都记住,但是要学会怎么去查找。

操作系统学习笔记(二)_父进程_04

 遇到需要就去查看POSIX手册即可。官网:https://pubs.opengroup.org/onlinepubs/7908799/index.html

二,内核态和用户态

  计算机提供了一种硬件机制,将内存分为两段不同的区域——内核段(即计算机加载完操作系统之后将操作系统从硬盘中转移到内存0地址开始的那一段)和用户段,内核态的数据在这时DPL为0,若当前的调用CPL<DPL,才可以成功调取内核段中的数据。这就阻止了用户程序胡乱调用操作系统中十分重要的数据,因为用户程序使用调用机制时CPL为3,是不符合CPL<DPL的。

操作系统学习笔记(二)_父进程_05

  CPL是当前进程的权限级别(Current Privilege Level),是当前正在执行的代码所在的段的特权级,存在于cs寄存器的低两位。
  RPL说明的是进程对段访问的请求权限(Request Privilege Level),是对于段选择子而言的,每个段选择子有自己的RPL,它说明的是进程对段访问的请求权限,有点像函数参数。而且RPL对每个段来说不是固定的,两次访问同一段时的RPL可以不同。RPL可能会削弱CPL的作用,例如当前CPL=0的进程要访问一个数据段,它把段选择符中的RPL设为3,这样虽然它对该段仍然只有特权为3的访问权限。
  DPL存储在段描述符中,规定访问该段的权限级别(Descriptor Privilege Level),每个段的DPL固定。当进程访问一个段时,需要进程特权级检查,一般要求DPL >= max {CPL, RPL}

 

三,中断是进入内核的唯一方法

  那么如果用户程序是如何通过系统调用实现用户段和内核段之间的联系的呢?

操作系统学习笔记(二)_子进程_06 

操作系统学习笔记(二)_父进程_07

 

以用户态的printf为例,首先由库函数将printf转换为带有中断指令的代码。调用中断时,将DPL变为3,进入内核态之后,CPL变成了0,这时CPL<DPL,便调用system_call,由system_call调用call_table去查表,查到相对应的系统调用号,根据这个调用号才去执行真正要去执行相对应的系统调用,pringtf为例是由调用号4最终去调用了sys_write。

 操作系统学习笔记(二)_系统调用_08

 四,操作系统核心的两点在于多进程管理和文件管理

1)多进程管理

如果在cpu在工作时单道程序执行,由于外设的速度远远低于cpu的工作速度,这样会使cpu的效率大大降低,使其在大部分是时间都在等外外设准备好文件数据。这里参考程序查询方式。如果在一道程序之中外设还没准备好的时候cpu切换出去执行其他的程序,然后在切换出去执行的这一道程序又出现要等待外设准备数据的情况时再进行cpu的工作切换。这样就实现了多道程序交替执行,大大提高了cpu的工作效率。

操作系统学习笔记(二)_操作系统OS_09

 

像这样一个CPU上交替执行多个程序称为并发。那么怎么做到并发呢?由于cpu是取指执行的,而这一行动靠的是PC指针,由它从内存中取指令和数据。所以并发的关键是“切换PC指针从而跳转执行其它程序。”。但是由于只是暂时跳开执行其它的程序而提高cpu效率,而不是不再执行原有的程序,所以要保护现场,也就是要能够保留切换出去之前的程序原来的样子,包括pc和相应的寄存器。完成“保护现场”这一工作的是进程控制块PCB(process control block),以记录每一个运行中的程序的状态。

PCB:为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块PCB (Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤消而撤消。

操作系统学习笔记(二)_数据_10

 

操作系统学习笔记(二)_父进程_11

 

操作系统的开启初始化过程中执行到main程序时有一段fork()指令如下,完成了桌面的启动:

 操作系统学习笔记(二)_操作系统OS_12

 

 操作系统学习笔记(二)_操作系统OS_13

 

 所以main是0号进程,init是1号进程。

操作系统学习笔记(二)_数据_14

 

 1号进程init()一直在执行。

fork():在fork函数执行完毕后,如果创建新进程失败,则返回-1;如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

进程的状态有五种:

操作系统学习笔记(二)_操作系统OS_15

 

 正在运行的程序遇到突发事件比如错误,或者需要等待外设时就要切换当前进程到阻塞态,然后调用schedule()函数完成进程替代。Schedule()函数最主要作用就是从就绪进程中选择一个优先级最高的进程来代替当前进程运行。 

操作系统学习笔记(二)_操作系统OS_16getnext()是调度函数,涉及到优先级的设计,比如FIFO策略,除此之外还有很多的复杂算法。

 

比如:pCur进程需要等待外设准备数据,就将其转换成等待状态,然后调用schedule()。

操作系统学习笔记(二)_数据_17

 

 

操作系统学习笔记(二)_操作系统OS_18  操作系统学习笔记(二)_数据_19

 

 

2)文件管理