一步步编写操作系统 67 系统调用的实现1-2 68

接上文:

系统调用的子功能要用eax寄存器来指定,所以咱们要看看有哪些系统调用啦,在linux系统中,系统调用是定义在/usr/include/asm/unistd.h文件中,该文件只是个统一的入口,指向了32位和64位两种版本。在asm目录下提供了这两个版本,文件名分别是unistd_32.h 和unistd_64.h,这里给大家摘录了部分32位x86平台下的unistd_32.h文件,见图

 

在/usr/include/asm/unistd_32.h文件中共定义了348个系统调用,哦,给大家说一下,我用的linux版本是CentOS release 6.3 (Final),不知道新版本内核中是否增加了新的系统调用功能。

我们要用的系统调用是第4号调用,即__NR_write。不要被它前面的两个下滑线吓到,就是个命名而已,它代表就是我们所说的write系统调用。

如果不知道某个系统调用的用法,可以用man命令来查看,方法是man 2 系统调用名。咱们执行man 2 write看看,见图

 

man后面的数字2是表示查看System Calls方面的帮助,对于man自己的帮助信息,man命令也可以man自己,可以用man man来查看。上图只是部分帮助信息,咱们了解这些就够用了。write的功能是把buf指向的缓冲区中的count个字节写入fd指向的文件描述符,执行成功后返回写入的字节数,失败则返回-1。

如果在c语言中调用write的话,直接代入实参就行了,这是最简单的方式,如代码c_syscall.c:

#include <unistd.h>
int main(){write(1,"hello,world\n",4);return 0;
}

为了使用c标准库中的write函数,文件开头包含了标准头文件unistd.h,通过该函数可以使用系统的write系统调用,该文件在磁盘上的路径是/usr/include/unistd.h。不过在本机上测试发现不包含unistd.h,其编译、运行都没问题,也许这和隐式声明有关,这里不再深究。

调用“系统调用”有两种方式:

  1. 将系统调用指令封装为c库函数,通过库函数进行系统调用,操作简单。
  2. 不依赖任何库函数,直接通过汇编指令int与操作系统通信。

以上的c代码就是用的第一种方式,不知道您是否对write函数的内部实现感兴趣,其实我也没研究过,不过万变不离其宗,核心思想是必须与进行内核沟通才能获得内核提供的功能。所以,write内部封装的一定是系统调用指令,按照这种设想,下次咱们会模拟一下它的实现。

 

调用“系统调用”有两种方式:

  1. 将系统调用指令封装为c库函数,通过库函数进行系统调用,操作简单。
  2. 不依赖任何库函数,直接通过汇编指令int与操作系统通信。

以上的c代码就是用的第一种方式,不知道您是否对write函数的内部实现感兴趣,其实我也没研究过,不过万变不离其宗,核心思想是必须与进行内核沟通才能获得内核提供的功能。所以,write内部封装的一定是系统调用指令,按照这种设想,一会咱们会模拟一下它的实现。

我们这里要介绍下第二种:跨过库函数直接与系统内核通信,这样最终的程序是不需要与任何库文件链接,这是获得系统功能效率最高的方式。

我相信,如果曾经学过汇编语言,老师都给咱们演示过第二种方式,但大多数同学还是觉得云里雾里,即使照葫芦画瓢完成了打印字符串的工作,也有部分同学不清楚自己在做什么,所以我在这里尽量多说一点。

前面我们已经知道了write系统调用函数的c语言使用方式,我们要用汇编代码直接与内核通信该怎么做?我们要看看系统调用输入参数的传递方式。

当输入的参数小于等于5个时,linux是用寄存器传递参数。当参数个数大于5个时,把参数按照顺序放入连续的内存区域,并将该区域的首地址放到ebx寄存器。这里我们只演示参数小于等于5个的情况。

eax寄存器用来存储子功能号(寄存器eip、ebp、esp是不能使用的)。5个参数是存放在以下寄存器中,传送参数的顺序是:

  1. ebx存储第1个参数
  2. ecx存储第2个参数
  3. edx存储第3个参数
  4. esi存储第4个参数
  5. edi存储第5个参数

