1.1 什么是操作系统
首先我们来看计算机硬件设备的分布
那么有了这些硬件设备,计算机可以干什么呢,计算机可以帮助我们解决很多实际的问题,比如当我们在键盘上输入“hello world”,计算机会帮我们将其展示在显示器上,这里我们给出实现的机制:
但在实际实现的时候,我们只需键入即可,而底层复杂的操作对于用户来说就像一个黑盒子,是不可见的。那这个封装起来的黑盒子是由什么实现的呢,
这里我们先给出结论——操作系统
什么是操作系统
操作系统(Operating System,简称OS)是管理计算机硬件与软件资源的计算机程序。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入设备与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。
下面我们来对定义进行剖析
操作系统管理的计算机硬件都有什么?
- cpu管理
- 内存管理
- 终端管理
- 磁盘管理
- 文件管理
- 网络管理
- 电源管理
- 多核管理
而对于操作系统,我们应该做到能改操作系统。
1.2 揭开钢琴的盖子
我们不妨把操作系统比作一个钢琴,要弹出美妙的乐曲,我们不仅要了解每个琴键的作用,也要可以打开盖子,通过调整琴弦的粗细长短改变音调。
我们不妨试着打开计算机的盖子,从打开电源的那一刻开始,操作系统的故事就已然开始了——
首先,我们先根据认知猜测一下,打开电源后显示"loading ...",那这些数据是通过怎样的曲折最终发送到显存的呢?开机后,整个IO设备和文件系统都可以用了,是不是在开机时完成的初始化呢,如果是,又做了怎样的初始化呢?
这一切,都要从最核心的思想——冯诺依曼结构谈起。
艺术来源于生活,技术也不例外,计算机说到底就是一个计算模型,当人要计算1+1=2的时候,我们会先将1+1写在纸上,然后大脑运算出结果2,再将其写到纸上。这里,纸就如同一个存储器,而大脑就是运算器,控制器,笔是输入输出设备
。很酷对吧,但是1*1=?怎么搞呢?2/2呢,看来这个计算模型还需要存储多种模式,而运算器能识别不同的模式。至此,无龙珠集齐,我们可以召唤神龙——冯诺依曼结构
所谓大道至简,不过如是
2.2操纵系统启动
小小的分割一手,因为以上知识帮助我们在脑海中形成了一个操作系统的最小单元,接下来就要一探究竟了。
计算机对应用程序的处理,也就是取指执行的过程,那么它指向内存何处呢?
-
X86 PC刚开机时CPU处于实模式,此时CS=0XFFFF,IP=0X0000,寻址到0XFFFFO,也就是ROM BIOS映射区,接着检查RAM,键盘,显示器,软硬磁盘,将磁盘的0磁道0扇区读入0X7C00处,设置cs=0x7c0,ip=0x0000
-
为什么要跳转到0X7C00处呢?这块内存区域存储的是什么呢?
这里存放的是磁盘引导扇区读入的512个字节,也是开机后执行的第一段我们可以控制的程序,操作系统的故事从这里开始........
代码很多,我们不追求每句都能看懂,而要抓住核心点——
这段代码将引导扇区的代码
以字节为单位循环加载到CS=INITSEG,ip= GO
处,或称段间跳转在bootsect.s中加载load_setup模块,该模块通过中断信号0X13读取到扇区数量,并跳转ok_load_setup模块,
在该模块中获取磁盘参数,读取光标位置,循环写入24个字符,并在最后跳转read_it标号,而该标号属于System模块
在System模块中,读取磁道,重新回到setup模块执行,CPU进入保护模式,并初始化中断控制,那CPU是如何进入保护模式的呢?自然需要标志寄存器cr0.当
PE=1时启动保护模式,PG=1时自动分页
,CPU在进入保护模式后,有一个关键跳转,即jmpi 0, 8,这里设置cs=8是用来查询GDT表,即中断向量表。这里我们简单那描述一下GDT表的表项——那么程序计数器PC跳转到了哪呢?那就是关键的System模块,
开局几张图,剩下全靠编,假装有"汇字"
才疏学浅,这几张图以我的水准实在无法给出一个容易理解的答案,等什么时候经过时间沉淀突然悟了,再回来讲述这些汇编背后的故事.........
3.1操作系统的接口
要学习操作系统的接口,我们必须先了解接口是什么,在日常生活中,我们使用插座,油门这种类型的物件,其实就是在使用接口。牛津词典是这样定义接口的:
接口是连接两个东西,屏蔽细节,用于信号转换的
那么什么是操作系统的接口呢?
可见,操作系统接口将上层软件和操作系统连接起来。表面上看用户可以直接与操作系统交互,然而操作系统接口是面对用户的吗?
显然不是,我们通常使用计算机的方式有三种,分别是
-
命令行
-
图形化界面
-
应用程序
由此可以看出操作系统接口不是面向用户的,这三种方式都归于调用系统常用的C语言函数及普通函数(应用程序接口)来实现。
到这里,我们可以给出操作系统接口一个相对贴近的定义了——
操作系统接口:接口表现为调用,又由系统提供,因而称操作系统接口为系统调用
既然交互的过程是通过函数来实现的,那通常会调用那些函数呢,IEEE组织针对系统调用制定了一个标准族,即POSIX
可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX),是IEEE为要在各种UNIX操作系统上运行的软件,而定义API的一系列互相关联的标准的总称
这里我抛转引玉。简要介绍操作系统实现系统调用的方式。
假设我们在linux0.11中添加一个名称为hello的系统调用,它实现的功能是打印hello,xaiolin。操作系统实现系统调用的基本过程是:
- 应用程序调用库函数API
- API将系统调用号存入EAX寄存器,然后通过中断调用使系统进入内核态
- 内核中的中断处理函数根据系统调用号调用对应的内核函数
- 系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数
- 中断处理函数返回到API
- API将EAX返回给应用程序。
对应的,我们操作的过程是这样的——
- 首先参照其他API编写用户接口_syscall1(int,hello)
- 接着找到linux0.11目录下的/include/unistd.h文件,在其中添加系统调用号和用户接口声明_syscall1(int,hello),这一步主要是为了补全宏展开以后需要的参数。
- 然后就要在kernel/x.c文件中写真正的系统调用函数了,这里调用put_fs_byte循环打印输出,再加入简单的参数判断即可。
- 那系统怎么知道系统调用的存在呢?所以还要在include/linux/sys.h文件中加入系统调用声明,即sys_hello(),下一步很自然应该是修改系统调用的总数,使其总数加1。
- 到这里,外围的工作就ok了,但要展示效果,还需要编译内核,所以还需要修改MakeFile文件,具体操作是在依赖中添加对应项,最终make file就ok了
好了,回归正题,如何扩展系统调用的参数数量呢?有看到几种方法,但回答的都很简略,想知道具体的实施方法。
日拱一卒,功不唐捐