机器指令、汇编指令、系统调用(OS Kernel)、标准库、应用

2020-05-22  本文已影响0人  Tenloy

一、概述

首先看一下软硬件间的关系,如图所示:

二、硬件 — CPU指令集

一般指令集专利持有者在设计指令集的时候,往往提供指令集对应的机器语言规范。 而为了方便,一般也会提供汇编语言规范。

(注意:CPU微架构设计厂商可能会对指令集进行微调。关于指令集设计者、微架构设计者两者的关系可以参考指令集、微架构、手机芯片(Soc)及ARM的介绍(偏硬件科普))

汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。汇编语言又被称为第二代计算机语言。

汇编语言产生的原因:

对于绝大多数人来说,二进制程序是不可读的,当然有能人可以读,比如第一代程序员,但这类人快灭绝了,直接看二进制不容易看出来究竟做了什么事情,比如最简单的加法指令二进制表示为 00000011,如果它混在一大串01字符串中就很难把它找出来,所以汇编语言主要就是为了解决二进制编码的可读性及编写效率问题(CPU指令集机器码形式记不住呀...)。

即指令集有两种表示形式:机器码形式、汇编语言形式。比如 00000011 加法指令,对应的汇编指令是 ADD,在调用汇编器时就会把 ADD 翻译成 00000011。

常见的指令集以及汇编语言规范:

3.2 壳层(Shell)

操作系统存在的目的是方便用户的使用,而内核提供的代码形式的系统调用,是面向编程开发者的,所以需要一个壳层:在计算机科学中指“为用户提供用户界面”的软件,或者说是指操作系统中提供访问内核所提供之服务的程序

Shell又分为了CLI Shell(命令行)、GUI Shell(图形化)两种形式,用户都可以用来使用操作系统。区别:

3.3 目前的操作系统为例

对上面的知识点进行总结,以目前的操作系统为例:

针对不同的角色,操作系统提供了不同的用户界面(不同的操作系统提供的用户界面也不同)

3.4 ABI与API

操作系统相关的有两类接口(意思并不是说只有OS才有ABI、API):

3.4.1 API

API(Application Programming Interface)应用程序接口:定义了源代码和库之间的接口(函数名、参数、返回值、数据类型定义等),即规定源代码可以怎么使用库的功能,使得一套 源代码可以在支持这个API的任何系统中 编译

3.4.2 ABI

ABI(Application Binary Interface)应用二进制接口:是指两程序模块间的接口,通常其中一个程序模块会是库或操作系统所提供的服务,而另一边的模块则是用户所运行的程序(前面已经知道程序主模块、动态链接库都叫程序模块)。

规定了机器代码的书写格式(二进制应用程序应该怎么调用CPU指令集中的指令),使得一套编译好的二进制代码(目标文件、可执行文件)可以在兼容ABI的系统中,无需任何修改直接运行

ABI涵盖了各种细节,如:

3.4.3 两者对比

四、语言标准库

编程语言的标准库是该语言的每种实现中都按例提供的库。在某些情况下,编程语言规格说明中会直接提及该库;另一些情况下,标准库的内容由编程社区中的非正式惯例决定。

根据宿主语言构成要素的不同,标准库可包含如下要素:

大多数标准库都至少含有如下常用组件的定义:

比如我们经典的C语言版“hello world”程序,使用C语言标准库的“printf”函数来输出一个字符串,“printf”函数对字符串进行一些必要的处理以后,最后会调用操作系统提供的系统调用。

各个操作系统,往终端输出字符串的API都不一样,在Linux下,它是一个 “write”的系统调用,而在Windows下则是 “WriteConsole”系统API。此外,还带有很多一些常用的函数。

Q1:如Java、Object-C语言库可不可以不调用C、C++语言格式的系统调用(API)?

当然可以,上文讲过,操作系统一般都提供了两种方式来使用系统调用:C语言库、汇编语言。系统调用对Linux、DOS来说只是int 0x80(中断)而已,所以不管语言库是什么语言编写的,(在Linux上运行的软件代码)只要能通过编译器翻译成对应Linux下的int 0x80的汇编语言就行了。

但是注意:封装高级语言格式的系统调用、设置0x80中断及对应的中断处理程序实现系统调用,都是操作系统提供的功能。

而且操作系统的系统调用实现是一定的,两种方式(其实是一种,C库本质上也是封装的汇编中断指令)最后找到的系统调用函数的实现(函数入口地址)都是一样的...

其实,编程人员开发时,用什么语言来调用系统调用是无所谓的,因为运行的时候,都需要编译、汇编成机器代码。从硬件角度来看(即最后的可执行文件),区别不大的(高级语言调用、汇编语言调用最后生成的机器码还是有些区别的)。

Q2:既然可以直接写汇编语言,而汇编语言又是直接对应CPU指令,即可以直接操作硬件资源,那为什么不能越过操作系统直接使用硬件?

不能。主语不要弄错,并不是汇编语言为什么不能操控硬件,是用户程序不能越过操作系统操控硬件。

核心问题:用户程序和系统调用的权限问题

系统调用是操作系统内核的代码,拥有最高的权限。以x86架构为例,x86指令运行的权限是从Ring0到Ring3的,操作系统内核运行在R0,用户程序运行在R3。

有一些指令只能在R0执行,比如修改CR寄存器(mov cr0, eax),这种操作在用户态直接会报错。因为CPU会保存当前运行的代码的CPL(当前特权级别)和IOPL(IO权限),如果CPL/IOPL不符,CPU会抛出异常,丢给内核R0的代码去处理。这种错误,是执行到某条特定指令才会遇到的,不是加载的时候遇到的,是CPU行为,不是操作系统行为。

操作系统之所以不会被限制,是因为操作系统内核运行在R0上,对CPU有完整的控制权。

计算机刚启动时,属于实模式,从实模式切换到保护模式的过程中,默认是进入到R0里的,所以操作系统在启动的过程中,是自动获得了R0的权限的。而用户代码都是被操作系统启动的(装载、链接),此时操作系统能控制用户代码运行在什么级别上

那么为什么操作系统的代码就可以切换特权级而用户的代码却不可以切换特权级?

实际上可以的。Windows可以加载用户驱动到内核(但受到一些限制),这就是把用户代码放到R0里执行的过程,Linux也有类似的东西存在。

五、编程开发的应用程序

内核 → 系统调用 → Shell

内核 → 系统调用 → 编程语言标准库 → 编程人员开发的应用程序

以我们日常使用的手机系统为例,应用程序与GUI Shell的关系其实并没有谁基于谁的关系,Shell本质上也是一个应用程序。都是依赖于内核提供的系统调用开发、运行的。只不过功能不同而已:

上一篇下一篇

猜你喜欢

热点阅读