好啦,理论知识够用啦,现在赶紧实践一把,见以下代码syscall_write.S

 1 section .data2 str_c_lib: db "c library says: hello world!", 0xa ;0xa为LF ascii码3 str_c_lib_len equ $-str_c_lib45 str_syscall: db "syscall says: hello world!", 0xa6 str_syscall_len equ $-str_syscall78 section .text9 global _start10 _start:11 ;;;;;;;;;;;;; 方式1: 模拟c语言中系统调用库函数write ;;;;;;;;;;;;;12 push str_c_lib_len ;按照c调用约定压入参数13 push str_c_lib14 push 11516 call simu_write ;调用下面定义的simu_write17 add esp,12 ;回收栈空间1819 ;;;;;;;;;;;;; 方式2: 跨过库函数,直接进行系统调用 ;;;;;;;;;;;;;20 mov eax,4 ;第4号子功能是write系统调用(不是c库函数write)21 mov ebx, 122 mov ecx, str_syscall23 mov edx, str_syscall_len24 int 0x80 ;发起中断,通知linux完成请求的功能。2526 ;;;;;;;;;;;;; 退出程序 ;;;;;;;;;;;27 mov eax,1 ;第1号子功能是exit28 int 0x80 ;发起中断,通知linux完成请求的功能。2930 ;;;;;;;下面自定义的simu_write用来模拟c库中系统调用函数write,;;;;;;这里模拟它的实现原理31 simu_write:32 push ebp ;备份ebp33 mov ebp,esp34 mov eax,4 ;第4号子功能是write系统调用(不是c库函数write) 35 mov ebx, [ebp+8] ;第1个参数36 mov ecx, [ebp+12] ;第2个参数37 mov edx, [ebp+16] ;第3个参数38 int 0x80 ;发起中断,通知linux完成请求的功能39 pop ebp ;恢复ebp40 ret

代码syscall_write.S中,我们演示了系统调用的两种方式。程序开头定义了两种方式下打印的字符串,其中0xa为LF(LineFeed)ascii码,这样就会输出一个换行符。

第11~17行是在演示方式1,模拟调用c库函数write的方式。因为write是c库函数,按一般的做法是,汇编程序需要与c代码生成的目标文件链接才能调用c的代码。在这个例子中我们并没有这样帮做,因为我想让大家了解write函数的本质,所以,在这里为大家定义了simu_write来代替c库函数write,用它来简单解释write的原理,它定义在31~40行。这里是按照c调用约定将参数从右到左依次入栈,随后调用simu_write实现字符串打印功能。

第19~24行是在演示第2种系统调用的方式,这是最简单直接可依赖的方式。20~24行是在eax中赋予子功能号、将参数按照顺序依次写入对应的寄存器。

第31~40行是simu_write的实现,它内部在本质上是和第2种方式一样,都是在内部调用int指令直接和系统通信实现系统调用。此函数只是为了试图揭开c库函数的实现原理,良苦用心您懂的。

好啦,编译链接过程如下:

nasm -f elf -o syscall_write.o syscall_write.S

其中-f参数是用来指定编译输出的文件格式,这里需要指定为elf,目的是将来要和gcc编译的elf格式的目标文件链接,所以格式必须相同。nasm输出为目标文件,已经用-o指定文件名为syscall_write.o。

最后用ld程序将syscall_write.o链接成elf格式的二进制可执行文件。

ld -o syscall_write.bin syscall_write.o

程序执行后的效果如图

 

顺便说一句,syscall_write.bin如果因为权限不足而无法执行时,可以用以下指令增加执行权限:

chmod u+x syscall_write.bin

本文摘自《操作系统真象还原》,请大家支持正版,多谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【HDU - 6662】Acesrc and Travel(树形dp,博弈dp)

