9.5.UNIX系统调用
9.5.1 进程控制
1.进程的创建和终止
(1) 创建进程(fork)
(2) 终止进程(exit)
2.改变进程映像和等待
(1) 执行一个文件(exec)
(2) 等待子进程结束(wait)
3.其它进程调用
(1) 获得进程ID
(2) 获得用户ID
(3) 进程暂停(pause)
9.5.2 文件操纵
1.文件的创建和删除
2.文件的打开和关闭
3.文件的读和写
4.建立与文件的连接和去连接
9.5.3 进程通信和信息保护
1.进程通信
为了实现进程间的通信,在UNIX系统中提供了一个用于进程间通信的软件包,简称IPC。它由消息机制、共享存储器机制和信号量机制三部分组成。在每一种通信机制中,都提供了相应的系统调用供用户程序进行进程间的同步与通信用。
2.信息维护
(1) 设置和获得时间
(2) 获得进程和子进程时间(times)
(3) 设置文件访问和修改时间(utime)
(4) 获得当前UNIX系统的名称(uname)
9.6.系统调用的实现
系统调用的实现与一般过程调用的实现相比,两者间有很大差异。对于系统调用,控制是由原来的用户态转换为系统态,这是借助于陷入机制来完成的,在该机制中包括陷入硬件机构及陷入处理程序两部分。当应用程序使用OS的系统调用时,产生一条相应的指令,CPU在执行这条指令时发生中断,并将有关信号送给中断和陷入硬件机构,该机构收到信号后,启动相关的陷入处理程序进行处理,实现该系统调用所需要的功能。
9.6.1 系统调用的实现方法
1.系统调用号和参数设置
往往在一个系统中设置了许多条系统调用,并赋予每条系统调用一个唯一的系统调用号。在系统调用命令(陷入指令)中把相应的系统调用号传递给中断和陷入机制的方法有很多,在有的系统中,直接把系统调用号放在系统调用命令(陷入指令)中;在另一些系统中,则将系统调用号装入某指定寄存器或内存单元中。
每一条系统调用都含有若干个参数,在执行系统调用时,如何设置系统调用所需的参数,即如何将这些参数传递给陷入处理机构和系统内部的子程序(过程),常用的实现方式有以下几种:
(1) 陷入指令自带方式。
(2) 直接将参数送入相应的寄存器中。
(3) 参数表方式。
2.系统调用的处理步骤
系统调用的处理过程可分成以下三步:
首先,将处理机状态由用户态转为系统态;之后,由硬件和内核程序进行系统调用的一般性处理,即首先保护被中断进程的CPU环境,将处理机状态字PSW、程序计数器PC、系统调用号、用户栈指针以及通用寄存器内容等压入堆栈;然后,将用户定义的参数传送到指定的地址并保存起来。
其次,分析系统调用类型,转入相应的系统调用处理子程序。为使不同的系统调用能方便地转向相应的系统调用处理子程序,在系统中配置了一张系统调用入口表。表中的每个表目都对应一条系统调用,其中包含该系统调用自带参数的数目、系统调用处理子程序的入口地址等。因此,核心可利用系统调用号去查找该表,即可找到相应处理子程序的入口地址而转去执行它。
最后,在系统调用处理子程序执行完后,应恢复被中断的或设置新进程的CPU现场,然后返回被中断进程或新进程,继续往下执行。
3.系统调用处理子程序的处理过程
系统调用的功能主要是由系统调用子程序来完成的。对于不同的系统调用,其处理程序将执行不同的功能。我们以一条在文件操纵中常用的Creat命令为了来说明。
进入Creat的处理子程序后,核心将根据用户给定的文件路径名Path,利用目录检索过程去查找指定文件的目录项。查找目录的方式可以用顺序查找法,也可用Hash查找法。如果在文件目录中找到了指定文件的目录项,表示用户要利用一个已有文件来建立一个新文件。但如果在该已有文件的属性中有不允许写属性,或者创建者不具有对该文件进行修改的权限,便认为是出错而做出错处理;若不存在访问权限问题,便将已存文件的数据盘块释放掉,准备写入新的数据文件。如果未找到指明文件,则表示要创建一个新文件,核心便从其目录中找出一个空目录项,并初始化该目录项,包括填写文件名、文件属性、文件建立日期等,然后将新文件打开。
9.6.2 UNIX系统调用的实现
在UNIX系统V的内核程序中,有一个trap.S文件,它是中断和陷入总控程序。该程序用于中断和陷入的一般性处理。为提供运行效率,该文件采用汇编语言编写。由于在trap.S中包含了绝大部分的中断和陷入向量的入口地址,因此,每当系统发生了中断和陷入情况时,通常都是先进入trap.S程序,由它先处理有关CPU环境保护的问题。
另外还有一个处理各种陷入情况的C语言文件,即trap.C程序,共有12种陷入的处理要调用trap.C程序(如系统调用、进程调用中断、跟踪自陷非法指令、访问违章、算术自陷等)用于处理在中断和陷入发生后需要处理的若干公共问题。如果因系统调用进入trap.C,它所要进行的处理将包括:确定系统调用号、实现参数传送、转入相应的系统调用处理子程序。在由系统调用处理子程序返回到trap.C后,重新计算进程的优先级,对收到的信号进行处理等。
1.CPU环境保护
当用户程序在用户态,且在执行系统调用命令(即CHMK命令)之前,应在用户空间提供系统调用所需的参数表,并将该参数表的地址送入R0寄存器。在执行CHMK命令后,处理机将由用户态转为核心态,并由硬件自动地将处理机状态长字(PSL)、程序计数器(PC)和代码操作数(code)压入用户核心栈,继而从中断和陷入向量表中取出trap.S的入口地址,然后便转入中断和陷入总控程序trap.S中执行。
trap.S程序执行后,继续将陷入类型type和用户栈指针usp压入用户核心栈,接着还要将被中断进程的CPU环境中的一系列寄存器如R0 ~ R11的部分或全部压入栈中。至于哪些寄存器的内容要压入栈中,这取决于特定寄存器中的屏蔽码,该屏蔽码的每一位都与R0 ~ R11中的一个寄存器相对应。当某一位置成1时,表示对应寄存器的内容应压入栈中。
2.AP和FP指针
为实现系统调用的嵌套使用,在系统中还设置了两个指针,其一是系统调用参数表指针AP,用于指示正在执行的系统调用所需参数表的地址,通常是把该地址放在某个寄存器中;再者,还须设置一个调用栈帧指针。所谓调用栈帧(或简称栈帧),是指每个系统调用需要保存而被压入用户核心栈的所有数据项;而栈帧指针FP则是用于指示本次系统调用所保存的数据项。每当出现新的系统调用时,还须将AP和FP303压入栈中,如下图示出了在trap.S总控程序执行后用户核心栈的情况。
3.确定系统调用号
由上述得知,在中断和陷入发生后,应先经硬件陷入机构予以处理,再进入中断和陷入总控程序trap.S,在保护好CPU现场后再调用trap.C继续处理。
4.参数传送
参数传送是指由trap.C程序将系统调用参数表中的内容从用户区传送到User结构的U.U-arg中,供系统调用处理程序使用。
5.利用系统调用定义表转入相应的处理程序
在UNIX系统中,对于不同(编号)的系统调用,都设置了与之相应的处理子程序。为使不同的系统调用能方便地转入其相应的处理子程序,也将各处理子程序的入口地址放入了系统调用定义表即Sysent[]中。该表实际上是一个结构数组,在每个结构中包含三个元素,其中第一个元素是相应系统调用所需参数的个数;第二个元素是系统调用经寄存器传送的参数个数;第三个元素是相应系统调用处理子程序的入口地址。在系统中设置了该表后,便可根据系统调用号i从系统调用定义表中找出相应的表目,再按照表目中的入口地址转入相应的处理子程序,由该程序去完成相应系统调用的特定功能。在该子程序执行完后,仍返回到中断和陷入总控程序中的trap.C程序中,去完成返回到断点前的公共处理部分。
6.系统调用返回前的公共处理
在UNIX系统中,进程调度的主要依据是进程的动态优先级。随着进程执行时间的加长,其优先级将逐步降低。每当执行了系统调用命令并由系统调用处理子程序返回到trap.C后,都将重新计算该进程的优先级;另外,在系统调用执行过程中,若发生了错误使进程无法继续运行时,系统会设置再调度标志。处理子程序在计算进程的优先级后,又去检查该再调度标志是否已又被设置。若已设置,便调用switch调度程序,再去从所有的就绪进程中选择优先级最高的进程,把处理机让给该进程去运行。
9.6.3 Linux系统调用
与UNIX相似,Linux采用类似技术实现系统调用。
Linux的系统调用号就是系统调用入口表中位置的序号。所有系统调用通过接口函数将系统调用号传给内核,内核转入系统调用控制程序,再通过调用号位置来定位核心函数。
系统调用控制程序的工作流程:①去系统调用号,检验合法性;②执行int 80h产生中断;③进行地址空间的转换,以及堆栈的切换,进入内核态;④进行中断处理,根据系统调用号定位内核函数地址;⑤根据通用寄存器内容,从用户栈中取入口参数;⑥核心函数执行,把结果返回应用程序。
9.6.4 Win32的应用程序接口
首先需要说明的是应用程序接口(API)与系统调用的区别和联系。API是一个函数的定义,说明如何获得一个给定的服务,而系统调用是通过中断向内核发出的一个请求。一个API函数可能不与任何系统调用相对应,也可以调用若干个系统调用,不同的API函数可能装了相同的系统调用。
Windows系统在程序设计模式上与UNIX以及Linux系统有着根本的不同。Windows程序采用的是事件驱动方式,即主程序等待事件的发生,如鼠标的点击、键盘的敲击或一个USB部件的插入等,然后根据事件内容,调用相应的程序进行处理。因此,在Windows系统中,定义了一系列的程序,称为Win32 API(Application Programming interface),用来提供操作系统的服务。
在Windows系统中,通过Kernel、User和GUI三个组件来支持API。Kernel包含了大多数操作系统函数,如内存管理、进程管理;User集中了窗口管理函数,如窗口创建、撤销、移动、对话及各种相关函数;GUI提供画图函数、打印函数。所有应用程序都共享这三个模块的代码,每个Windows的API函数都可通过名字来访问。具体做法是,在应用程序中使用函数名,并用适当的函数库进行编译和链接,然后,应用程序便可运行。实际上,Windows将三个组件置于动态链接库DDL(Dynamic Link Library)中。
————————————————