当前位置: 首页 > news >正文

系统调用与API

系统调用介绍

        什么是系统调用

                        为了让应用程序有能力访问系统资源,也为了让程序借助操作系统做一些由操作系统支持的行为,每个操作系统都会提供一套接口,以供应用程序使用。系统调用涵盖的功能很广,有程序运行所必需的支持,例如创建/退出进程和线程,进程内存管理,也有对系统资源的访问,例如文件,网络,进程间通信,硬件设备的访问,也可能有对图形界面的操作支持。

        Linux系统调用

                        下面让我们来看看Linux系统调用的定义,有一个比较直观的概念。在x86下,系统调用由0x80中断完成,各个通用寄存器用于传递参数,EAX寄存器用于表示系统调用的接口号,比如EAX=1表示退出进程(exit);EAX=2表示创建进程(fork);EAX=3表示读取文件或IO(read);EAX=4表示写文件或IO(write)等,每个系统调用都对应内核源代码中的一个函数,它们都是以"sys_"开头,比如exit调用对应内核中的sys_exit函数。当系统调用返回时,EAX又作为调用结果的返回值。

EAX名字C语言定义含义参数
1exitvoid _exit(int status)退出进程EBX表示退出码
3readssize_t read(int fd, void* buf, size_t count)读文件EBX表示文件句柄,ECX表示读取缓冲区地址,EDX表示读取大小

        系统调用的弊端

                        系统调用完成了应用程序和内核交流的工作,因此理论上只需要系统调用就可以完成一些程序,但是:

                                理论上,理论总是成立的。

                                事实上,包括Linux,大部分操作系统的系统调用都有两个特点:

                                1、使用不便。操作系统提供的系统调用接口往往过于原始,程序员需要了解很多与操作系统相关的细节。如果没有进行很好的包装,使用起来不方便。

                                2、各个操作系统之间系统调用不兼容。

系统调用原理

        特权级与中断

                        现在的CPU常常可以在多种截然不同的特权级别下执行指令,在现代操作系统中,通常也据此有两种特权级别,分别为用户模式和内核模式,也被称为用户态和内核态。由于有多种特权模式的存在,操作系统就可以让不同的代码运行在不同的模式下,以限制它们的权利,提高稳定性和安全性。普通应用程序运行在用户态的模式下,诸多操作将受到限制,这些操作包括访问硬件设备,开关中断,改变特权模式等。

                        一般来说,运行在高特权级的代码将自己降至低特权级是允许的,但反过来低特权级的代码将自己提升至高特权级则不是轻易就能进行的。在将低特权级的环境转为高特权级时,必须使用一种较为受控和安全的形式,以防止低特权模式的代码破坏高特权模式代码的执行。

                        系统调用是运行在内核态的,而应用程序基本都是运行在用户态的。用户态的程序如何运行内核态的代码呢?操作系统一般是通过中断来从用户态切换到内核态。什么是中断呢?中断是一个硬件或软件发出的请求,要求CPU暂停当前的工作转手去处理更加重要的事情。

                        中断一般具有两个属性,一个称为中断号(从0开始),一个称为中断处理程序。不同的中断具有不同的中断号,而同时一个中断处理程序一一对应一个中断号。在内核中,有一个数组称为中断向量表,这个数组的第n项包含了第n号中断的中断处理程序的指针。当中断到来时,CPU会暂停当前执行的代码,根据中断号,在中断向量表中找到对应的中断处理程序,并调用它。中断处理程序执行完成之后,CPU会继续执行之前的代码。一个简单的示意图如图1所示。

 图1 CPU中断过程

                        通常意义上,中断有两种类型,一种称为硬件中断,这种中断来自硬件的异常或其他事件的发生,如电源掉电,键盘被按下。另一种称为软件中断,软件中断通常是一条指令,带有一个参数记录中断号,使用这条指令用户可以手动触发某个中断并执行其中断处理程序。例如在i386下,int 0x80这条指令会调用第0x80号中断的处理程序。

                        由于中断号是很有限的,操作系统不会舍得用一个中断号来对应一个系统调用,而更倾向于一个或少数几个终端号来对应所有的系统调用。对于同一个中断号,操作系统如何知道是哪一个系统调用被调用呢?和中断一样,系统调用都有一个系统调用号,就像身份标识一样来表明是那个系统调用,这个系统调用号通常就是系统调用在系统调用表中的位置,例如Linux里的fork系统调用号是2。这个系统调用号在执行int指令前会被放置在某个固定的寄存器里,对应的中断代码会取得这个系统调用号,并且调用正确的函数。以Linux的int 0x80为例,系统调用号是由eax来传入。用户将系统调用号放入eax,然后使用int 0x80调用中断,中断服务程序就可以从eax里取得系统调用号,进而调用对应的函数。

        基于int的Linux的经典系统调用实现

                图2是以fork为例的Linux系统调用的执行流程。