题干&#xff1a; Acesrc is a famous tourist at Nanjing University second to none. During this summer holiday, he, along with Zhang and Liu, is going to travel to Hong Kong. There are nnspots in Hong Kong, and n−1n−1 bidirectional sightseeing bus routes …

一步步编写操作系统 69 汇编语言和c语言共同协作 70

由于有了上一节的铺垫&#xff0c;本节的内容相对较少&#xff0c;这里给大家准备了两个小文件来实例演示汇编语言和c语言相互调用。 会两种不同语言的人&#xff0c;只是掌握了同一件事物的两种表达方式。人在学习一种新语言时&#xff0c;潜意识里是建立了语言符号与事物形象…

一步步编写操作系统 71 直接操作显卡,编写自己的打印函数71-74

一直以来&#xff0c;我们在往屏幕上输出文本时&#xff0c;要么利用bios中断&#xff0c;要么利用系统调用&#xff0c;这些都是依赖别人的方法。咱们还用过一个稍微有点独立的方法&#xff0c;就是直接写显存&#xff0c;但这貌似又没什么含量。如今我们要写一个打印函数了&a…

【CodeForces - 208C 】Police Station(单源最短路条数,起点终点建图,枚举顶点)

题干&#xff1a; The Berland road network consists of n cities and of m bidirectional roads. The cities are numbered from 1 to n, where the main capital city has number n, and the culture capital — number 1. The road network is set up so that it is possi…

【Chrome浏览器】常用快捷键整理

标签页和窗口快捷键 1. Ctrl n 打开新窗口 2. Ctrl t 打开新的标签页&#xff0c;并跳转到该标签页 3. Ctrl Shift t 重新打开最后关闭的标签页&#xff0c;并跳转到该标签页 4. Ctrl Tab 跳转到下一个打开的标签页 5. Ctrl Shift Tab 跳转到上一个打开的标签页 6.…

【人工智能课程实验】 - 利用贝叶斯分类器实现手写数字 的识别

读入数据与预处理 因为老师给的文件无法直接读取&#xff0c;故从官网导入数据&#xff1a; 官网链接&#xff1a;http://www.cs.nyu.edu/~roweis/data.html 导入数据之后要对MATLAB文件进行读入&#xff1a; datasio.loadmat(trainfile) 对文件type一下&#xff1a; ty…

一步步编写操作系统 77 内联汇编与ATT语法简介

内联汇编 之前和大家介绍过了一种汇编方法&#xff0c;就是C代码和汇编代码分别编译&#xff0c;最后通过链接的方式结合在一起形成可执行文件。 另一种方式就是在C代码中直接嵌入汇编语言&#xff0c;强大的GCC无所不能&#xff0c;咱们本节要学习的就是这一种&#xff0c;它…

【Python学习】内置函数(不断更新)

关于常用在for循环中的range函数 python range() 函数可创建一个整数列表&#xff0c;一般用在 for 循环中。 函数语法 range(start, stop[, step]) 参数说明&#xff1a; start: 计数从 start 开始。默认是从 0 开始。例如range&#xff08;5&#xff09;等价于range&#…

【Python学习】 简单语法与常见错误(持续更新)

关于单引号和双引号 当输出的字符串内部没有单引号的时候&#xff0c;外面可以用单引号&#xff0c; 但是如果内部有了单引号&#xff0c;那么外部只能用双引号。 dict {Name: Zara, Age: 7, Class: First} print(dict) print (dict[Name]: , dict[Name]) print ("dic…

一步步编写操作系统 78 intel汇编与ATT汇编语法区别

本节咱们介绍下intel汇编语法和at&t汇编语法的区别。 以上表中未列出这两种语法在内存寻址方面的差异&#xff0c;个人觉得区别还是很大的&#xff0c;下面单独说说。 在Intel语法中&#xff0c;立即数就是普通的数字&#xff0c;如果让立即数成为内存地址&#xff0c;需要…

重读经典:《Masked Autoencoders Are Scalable Vision Learners》

