OS- -系统调用

OS- -系统调用

一、系统调用

  • 我们已经可以看到操作系统提供了两种功能:为用户提供应用程序抽象和管理计算机资源。
  • 对于大部分 在应用程序和操作系统之间的交互主要是应用程序的抽象,例如创建、写入、读取和删除文件。计算机 的资源管理对用户来说基本上是透明
  • 因此,用户程序和操作系统之间的接口主要是处理抽象。为了 真正理解操作系统的行为,我们必须仔细的分析这个接口。
  • 多数现代操作系统都有功能相同但是细节不同的系统调用,引发操作系统的调用依赖于计算机自身的机 制,而且必须用汇编代码表达。
  • 任何单CPU计算机一次执行执行一条指令。如果一个进程在用户态下 运行用户程序,例如从文件中读取数据。那么如果想要把控制权交给操作系统控制,那么必须执行一个 异常指令或者系统调用指令。
  • 操作系统紧接着需要参数检查找出所需要的调用进程。操作系统紧接着进 行参数检查找出所需要的调用进程。然后执行系统调用,把控制权移交给系统调用下面的指令。
  • 大致来 说,系统调用就像是执行了一个特殊的过程调用,但是只有系统调用能够进入内核态而过程调用则不能 进入内核态
  • 为了能够了解具体的调用过程,下面我们以read方法为例来看一下调用过程。
  • 像上面提到的那样, 会有三个参数,第一个参数是指定文件、第二个是指向缓冲区、第三个参数是给定需要读取的字节数。
  • 就像几乎所有系统调用一样,它通过使用与系统调用相同的名称来调用一个函数库,从而从C程序中调 用:read。
count = read(fd,buffer,nbytes);
  • 系统调用在count中返回实际读出的字节数。这个值通常与nbytes相同,但也可能更小。比如在读过 程中遇到了文件尾的情况。

  • 如果系统调用不能执行,不管是因为无效的参数还是磁盘错误,count的值都会被置成-1,然后在全局 变量errno中放入错误信号。程序应该进场检查系统调用的结果以了解是否出错。

  • 系统调用是通过一系列的步骤实现的,为了更清楚的说明这个概念,我们还以read调用为例,在准备 系统调用前,首先会把参数压入堆栈,如下所示
    在这里插入图片描述

  • 完成系统调用read(fd, buffer, nbytes)的11个步骤

  • C和C++编译器使用逆序传参,放在堆栈的顶部)。

  • 第一个 参数和第三个参数都是值调用,但是第二个参数通过引用传递,即传递的是缓冲区的地址(由&指 示),而不是缓冲的内容。

  • 然后是C调用系统库的read函数,这也是第四步。

  • 在由汇编语言写成的库过程中,一般把系统调用的编号放在操作系统所期望的地方,如寄存器(第五 步)。

  • 然后执行一个TRAP指令将用户态切换到内核态,并在内核中的一个固定地址开始执行第六 步

  • TRAP指令实际上与过程调用指令非常相似,它们后面都跟随一个来自远处位置的指令,以及供以 后使用的一个保存在栈中的返回地址。

TRAP指令与过程调用指令存在两个方面的不同:

  • TRAP指令会改变操作系统的状态,由用户态切换到内核态,而过程调用不改变模式
  • •其次,TRAP指令不能跳转到任意地址上。根据机器的体系结构,要么跳转到一个单固定地址上,或者指令中有一8位长的字段,它给定了内存中一张表格的索引,这张表格中含有跳转地址,然 后跳转到指定地址上。
  • 跟随在TRAP指令后的内核代码开始检查系统调用编号,然后dispatch给正确的系统调用处理器, 这通常是通过一张由系统调用编号所引用的、指向系统调用处理器的指针表来完成第七步。
  • 此时,系统 调用处理器运行第八步
  • 一旦系统调用处理器完成工作,控制权会根据TRAP指令后面的指令中返回给 函数调用库第九步。
  • 这个过程接着以通常的过程调用返回的方式,返回到客户应用程序,这是第十步。
  • 然后调用完成后,操作系统还必须清除用户堆栈,然后增加堆栈指针(increment stackpointer), 用来清除调用read之前压入的参数。

从而完成整个read调用过程。

  • 在上面的第九步中我们说道,控制可能返回TRAP指令后面的指令,把控制权再移交给调用者这个过程 中,系统调用会发生阻塞,从而避免应用程序继续执行。
  • 这么做是有原因的。例如,如果试图读键盘, 此时并没有任何输入,那么调用者就必须被阻塞。
  • 在这种情形下,操作系统会检查是否有其他可以运行 的进程。这样,当有用户输入时候,进程会提醒操作系统,然后返回第9步继续运行。