图2 Linux系统中断流程 

                触发中断

                        首先当程序在代码里调用一个系统调用时,是以一个函数的形式调用的,例如程序调用fork:

                        int main(){ fork();}

                        fork函数是一个对系统调用fork的封装,可以用下列宏来定义它:

                        _syscall0(pid_t, fork);

                        _syscall0是一个宏函数,用于定义一个没有参数的系统调用的封装。它的第一个参数为这个系统调用的返回值类型,这里为pit_t,是一个Linux自定义类型,代表进程的id。_syscall0的第二个参数是系统调用的名称,_syscall0展开之后会形成一个与系统调用名称同名的函数。下面的代码是i386版本的syscall0定义:

                        #define _syscall0(type, name )                

                        type name(void)                                       

                        {                                                                 

                                long __res;                                          

                                __asm__ volatile("int $0x80"

                                        : "=a" (__res)                          

                                        : "0" (__NR__##name));                

                                __syscall_return(type, __res);                

                                                                            

                        对应syscall0(pid_t, fork),上面的宏将展开为:

                        pid_t fork(void)

                        {

                                long __res;

                                __asm__ volatile("int $0x80"

                                        : "=a" (__res)

                                        : "0" (__NR_fork));

                                __syscall_return(pid_t, __res);

                        }

                        首先__asm__是一个gcc的关键字,表示接下来要嵌入汇编代码。volatile

关键字告诉gcc对这段代码不进行任何优化。

                        __asm__的第一个参数是一个字符串,代表汇代码的文本。这里的汇编代码只有一句: int $0x80,这就要调用0x80号中断

                        "=a"(__res)表示用eax(a表示eax)输出返回数据并存储在__res里。

                        "0"(__NR_#name)表示__NR_#name为输入,"0"指示由编译器选择和输出相同的寄存器(eax)来传递参数。

                        更直观一点,可以把这段汇编改写为更为可读的格式:

                        main-->fork:

                        pid_t fork(void)

                        {

                                long __res;

                                $eax=__NR_fork

                                int $0x80

                                __res = $eax

                                __syscall_return(pid_t, __res);

                        }

                        如果系统调用本身由参数要如何实现呢?下面是x86Linux下的syscall1,用于带1个参数的系统调用:

                        #define _syscall2(type, name, type1, arg1)

                        type name(type1, arg1)

                        {

                                long __res;

                                __asm__ volatile("int $0x80"

                                        : "a=" (__res)

                                        : "0" (__NR_##name), "b"((long)(arg1)));

                                __syscall_return(type, __res);                  

                        }

                        这段代码和_syscall0不同的是,它多了一个"b"((long)(arg1))。这一句的意思是先把arg1强制转换为long,然后存放在EBX(b代表EBX)里作为输入。编译器还会生产相应的代码来保护原理的EBX的值不被破坏。

                        可见,如果系统调用由一个参数,那么参数通过EBX来传入。x86下Linux支持的系统调用参数至多由6个,分别使用6个寄存器来传递,它们分别是EBX,ECX,EDX,ESI,EDI和EBP。

                        当用户调用某个系统调用的时候,实际是执行了以上一段汇编代码。CPU执行到int $0x80时,会保存现场以便恢复,接着会将特权状态切换到内核态。然后CPU便会查找中断向量表中的第0x80号元素。

                切换堆栈

                        在实际执行中断向量表中的第0x80号元素所对应的函数之前,CPU首先还要进行栈的切换。在Linux中,用户态和内核态使用的是不同的栈,两者各自负责各自的函数调用,互不干扰。但在应用程序调用0x80号中断时,程序的执行流程从用户态切换到内核态,这时程序的当前栈必须也相应的从用户栈切换到内核栈。从中断处理函数返回时,程序的当前栈还要从内核栈切换回用户栈。

                        所谓的"当前栈",指的是ESP的值所在的栈空间。如果ESP的值位于用户栈的范围内,那么程序的当前栈就是用户栈,反之亦然。此外,寄存器SS的值还应该指向当前栈所在的页。所以,将当前栈由用户栈切换为内核栈的实际行为就是:

                        1、保存当前的ESP,SS的值。

                        2、将ESP,SS的值设置为内核栈的相应值。

                        反过来,将当前栈由内核栈切换为用户栈的实际行为则是:

                        1、恢复原来ESP,SS的值。

                        2、将用户态的ESP,SS的值保存在内核栈上。

                        当0x80号中断发生的时候,CPU除了切入内核态之外,还会自动完成下列几件事:

                        1、找到当前进程的内核栈(每一个进程都有自己的内核栈)。

                        2、在内核栈中依次压入用户态的寄存器SS,ESP,EFLAGS,CS,EIP。

                        而当内核从系统调用中返回的时候,需要调用iret指令来回倒用户态,iret指令则会从内核栈里弹出寄存器SS,ESP,EFLAGS,CS,EIP的值,使得栈恢复到用户态的状态。这个过程可以用图3来表示。

图3 中断时用户栈和内核栈切换

 

相关文章:

系统调用与API

系统调用介绍 什么是系统调用 为了让应用程序有能力访问系统资源,也为了让程序借助操作系统做一些由操作系统支持的行为,每个操作系统都会提供一套接口,以供应用程序使用。系统调用涵盖的功能很广,有程序运行所必需的支持&#xf…...

OpenPCDet系列 | 5.4.1 DenseHead中的AnchorGenerator锚框生成模块

文章目录 AnchorGenerator模块AnchorGenerator.generate_anchors函数 AnchorGenerator模块 首先,根据点云场景将其划分为一个个grid,这个grid size是可以通过配置文件设定的点云场景方位和voxel大小计算出来的。 POINT_CLOUD_RANGE: [0, -39.68, -3, 6…...

【开发者指南】如何在MyEclipse中使用HTML或JSP设计器?(上)

MyEclipse v2022.1.0正式版下载 一、HTML & JSP 可视化设计器 本文简要介绍了 MyEclipse HTML 和 JSP Web 设计器的概念、功能和基本操作过程。这两个设计器具有相似的功能和相同的操作模型,但本文为专门针对其类型的内容。本文档中的示例是使用 MyEclipse HT…...

Node开发Web后台服务

简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。 能方便地搭建响应速度快、易于扩展的网络应用&#…...

Linux下对mmap封装使用

Linux下对mmap封装使用 1、mmap简介2、Linux下mmap使用介绍2.1、mmap函数2.2、munmap函数 3、对mmap进行封装4、对封装类MEM_MAP进行测试5、mmap原理6、源代码下载 1、mmap简介 mmap即memory map,是一种内存映射文件的技术。mmap可以将一个文件或者其它对象映射到进…...

深入了解云计算:发展历程、服务与部署模型、未来趋势与挑战

开篇博主 bluetata 的观点:PaaS 服务必将是未来10年云计算权重最高的趋势(05/02/2023 15:32) 文章目录 一、前言二、认识了解云计算2.1 什么是云计算2.1.1 维基百科上的云计算定义2.1.2 NIST 标准云计算定义2.1.3 如果被面试如何解释云计算 2…...

使用乐鑫 Web IDE 助力物联网开发

乐鑫 Web IDE 是基于 Eclipse Theia 的框架,支持 ESP-IDF VS Code 插件同时具备多项辅助工具。您可以观看我们在 Espressif DevCon22 上的演示视频​​​​​​​,了解它的实际应用。 【乐鑫开发者大会-21】搭载 ESP-IDF Visual Studio Code 插件的乐鑫 …...

Maven(5)---Maven的部署和发布

Maven的部署和发布 在前面的博客中,我们已经学习了Maven的基础知识、依赖管理、插件和生命周期,以及多模块项目管理。本篇博客将介绍Maven的部署和发布功能。 什么是部署和发布 在软件开发过程中,部署和发布是非常重要的环节。部署是指将软…...

内网渗透之权限维持-黄金白银票据隐藏账户远控-RustDeskGotoHTTP

0x01权限维持-隐藏用户 CreateHiddenAccount工具 CreateHiddenAccount -u test -p Psswrd用户管理能查看到,命令查看看不到,单机版无法删除(不在任何组),域环境(在administrator组中)可以删除 0x02权限维持-黄金白银票据 ⻩⾦票据⽣成攻…...

动态规划——带权活动选择

带权活动选择Time Limit: 3000 MSMemory Limit: 1000 KB Description 给定n个活动&#xff0c;活动ai表示为一个三元组(si,fi,vi)&#xff0c;其中si表示活动开始时间&#xff0c;fi表示活动的结束时间&#xff0c;vi表示活动的权重, si<fi。带权活动选择问题是选择一些活…...

软考A计划-真题-分类精讲汇总-第十八章(面向对象程序设计)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…...

【C++ 入坑指南】(09)数组

文章目录 简介一维数组1. 定义2. 特点3. 用途4. 示例 二维数组1. 定义2. 用途3. 示例 简介 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 一维数组 1. 定义…...

Vue.js

文章目录 Vue&#xff08;前端框架&#xff09;data基本语法v-bind&#xff08;属性&#xff09;v-if&#xff08;条件&#xff09;v-formethods事件v-model表单绑定todolist&#xff08;添加删除展示内容&#xff0c;含上下移动&#xff09;es6语法生命周期函数axios发送ajax请…...

博士毕业答辩流程 注意事项

前言&#xff1a;2023年5月17日14:00-17:00&#xff0c;与实验室其他同学一起旁听了本实验室的博士论文答辩。接下来&#xff0c;我对博士毕业答辩的大致流程进行简要介绍&#xff0c;并对个环节的注意事项进行总结归纳&#xff0c;供毕业生参考。 目录 1. 准备阶段2. 汇报期间…...

拼多多开放平台订单详情接口解析

API接口订单接口是指用于实现订单相关操作的程序接口。通过这个接口&#xff0c;用户可以实现创建、修改、查询和取消订单等功能。 常见的API接口订单接口包括&#xff1a; 创建订单接口&#xff0c;用于实现用户下单操作。 修改订单接口&#xff0c;用于修改已有订单信息。 …...

如何把ipa文件(iOS安装包)安装到iPhone手机上? 附方法汇总

苹果APP安装包ipa如何安装在手机上&#xff1f;很多人不知道怎么把ipa文件安装到手机上&#xff0c;这里就整理了苹果APP安装到iOS设备上的方式&#xff0c;仅供参考 苹果APP安装包ipa如何安装在手机上&#xff1f;使用过苹果手机的人应该深有感触&#xff0c;那就是苹果APP安…...

由浅入深了解 深度神经网络优化算法

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 导言 优化是从一组可用的备选方案中选择最佳方案。优化无疑是深度学习的核心。基于梯度下降的方法已经成为训练深度神经网络的既定方法。 在最简单的情况下&#xff0c;优化问题包括通过系统地从允许集合中…...

LIN-报文结构

文章目录 协议规范一、字节场二、报文头&#xff08;HEADER FIELDS&#xff09;同步间隔&#xff08;synchronisation break)同步场&#xff08;SYNCH FIELD&#xff09;标识符场&#xff08;IDENTIFIER FIELD&#xff09; 三、数据场&#xff08;DATE FIELDS&#xff09;四、校…...

南京邮电大学通达学院2023c++实验报告(三)

题目 实验题目1 某公司财务部需要开发一个计算雇员工资的程序。该公司有3类员工,工资计算方式如下: (1)工人工资:每小时工资额(通过成员函数设定)乘以当月工作时数(通过成员函数设定),再加上工龄工资。 (2)销售员工资:每小时工资额(通过成员函数设定)乘以当月…...

ISO9000和ISO9001有哪些区别?

作为ISO标准体系的新手&#xff0c;ISO9000和ISO9001是第一个接触到的标准。有些人可能会含糊地表达包含关系的词语&#xff0c;但他们仍然无法真正理解它们。两者的关系是什么&#xff1f;有什么区别&#xff1f;事实上&#xff0c;两者的主要区别体现在以下三个方面: 第一&am…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...