【操作系统基础】系统接口与系统调用

news2025/1/22 9:24:40

本文参考MOOC哈工大操作系统课程与课件
主要基于Linux 0.11系统展开
”Author:Mayiming“

文章目录

  • 一、操作系统接口
    • 1. 什么是操作系统接口?
    • 2. 操作系统接口体现在哪?
    • 3. 命令行是怎么执行代码的?
    • 4. 图形界面是怎么执行代码的?
    • 5. POSIX接口
  • 二、系统调用的实现
    • 1. 什么是系统调用?
    • 2. 为什么要使用系统调用?
    • 3.内核态和用户态
    • 4. 如何进入内核执行代码?
    • 5. 系统调用的过程
    • 6. 系统调用的实现
      • 6.1 INT 0x80的处理过程
      • 6.2 system_call
    • 7. 整体流程

一、操作系统接口

1. 什么是操作系统接口?

如众多接口一样,接口的存在主要是为使用者提供一种方便易懂的途径使用一件东西(不必搞懂这件东西背后的实现原理),接口屏蔽了其背后的复杂性,提供了方便和简洁的使用方法。
操作系统中的接口体现的形式就是函数,函数的存在给使用者提供了方便的调用方法,屏蔽了函数内部实现的细节。

2. 操作系统接口体现在哪?

在linux中体现在命令行、shell、代码中执行的函数等,在windows中体现在界面按钮、各种应用程序。
这些内容的本质都是通过代码实现的,这些代码又是通过操作系统提供的接口来操作计算机硬件实现的具体功能。

3. 命令行是怎么执行代码的?

例如:我们执行以下命令

hostname:~$ ./output "hello"
ECHO:hello

其中output.c代码为

#include <stdio.h>
int main(int argc, char * argv[])
{ printf(“ECHO:%s\n”, argv[1]); }

output由output.c编译而来

gcc –o output output.c

命令在shell中执行,而shell依然是一段代码 /bin/sh