下面,我们会列出一些常用的POSIX系统调用,POSIX系统调用大概有100多个,它们之中最重要 的一些调用见下表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 上面的系统调用参数中有一些公共部分,例如pid系统进程id, fd是文件描述符,n是字节数, position是在文件中的偏移量、seconds是流逝时间。
  • 从宏观角度上看,这些系统调所提供的服务确定了多数操作系统应该具有的功能,下面分别来对不同的 系统调用进行解释

用于进程管理的系统调用

  • 在UNIX中,fork是唯一可以在POSIX中创建进程的途径,它创建一个原有进程的副本,包括所有 的文件描述符、寄存器等内容。

  • 在fork之后,原有进程以及副本(父与子)就分开了。在fork过程 中,所有的变量都有相同的值,虽然父进程的数据通过复制给子进程,但是后续对其中任何一个进程的 修改不会影响到另外一个

  • fork调用会返回一个值,在子进程中该值为0,并且在父进程中等于子进程 的进程标识符(Process IDentified,PID) ,使用返回的PID,就可以看出来哪个是父进程和子进 程。
    在这里插入图片描述

  • 在多数情况下,在fork之后,子进程需要执行和父进程不一样的代码

  • 从终端读取命令,创建一个子 进程,等待子进程执行命令,当子进程结束后再读取下一个输入的指令。

  • 为了等待子进程完成,父进程 需要执行waitpid系统调用,父进程会等待直至子进程终止(若有多个子进程的话,则直至任何一 个子进程终止)。

  • waitpid可以等待一个特定的子进程,或者通过将第一个参数设为-1的方式,等待任 何一个比较老的子进程。

  • 当waitpid完成后,会将第二个参数statloc所指向的地址设置为子进程的 退出状态(正常或异常终止以及退出值)。有各种可使用的选项,它们由第三个参数确定。

  • 例如,如果 没有已经退出的子进程则立刻返回。

  • 那么shell该如何使用fork呢?在键入一条命令后,shell会调用fork命令创建一个新的进程。这个子 进程会执行用户的指令

  • 通过使用execve系统调用可以实现系统执行,这个系统调用会引起整个核 心映像被一个文件所替代,该文件由第一个参数给定。

下面是一个简化版的例子说明fork、waitpid和 execve的使用:
在这里插入图片描述
在这里插入图片描述

  • —般情况下,execve有三个参数:将要执行的文件名称,一个指向变量数组的指针,以及一个指向环 境数组的指针。
  • 先看一个shell指令
cp filel file2
  • 此命令把filel复制到file2文件中,在shell执行fork之后,子进程定位并执行文件拷贝,并将源文件 和目标文件的名称传递给它。
  • cp的主程序(以及包含其他大多数C程序的主程序)包含声明
main(argc,argv,envp)
  • 其中第一个参 数argc是命令行中参数数目的计数,包括程序名称。
  • 对于上面的例子,argc是3。
  • 第二个参 数argv是数组的指针。该数组的元素i是指向该命令行第i个字符串的指针。在上面的例子中, argv[0]指向字符串cp, argv[1]指向字符串filel, argv[2]指向字符串file2。
  • main的第三个参数是指向 环境的指针,该环境是一个数组,含有name = value的赋值形式,用以将诸如终端类型以及根目录 等信息传送给程序。这些变量通常用来确定用户希望如何完成特定的任务(例如,使用默认打印机)。
  • 在上面的例子中,没有环境参数传递给execve ,所以环境变量是0 ,所以execve的第三个参数为0
  • 可能你觉得execve过于复杂,这时候我要鼓励一下你,execve可能是POSIX的全部系统调用中最复 杂的一个了,其他都比较简单。
  • 作为一个简单的例子,我们再来看一下exit ,这是进程在执行完成 后应执行的系统调用。这个系统调用有一个参数,它的退出状态是0 - 255之间,它通过waitpid系统 调用中的statloc返回给父级。
  • UNIX中的进程将内存划分成三个部分:text segment,文本区,例如程序代码,data segment, 数据区,例如变量,stack segment ,栈区域。数据向上增长而堆栈向下增长,如下图所示
    在这里插入图片描述
  • 上图能说明三个部分的内存分配情况,夹在中间的是空闲区,也就是未分配的区域,堆栈在需要时自动 的挤压空闲区域,不过数据段的扩展是显示地通过系统调用brk进行的,在数据段扩充后,该系统调 用指向一个新地址。
  • 但是,这个调用不是POSIX标准中定义的,对于存储器的动态分配,鼓励程序员 使用malloc函数,而malloc的内部实现则不是一个适合标准化的主题,因为几乎没有程序员直接 使用它。