MAE 论文逐段精读【论文精读】这一次李沐博士给大家精读的论文是 MAE&#xff0c;这是一篇比较新的文章&#xff0c;2021年11月11日才上传到 arXiv。这篇文章在知乎上的讨论贴已经超过了一百万个 view&#xff0c;但是在英文社区&#xff0c;大家反应比较平淡一点&#xff0c;R…

【Python学习日志】 - Numpy包

NumPy是什么&#xff1f; 使用Python进行科学计算的基础包&#xff0c;在数据分析的时候比较常用到矩阵计算。这时太多的Np属性不记得&#xff0c;所以方便自己使用把一些常用的Np属性汇总记录一下使用的时候方便查找。 ndarray.ndim 阵列的轴数&#xff08;尺寸&#xff09;…

详解协同感知数据集OPV2V: An Open Benchmark Dataset and Fusion Pipeline for Perception with V2V Communication

在《详解自动驾驶仿真框架OpenCDA: An Open Cooperative Driving Automation Framework Integrated with Co-Simulation》 一文中介绍了自动驾驶仿真框架 OpenCDA。本文将介绍论文作者另一篇最新工作 OPV2V&#xff0c;论文收录于 ICRA2022。 OPV2V 数据集主要 feature 有&…

【Python学习】 - 如何在Spyder中弹出plot绘图窗口而不是在Console中绘图

依次选择这几项&#xff1a; 点击ok确认。 注意&#xff1a;点击ok之后不会立即生效&#xff0c;重启Spyder之后才会生效

mysql系列:加深对脏读、脏写、可重复读、幻读的理解

关于相关术语的专业解释&#xff0c;请自行百度了解&#xff0c;本文皆本人自己结合参考书和自己的理解所做的阐述&#xff0c;如有不严谨之处&#xff0c;还请多多指教。 **不可重复读的重点是修改: **同一事务&#xff0c;两次读取到的数据不一样。 幻读的重点在于新增或者…

重读经典(点云深度学习开山之作):《Deep learning on point clouds for 3D scene understanding》(持续更新中)

本文介绍的是 PointNet 作者的博士论文&#xff1a;3D场景理解中的点云深度学习。从上图可以看到&#xff0c;整个博士论文主要贡献有两块&#xff1a;一是点云深度学习的网络架构&#xff08;PointNet 和 PointNet&#xff09;&#xff1b;二是在3D场景理解中的应用&#xff0…

Coursera自动驾驶课程第17讲:An Autonomous Vehicle State Estimator

在第16讲《Coursera自动驾驶课程第16讲&#xff1a;LIDAR Sensing》我们学习了自动驾驶目前常用的3D 传感器&#xff0c;激光雷达&#xff0c;了解了激光雷达的工作原理&#xff0c;掌握了对点云数据的操作以及如何使用点云配准方法来进行汽车定位。 回顾一下&#xff0c;在本…

!何为脏读、不可重复读、幻读

2.0、前言 事务的隔离性是指多个事务并发执行的时候相互之间不受到彼此的干扰的特性&#xff0c;隔离性是事务ACID特性中的I&#xff0c;根据隔离程度从低到高分为Read Uncommitted&#xff08;读未提交&#xff09;&#xff0c;Read Committed&#xff08;读已提交&#xff0…

【转】JPA、Hibernate和Mybatis区别和总结

很多人都用过java的数据库连接池C3P0&#xff0c;但官方没有说明名称的由来。 据传闻&#xff1a;连接池作者是《星球大战》迷&#xff0c;C3P0就是其中的一个机器人&#xff0c;并且这个名称中包涵connection 和pool的单词字母。因此叫这个名字&#xff08;根据网友提醒&…

详解3D物体检测模型: Voxel Transformer for 3D Object Detection

本文介绍一个新的的3D物体检测模型&#xff1a;VoTr&#xff0c;论文已收录于ICCV 2021。 这是第一篇使用 voxel-based Transformer 做3D 主干网络&#xff0c;用于点云数据3D物体检测。由于有限的感受野&#xff0c;传统的 3D 卷积网络检测器&#xff08;voxel-based&#xff…