我是如何学习写一个操作系统(四):操作系统之系统调用
前言
最近有点事情,马上要开学了,所以学习的脚步就慢下来了。这一篇主要是来说操作系统的系统调用的,像C语言的printf深入到内部就是一个有关屏幕输出的系统调用
什么是系统调用
之前提过操作系统是对硬件的抽象,也是软硬件之间的一层。之前比如如果我们想要在屏幕上输出一些字符,就需要一些指令操作,然后把数据放到显存上。但是在有了操作系统后,就不需要这样做,也不能这样做了。这时候只要操作系统提供一个接口来让我们完成这个任务
由操作系统实现提供的所有系统调用所构成的集合即程序接口或应用编程接口(Application Programming Interface,API)。是应用程序同系统之间的接口。
系统调用的实现
在硬件设计上,通过区分内核态和用户态来把内核程序和用户程序隔离开
CS寄存器最低的两位为0即是内核态,为3是用户态
但是系统调用的代码是处在内核态的,所以就需要提供一种方法来能够让用户程序进入内核态来实现系统调用
在X86里,INT指令就是硬件用来提供由用户态进入内核态的方法,所以系统调用的实现就可以变为:
- 由用户程序发起一个INT指令,指明要调用的服务
- 在操作系统里写出相应的中断处理
- 由操作系统根据用户指明要调用的服务取执行相应的代码
内联汇编
稍微说一下C里的内联汇编,以免之后忘记。
gcc的内联汇编一般都是这个格式
asm ( 汇编指令: 输出操作数 // 非必需: 输入操作数 // 非必需: 其他被污染的寄存器 // 非必需);
第一部分就是汇编指令
第二部分是输出操作数,都是 "=?"(var) 的形式, var可以是任意内存变量(输出结果会存到这个变量中), ?一般是下面这些标识符 (表示内联汇编中用什么来代理这个操作数):
a,b,c,d,S,D 分别代表 eax,ebx,ecx,edx,esi,edi 寄存器
r 上面的寄存器的任意一个(谁闲着就用谁)
m 内存
i 立即数(常量,只用于输入操作数)
g 寄存器、内存、立即数 都行
在汇编中用%序号来代表这些输入/输出操作数,序号从0开始。为了与操作数区分开来,寄存器用两个%引出,如:%%eax第三部分是是输入操作数,都是 "?"(var) 的形式, ? 除了可以是上面的那些标识符,还可以是输出操作数的序号,表示用 var 来初始化该输出操作数,上面的程序中 %0 和 %1 就是一个东西,初始化为 1(a的值)。
第四部分标出那些在汇编代码中修改了的、 又没有在输入/输出列表中列出的寄存器, 这样 gcc 就不会擅自使用这些"危险的"寄存器。 还可以用 "memory" 表示在内联汇编中修改了内存, 之前缓存在寄存器中的内存变量需要重新读取。
参考链接
Linux0.11里对系统调用的代码实现
设置中断
- 首先需要对IDT设置中断调用的处理函数
#define set_system_gate(n,addr) \_set_gate(&idt[n],15,3,addr)#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \"movw %0,%%dx\n\t" \"movl %%eax,%1\n\t" \"movl %%edx,%2" \: \: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"o" (*((char *) (gate_addr))), \"o" (*(4+(char *) (gate_addr))), \"d" ((char *) (addr)),"a" (0x00080000))set_system_gate(0x80,&system_call);
实现中断函数
- sys_call_table[]是一个指针数组,定义在include/linux/sys.h中,该指针数组中设置了所有72个系统调用C处理函数地址。
system_call:cmpl $nr_system_calls-1,%eaxja bad_sys_callpush %dspush %espush %fspushl %edxpushl %ecx # push %ebx,%ecx,%edx as parameterspushl %ebx # to the system callmovl $0x10,%edx # set up ds,es to kernel spacemov %dx,%dsmov %dx,%esmovl $0x17,%edx # fs points to local data spacemov %dx,%fscall sys_call_table(,%eax,4)pushl %eaxmovl current,%eaxcmpl $0,state(%eax) # statejne reschedulecmpl $0,counter(%eax) # counterje reschedule
提供接口
- 在linux向应用程序提供系统调用接口write
- _syscall3的本质上是一个宏
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \: "=a" (__res) \: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \return (type) __res; \
errno=-__res; \
return -1; \
}
小结
这样对于一个系统调用就会变成
printf 用户调用
⬇
int 0x80 库函数的实现
⬇
进入内核
system_call 中断调用
⬇
sys_ 系统调用
转载于:https://www.cnblogs.com/secoding/p/11414172.html
我是如何学习写一个操作系统(四):操作系统之系统调用相关推荐
- 我是如何学习写一个操作系统(完结):总结和系列索引
前言 从一开始的引导程序到现在的文件系统已经有十篇了,算是自己对操作系统的学习的一个笔记,原本是想结合自己之前写的玩具操作系统FragileOS,但是之后就转到了结合Linux 0.11的代码去写这个 ...
- 我是如何学习写一个操作系统(五):故事的高潮之进程和线程1
前言 为什么取这个标题呢?一是进程和线程是作为操作系统里最重要最核心的一部分.二是确实吃冰棍拉冰棍,没话,强行凑标题和之前的标题差不多字数. 前一章写了系统调用的过程,算是一个小插曲,这个部分不管在哪 ...
- 我是如何学习写一个操作系统(一):开篇
前言 有人说程序员的三大浪漫分别是编译原理.操作系统和计算机网络 在之前完成了一两个小软件和网站后遇见了编译原理,也不知道为什么就傻傻的学,还写了一个从零写编译器的系列学习笔记,不过都是小打小闹,只能 ...
- 我是如何学习写一个操作系统(九):文件系统
前言 这个应该是这个系列的尾声了,一个完整的操作系统可能最主要的也就是分成这几大模块:进程管理.内存管理和文件系统.计算机以进程为基本单位进行资源的调度和分配:而与用户的交互,基本单位则是文件 生磁盘 ...
- 我是如何学习写一个操作系统(八):内存管理和段页机制
前言 多进程和内存管理是紧密相连的两个模块,因为运行进程也就是从内存中取指执行,创建进程首先要将程序和数据装入内存.将用户原程序变成可在内存中执行的程序,而这就涉及到了内存管理. 内存的装入 绝对装入 ...
- 我是如何学习写一个操作系统(二):操作系统的启动之Bootloader
前言 今天本来的任务看书和把之前写的FragileOS整理一下,但是到现在还在摸鱼,书也只看一点.后来整理了一下写这个系列的思路,原本的目的是对操作系统原理性的学习和对之前写的一个玩具型操作系统的回顾 ...
- 我是如何学习写一个操作系统(七):进程的同步与信号量
前言 在多进程的运行环境下,进程是并发执行的,不同进程间存在着不同的相互制约关系.为了协调进程之间的相互制约关系,达到资源共享和进程协作,避免进程之间的冲突,引入了进程同步的概念. 临界资源 多个进程 ...
- 我是如何学习写一个操作系统(三):操作系统的启动之保护模式
前言 上一篇其实已经说完了boot的大致工作,但是Linux在最后进入操作系统之前还有一些操作,比如进入保护模式.在我自己的FragileOS里进入保护模式是在引导程序结束后完成的. 实模式到保护模式 ...
- 我是如何学习写一个操作系统(六):进程的调度
前言 既然引进了多进程,其实也就是在进程之间来回切换,那么就会有进程之间的调度问题.实则是在可运行进程之间分配有限的处理器时间资源的内核子系统. 几个简单的CPU调度算法 First Come, Fi ...
最新文章
- PHP开发移动端接口
- Nodejs连接mysql的增、删、改、查操作
- 理解python并发编程_Python并发编程很简单
- firefox与ie 的javascript区别
- 文献记录(part16)--Learning Bayesian Network Classifiers: Searching in a Space of Partially ...
- PyTorch中的Variable类型
- 用nginx缓存静态文件
- 主键与主键索引的关系
- 如何修改maven默认仓库(即repository)的路径
- 浅谈.net的后台校验
- 我的世界java版怎么打开聊天栏_我的世界JAVA版才有的隐藏模式只有开发者才知道怎么进入...
- 计算机培训作息时间安排,985学霸作息时间表“走红”,网友:越努力,越幸运...
- 学生也可以搭建自己的网站详细讲解
- 数据守护集群之MPP主备集群
- 商业银行的科技发展趋势
- 使用python编写彩色圆圈
- Perl正则表达式讲解
- 你敢相信?这8项AI技术已经实现……
- END-USER LICENSE AGREEMENT of Atelier Juvenil Pardo APP
- 适合初学者练手的vue小商城项目(附github源码)
热门文章
- 你真的在正确地使用WLAN控制器吗?
- Lucene系列:(9)搜索结果排序
- android动画Rotate
- 戴帽子对眼睛是否有好处
- 去螨虫能不能用去螨皂?
- org.codehaus.janino.CompilerFactory cannot be cast to org.codehaus.commons.compiler.ICompilerFactory
- /hbase/WALs/desktop,xxxxxxxx-splitting is non empty: Directory is not empty
- xfce4开始菜单中的小老鼠图标不见了
- Pessimistic Error Pruning example of C4.5
- SVM入门(八)松弛变量(转)