用于文件管理的系统调用

  • 许多系统调用都与文件系统有关,要读写一个文件,必须先将其打开。

  • 这个系统调用通过绝对路径名或 指向工作目录的相对路径名指定要打开文件的名称,而代码O-RDONLY、 O-WRONLY或O_RDWR 的含义分别是只读、只写或者两者都可以,为了创建一个新文件,使用O-CREATE参数。

  • 然后可使用 返回的文件描述符进行读写操作。接着,可以使用close关闭文件,这个调用使得文件描述符在后续的 open中被再次使用。

  • 最常用的调用还是read和write ,我们再前面探讨过read调用,write具有与read相同的参 数。

  • 尽管多数程序频繁的读写文件,但是仍有一些应用程序需要能够随机访问一个文件的任意部分。

  • 与每个 文件相关的是一个指向文件当前位置的指针。

  • 在顺序读写时,该指针通常指向要读出(写入)的下一个 字节。Iseek调用可以改变该位置指针的值,这样后续的read或write调用就可以在文件的任何地 方开始。

  • Iseek有三个参数,position = iseek(fd,offset,whence),第一个是文件描述符,第二个是文 件位置,第三个是说明该文件位置是相对于文件起始位置,当前位置还是文件的结尾。

  • 在修改了指针之 后,Iseek所返回的值是文件中的绝对位置。

  • UNIX为每个文件保存了该文件的类型(普通文件、特殊文件、目录等)、大小,最后修改时间以及其他信息,程序可以通过stat系统调用查看这些信息。

  • s = stat(name,&buf),第一个参数指定了 被检查的文件;第二个参数是一个指针,该指针指向存放这些信息的结构。对于一个打开的文件而言, fstat调用完成同样的工作。

用于目录管理的系统调用

  • 下面我们探讨目录和整个文件系统的系统调用,上面探讨的是和某个文件有关的系统调用。
  • mkdir和 rmdir分别用于创建s = mkdir(nname,mode)和删除s = rmdir(name)空目录
  • 下一个调用是s = link(namel,name2)它的作用是允许同一个文件以两个或者多个名称出现,多数情况下是在 不同的目录中使用link

下面我们探讨一下link是如何工作的
在这里插入图片描述

  • 图中有两个用户ast和jim ,每个用户都有他自己的一个目录和一些文件,如果ast要执行一个包 含下面系统调用的应用程序
link("/usr/jim/memo", "/usr/ast/note";
  • jim中的memo文件现在会进入到ast的目录中,在note名称下。
  • 此后,/usr/jim/memo和 /usr/ast/note会有相同的名称。
  • 用户目录是保存在/usr, /user, /home还是其他位置,都是由本地系统管理员决定的。
  • 要理解link是如何工作的需要清楚link做了什么操作。
  • UNIX中的每个文件都有一个独一无二的版本, 也称作i - number, i-编号,它标示着不同文件的版本。这个i -编号是i-nodes,i-节点 表的索 引。
  • 每个文件都会表明谁拥有这个文件,这个磁盘块的位置在哪,等等。目录只是一个包含一组(i编 号,ASCII名称)对应的文件
  • UNIX中的第一个版本中,每个目录项都会有16个字节,2个字节对应i -编号和14个字节对应其名称。现在需要一个更复杂的结构需要支持长文件名,但是从概念上讲一个 目录仍是一系列(i-编号,ASCII名称)的集合。
  • 在上图中,mail的i-编号为16,依此类推。link只 是利用某个已有文件的i-编号,创建一个新目录项(也许用一个新名称)。
  • 在上图b中,你会发现有两 个相同的70 i-编号的文件,因此它们需要有相同的文件。
  • 如果其中一个使用了 unlink系统调用的 话,其中—个会被移除,另一个将保留。如果两个文件都移除了,则UNIX会发现该文件不存在任何没 有目录项(i-节点中的一个域记录着指向该文件的目录项),就会把该文件从磁盘中移除。
  • 就像我们上面提到过的那样,mount系统s = mount(special,name,flag)调用会将两个文件系 统合并为一个。
  • 通常的情况是将根文件系统分布在硬盘(子)分区上,并将用户文件分布在另一个子)分区上,该根文件系统包含常用命令的二进制(可执行)版本和其他使用频繁的文件。然后,用 户就会插入可读取的USB硬盘。

通过执行mount系统调用,USB文件系统可以被添加到根文件系统中,

在这里插入图片描述

  • 如果用C语言来执行那就是