int main(int argc, char * argv[])
{ char cmd[20];
while(1) { scanf(%s”, cmd);  % 无限循环  shell永不退出
if(!fork()) {exec(cmd);}   % 创建进程-执行命令 (这里的cmd就是output中的代码)
else {wait();} } //while(1)

4. 图形界面是怎么执行代码的?

在这里插入图片描述
图像界面的框架主要由上图所示,主要流程为:

  1. 硬件输入(比如鼠标点击,键盘输入等)引发中断
  2. 进入系统消息队列,此时在操作系统内核层面
  3. 应用程序从内核中将消息队列取出,进入WinMain()消息循环
  4. 进入WinProc程序,执行程序

5. POSIX接口

POSIX: Portable Operating System Interface of Unix(IEEE制定的一个标准族)
POSIX 提供了一组标准的操作系统接口,使得软件可以在支持POSIX接口的操作系统之间移植。
在这里插入图片描述

二、系统调用的实现

1. 什么是系统调用?

系统调用(system call)其中system指的就是操作系统内核(前文【操作系统启动过程】中的system模块),call就如在汇编中call指令一样(跳转到call后面标签指向的程序地址进行执行)。
那么将上述两者结合起来,system call就是指一段代码(用户态)需要跳转到system模块中执行某一段代码(内核态)的过程,至于用户态和内核态到后面会解释。

2. 为什么要使用系统调用?

下面红色代码代表在内存低地址(system 系统内核的地址段中),蓝色代码代表在用户程序所在的地址段中。
在这里插入图片描述
为什么main()不能直接调用whoami()?

答案是为了系统安全考虑,如果用户程序可以任意访问任意内存,那么你的计算机、操作系统、各种信息将全部暴露在你下载的应用程序面前,如果遇到恶意程序这是非常恐怖的。另一方面,是为了系统稳定性考虑,系统内核中有许多重要的代码和数据(IDT,GDT),如果遇到应用程序纂改也会导致系统崩溃。

3.内核态和用户态

在这里插入图片描述

如何实现内核态和用户态的隔离的?

答案是通过硬件设计实现的,也只能通过硬件设计实现。
具体是通过对寄存器的检查实现的,CPL代表CS寄存器的最低两位,RPL为DS寄存器的最低两位。

4. 如何进入内核执行代码?

硬件提供了"主动进入内核的方法",如果无法进入内核执行代码则意味着应用程序无法调动计算机硬件,所以需要一种方法让应用程序安全的进入内核执行代码。

对于Intel x86,那就是中断指令int,int指令将使CS中的CPL从 3 改成 0 ,“进入内核”,还记得CPL = 0意味着内核态,此时代表内核已经打开了大门,DPL >= CPL检查通过即可JMP到系统内核段中执行代码。

5. 系统调用的过程

系统调用的大致过程分为以下三步:
在这里插入图片描述
接下来,又出现几个疑问?

  1. 除汇编外似乎没有直接写过INT指令的代码,那么INT是在哪执行的?
  2. INT中断后操作系统又做了什么事情?

6. 系统调用的实现

此时我们用C语言中的printf()函数作为例子,解析以下系统调用的过程。
在这里插入图片描述1. 代码是最上层是C语言代码printf()
2. 接下来printf()函数内部由C语言函数库解释,其中调用了__syscall3()函数
3. __syscall3() 函数中给出INT的名字为write
4. __syscall3() 函数内部执行了一段内嵌汇编,执行了中断 INT 0x80
5. 执行了INT 0x80引发中断后,就来到了OS内核中,中断返回后会回到用户态
上述过程中,INT 0x80之外的代码都由C库函数解释(在用户态执行),只有INT 0x80中断过程中会在OS内核中执行。

下面对上述中断过程展开介绍:
在linux/include/unistd.h中

#define _syscall3(type,name,atype,a,btype,b,ctype,c)\ % syscall3代表3个参数
type name(atype a, btype b, ctype c) \ 
{ long __res;\   % 定义返回变量
__asm__ volatile(int 0x80:=a”(__res):””(__NR_##name), % __NR_write 为输入,因为name参数为write ,__res为输出
”b”((long)(a)),”c”((long)(b)),“d”((long)(c)))); if(__res>=0) return % fd传给ebx, char *buf传给ecx, count 传给edx
(type)__res; errno=-__res; return -1;}
””(__NR_##name) 等价于代码 a”(__NR_##name) % 意思是将__NR_##name值赋给eax寄存器

eax寄存器放返回值(即__NR_write系统调用号),ebx/ecx/edx存放参数。

在linux/include/unistd.h中
#define __NR_write 4 //一堆连续正整数(数组下标,函数表索引)

6.1 INT 0x80的处理过程

回忆一下在前文【操作系统启动过程】中,head.s代码执行后main函数代码执行了一系列的初始化过程

void sched_init(void) % 初始化系统调用入口 
{ set_system_gate(0x80,&system_call); } % 表示INT 0x80就要调用system_call来处理

在这里插入图片描述
下面这段代码的主要功能就是初始化上面这个IDT表,该表第一行为edx,第二行为eax,将中断处理函数的入口写入到IDT中。
在linux/include/asm/system.h中

#define set_system_gate(n, addr) \
_set_gate(&idt[n],15,3,addr); //idt是中断向量表基址,从该表中找到0x80偏移地址
#define _set_gate(gate_addr, type, dpl, addr)\ //这里dpl就是内核态那里的DPL,gate_addr就是上面那个表的地址,addr是&system_call
__asm__(“movw %%dx,%%ax\n\t” “movw %0,%%dx\n\t”\  // dx=ax dx=%0
“movl %%eax,%1\n\t” “movl %%edx,%2:\ //  %1 = eax, %2 = edx
:”i”((short)(0x8000+(dpl<<13)+type<<8))),“o”(*(( \ // "i"代表立即数,"o"代表内存变量
char*)(gate_addr))),”o”(*(4+(char*)(gate_addr))),\
“d”((char*)(addr),”a”(0x00080000))
// %1 为0x8000 + dpl左移13位 + type左移8位 = 0xEF00 
// %1为gate_addr的低4位赋
// %2 为gate_addr的高四位
// 0x00080000赋给eax
// 将addr赋给edx
// movw位字传送16位,movl为字传送32位
// movl指令以寄存器作为目的时,它会把该寄存器的高4位字节设置为0

此时段选择符为0x0008,即CS=0x0008(CPL=0),回忆前文【操作系统启动过程】中 jmpi 0,8即为跳转到内核代码段,执行其中system_call
在这里插入图片描述

6.2 system_call

在linux/kernel/system_call.s中

nr_system_calls=72
.globl _system_call
_system_call: cmpl $nr_system_calls-1,%eax //eax中存放系统调用号
ja bad_sys_call
push %ds push %es push %fs
pushl %edx pushl %ecx pushl %ebx //调用的参数
movl $0x10,%edx mov %dx,%ds mov %dx,%es //内核数据 edx=0x10,ds=dx=0x10,es=dx=0x10 这个段对应内核数据段
movl $0x17,%edx mov %dx,%fs //fs可以找到用户数据
call _sys_call_table(,%eax,4) //a(,%eax,4)=a+4*eax 
// 4代表每个_sys_call的函数地址占4字节
// _sys_call_table为系统调用表的基地址
pushl %eax //返回值压栈,留着ret_from_sys_call时用
... //其他代码
ret_from_sys_call: popl %eax, 其他pop, iret
// _sys_call_table+4*%eax就是相应系统调用处理函数入口

在include/linux/sys.h中

fn_ptr sys_call_table[]=  // 函数指针表
{sys_setup, sys_exit, sys_fork, sys_read, sys_write,
...};
// sys_write对应的数组下标为4,__NR_write=4

call _sys_call_table(,%eax=4,4)就是call sys_write

7. 整体流程

  1. 开机,执行6.1中的初始化流程,将INT 0x80中断处理函数设为system_call
  2. 用户调用printf()
  3. C库函数展开到_sys_call3()函数
  4. _sys_call3()函数执行INT 0x80中断
  5. 执行system_call,查表得到__NR_write=4
  6. 执行call _sys_call_table(,%eax,4)调用sys_write
    在这里插入图片描述
    在这里插入图片描述

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

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

相关文章

【Pytorch】Anaconda安装Pytorch详解教程(踩坑经验)

文章目录1、查看本机的CUDA版本2、更新NVIDIA驱动程序3、创建并激活Anaconda虚拟环境4、安装Pytorch5、安装过程中的错误6、检验安装结果未来可期1、查看本机的CUDA版本 cmd命令行输入nvidia-smi 2、更新NVIDIA驱动程序 NVIDIA官网&#xff1a;https://www.nvidia.cn/ 根据…

12、Mybatis搭建流程

Mybatis搭建流程 第一步&#xff1a;引入jar包依赖 第二步&#xff1a;搭建mybatis核心配置文件mybatis-config.xml 一般就四个配置 <properties<typeAliases<Environments<mappers里面内容&#xff1a; mybatis核心配置文件的标签顺序 "(properties?,s…

什么是网站权重?网站权重查询方法有哪些?

什么是网站权重&#xff1f;网站权重查询方法有哪些&#xff1f; 什么是网站权重&#xff1f; 网站权重是搜索引擎给网站赋予的权威值。 网站权重不等于网站排名&#xff0c;但是影响网站排名。 网站权重查询的方法&#xff1a; 方法一&#xff1a;用SEO查询工具。 具体操作如下…

进程通信(1) ----- 无名管道和有名管道

文章目录一、实验目的二、实验内容三、实验要求四、实验步骤及操作五、程序源码1. 普通管道 piperw.c2. 无名管道 wrfifo.c3. 有名管道 rdfifo.c一、实验目的 1.了解操作系统中的无名管道和有名管道 2.掌握进程通信中的管道编程模型 二、实验内容 管道是一种进程间通信的方式…

day13_面向对象的三大特征之一(封装)

封装概述 为什么需要封装&#xff1f; 现实生活中&#xff0c;每一个个体与个体之间是有边界的&#xff0c;每一个团体与团体之间是有边界的&#xff0c;而同一个个体、团体内部的信息是互通的&#xff0c;只是对外有所隐瞒。例如&#xff1a;我们使用的电脑&#xff0c;内部…

Java框架篇(来自硅谷的面试题)

目录 一 简单的谈一下SpringMVC的工作流程&#xff1f; 二 说出Spring或者SpringMVC中常用的5个注解&#xff0c;并解释含义 三 简述SpringMVC中如何返回JSON数据&#xff08;北京&#xff09; 四 谈谈你对Spring的理解 五 Spring中常用的设计模式 六 Spring循环…

IDA安装使用

最近学逆向&#xff0c;先备一套工具吧&#xff0c;IDA名声在外&#xff0c;首当其冲&#xff01;&#xff01; 内容主要整理自别的博客&#xff0c; Linux下安装IDA 链接: https://pan.baidu.com/s/1p9elz1a34872LsY1WLJmlA?pwdis2u 这个ida版本为32bit Linux系统准备的&…

Linux生产者消费者与信号量

目录 一.生产者消费者概念 二.模拟实现基于阻塞队列的生产消费模型 2.1概念 2.2构造阻塞队列 三.信号量 3.1原理 3.2信号量函数 3.3信号量模拟互斥功能 一.生产者消费者概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间…

【C语言】十六进制转换为十进制

目录 题目描述 补充知识&#xff1a; 算法分析 优化算法 写在最后 题目描述 输入一个十六进制数字串&#xff0c;将其转换成为对应的整数并输出转换结果&#xff0c;遇到非十六进制数字或字符串结束符&#xff08;\0&#xff09;结束转换。 注意&#xff1a; 输入的字符…

【Java多线程】初识线程及三种创建方式

➤ Java多线程编程【一文全解】 文章目录线程简介进程的创建> 继承 Thread 类> 实现 Runnable 接口> 实现 Callable 接口线程简介 普通的程序中&#xff0c;方法的调用是执行到方法的时候&#xff0c;程序跳转到方法体中进行&#xff0c;是按照顺序进行的&#xff0c;…

说说未来趋势 「元宇宙」是什么?

最近「元宇宙」概念大火&#xff0c;连星爷等各行各业的各路大佬都可以传出消息布局进入这一个领域&#xff0c;那么这是不是意味这IT信息化时代的下一个风口&#xff0c;就是元宇宙呢&#xff1f;按小郭说呀&#xff0c;这目前来看&#xff0c;这个趋势是必然的&#xff0c;就…

Spirng 痛苦源码学习(一)——总起spring(一)

文章目录前言一、总览Spring的bean1&#xff09;bean的过程【先了解具体的生命周期后面弄】2&#xff09;hello spring 简单bean操作二、总览AOP- 1、test coding- 2、- debug- 3、- 总结debug三、总览事务- 1、- test coding- 2、 debugging- 3、 事务失效- 4、事务总结前言 …

cpu设计和实现(流水线暂停)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;数字电路里面流水线的引入&#xff0c;主要是为了提高数据的处理效率。那么&#xff0c;鉴于此&#xff0c;为什么又要对…

ssm宠物商城管理系统源码

在 Internet飞速开展的今天&#xff0c;互联网成为人们快速获取、发布和传 递信息的重要渠道&#xff0c;它在人们学习、工作、生活等各个方面发挥着重要的作用。 因此建立在 Internet应用上的地位显而易见&#xff0c;它已成为政府、企事业单位信息化 建立中的重要组成局部&am…

[附源码]SSM计算机毕业设计网上书店管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【kafka】十五、kafka消费者API

kafka消费者API Consumer消费数据时的可靠性是很容易保证的&#xff0c;因为数据在kafka中是持久化的&#xff0c;故不用担心数据丢失的问题。 由于consumer在消费过程中可能会出现断电宕机的等故障&#xff0c;consumer恢复后&#xff0c;需要从故障前的位置继续消费&#xf…

visual studio 2019 + Qt 开发,使用visual leak detector检测内存泄漏

选择了在vs2019上开发Qt, 遇到了内存泄露问题。还好vs上有方便的visual leak detector&#xff08;vld&#xff09;检测工具。 虽然官网上只支持到vs2015, 但vs2019上也能用。 具体参考这位博主的文章&#xff1a;https://blog.csdn.net/qq_22108657/article/details/1208843…

Redis数据库安装(Windows)

目录 一、下载Windows安装包 二、启动Redis 1.在终端中启动 2.使用start.bat文件启动 3.添加服务启动 三、安装Redis可视化管理工具 1.安装Redis图形客户端 2.连接数据库 一、下载Windows安装包 下载地址&#xff1a;Releases tporadowski/redis GitHub 选择下载相…

单链表的递归详解 (leetcode习题+ C++实现)

文章目录合并两个有序链表翻转链表链表中移除节点合并两个有序链表 传送门&#xff1a; https://leetcode.cn/problems/merge-two-sorted-lists/description/ 题目要求&#xff1b; 给你两个有序的链表&#xff0c;将这两个链表按照从小到大的关系&#xff0c;合并两个链表为…

Mybatis快速入门

Mybatis安装与配置 Mybatis概述 Mybatis本质上是一个别人写好的框架&#xff0c;用于简化 JDBC 开发&#xff0c;通过Mybatis框架&#xff0c;可以极大的降低JDBC的开发难度。 官方文档&#xff1a;https://mybatis.org/mybatis-3/zh/index.html Mybatis快速入门 需求&…