mount("/dev/sclb0", "/Yrnnt" ,0
  • 这里,第一个参数是USB驱动器0的块特殊文件名称,第二个参数是被安装在树中的位置,第三个参 数说明将要安装的文件系统是可读写的还是只读的。
  • 当不再需要一个文件系统时,可以使用amount移除之。

其他系统调用

  • 除了进程、文件、目录系统调用,也存在其他系统调用的情况,下面我们来探讨一下。
  • 我们可以看到上 面其他系统调用只有四种,首先来看第一个chdir, chdir调用更改当前工作目录
chdir("/usr/ast/test";
  • 在调用后,打开xyz文件,会打开/usr/ast/test/xyz文件,工作目录的概念消除了总是需要输入长文件 名的需要。
  • 在UNIX系统中,每个文件都会有保护模式,这个模式会有一个读-写-执行位,它用来区分所有者、 组和其他成员。chmod系统调用提供改变文件模式的操作。
  • 例如,要使一个文件除了对所有者之外的 用户可读,你可以执行
chmod("file",0644;
  • kill系统调用是用户和用户进程发送信号的方式,如果一个进程准备好捕捉一个特定的信号,那么在信号捕捉之前,会运行一个信号处理程序

  • 如果进程没有准备好捕捉特定的信号,那么信号的到来会 杀掉该进程(此名字的由来)。

  • POSIX定义了若干时间处理的进程。例如,time以秒为单位返回当前时间,0对应着1970年1月1日。在一台32位字的计算机中,time的最大值是(2人32) - 1秒,这个数字对应136年多一点。

  • 所以在 2106年,32位的UNIX系统会发飙。如果读者现在有32位UNIX系统,建议在2106年更换位64位 操作系统(偷笑〜)。

Win 32 API

上面我们提到的都是UNIX系统调用,现在我们来聊聊Win 32中的系统调用。

  • Windows和UNIX在各 自的编程方式上有着根本的不同。UNIX程序由执行某些操作或执行其他操作的代码组成,进行系统调用以执行某些服务。
  • Windows系统则不同,Windows应用程序通常是由事件驱动的。主程序会等待一 些事件发生,然后调用程序去处理。
  • 最简单的事件处理是键盘敲击和鼠标滑过,或者是鼠标点击,或者是插入USB驱动,然后操作系统调用处理器去处理事件,更新屏幕和更新程序内部状态。这是与UNIX 不同的设计风格。
  • 当然,Windows也有系统调用。在UNIX中,系统调用(比如read)和系统调用所使用的调用库(例 如read)几乎是一对一的关系。而在Windows中,情况则大不相同。
  • 首先,函数库的调用和实际的系 统调用几乎是不对应的。微软定义了一系列过程,称为Win32应用编程接口(Application Programming Interface),程序员通过这套标准的接口来实现系统调用。这个接口支持从Windows 95版本以来所有的Windows版本。
  • Win32 API调用的数量是非常巨大的,有数千个多。但这些调用并不都是在内核态的模式下运行时,有 —些是在用户态的模型下运行。
  • Win32 API有大量的调用,用来管理视窗、几何图形、文本、字体、滚 动条、对话框、菜单以及GUI的其他功能。为了使图形子系统在内核态下运行,需要系统调用,否则 就只有函数库调用。

我们把关注点放在和Win32系统调用中来,我们可以简单看一下Win32 API中的系统调用和UNIX中 有什么不同(并不是所有的系统调用)
在这里插入图片描述

  • 上表中是UNIX调用大致对应的Win32 API系统调用,简述一下上表
  • CreateProcess用于创建一 个新进程,它把UNIX中的fork和execve两个指令合成一个,一起执行。它有许多参数用来指定新创 建进程的性质。
  • Windows中没有类似UNIX中的进程层次,所以不存在父进程和子进程的概念。在进 程创建之后,创建者和被创建者是平等的。
  • WaitForSingleObject用于等待一个事件,等待的事件 可以是多种可能的事件。如果有参数指定了某个进程,那么调用者将等待指定的进程退出,这通过 ExitProcess 来完成。
  • 然后是6个文件操作,在功能上和UNIX的调用类似,然而在参数和细节上是不同的。和UNIX中一 样,文件可以打开,读取,写入,关闭。SetFilePointer和GetFileAttributesEx设置文件的 位置并取得文件的属性。
  • Windows中有目录,目录分别用CreateDirectory以及RemoveDirectory API调用创建和删 除。
  • 也有对当前的目录的标记,这可以通过SetCurrentDirectory来设置。使用GetLocalTime 可获得当前时间。
  • Win32接口中没有文件的链接、文件系统的mount、amount和stat ,当然,Win32中也有大量 UNIX中没有的系统调用,特别是对GUI的管理和调用

猜你喜欢

转载自blog.csdn.net/wolfGuiDao/article/details/107722419