菜鸟笔记
提升您的技术认知

操作系统笔记

进程,线程,协程与并行,并发进程线程协程的区别死锁进程,线程,多线程i++的线程安全性同步和异步孤儿进程和僵尸进程/proc进程信息linux中的分段和分页互斥量 mutex线程进程间通信进程创建进程优先级进程的基础知识进程与线程的区别(面试题)线程的控制(创建,终止,等待,分离)可重入 VS 线程安全死锁的概念一级缓存和二级缓存的理解一句话解说内存屏障 Memory barrierbrk(), sbrk() 用法详解malloc/free函数的简单实现一文讲透 “进程、线程、协程”Linux进程状态线程池的陷阱linux内核学习之进程和线程进程与线程的区别和联系内存寻址linux IO子系统和文件系统读写流程Page cache和buffer cache的区别与联系漫谈linux文件IO多线程和多进程的区别内存泄漏字节、字、位、比特的概念和关系如何避免死锁ANSI是什么编码?CPU寻址范围(寻址空间)CPU 使用率低高负载的原因创建多少个线程合适操作系统下spinlock锁解析、模拟及损耗分析线程堆栈堆和栈的内存分配堆和栈的概念和区别堆和栈的区别,申请方式,程序的内存分配什么是 POD 数据类型Linux内存分配小结--malloc、brk、mmap系统调用与内存管理(sbrk、brk、mmap、munmap)进程描述和控制CPU执行程序的原理编译的基本概念Linux虚拟地址空间布局一个程序从源代码到可执行程序的过程程序的运行机制——CPU、内存、指令的那些事分页内存管理——虚拟地址到物理地址的转换深刻理解Linux进程间通信fork之后父子进程的内存关系fork之后,子进程继承了父进程哪些内容关于协程及其锁的一些认识对协程的一点理解std::thread join和detach区别CAS和ABA问题CAS算法锁和无锁无锁队列的实现Lock-Free 编程锁开销优化以及CAS进程、线程和协程之间的区别和联系多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)Linux 原来是这么管理内存的线程上下文切换怎么玩儿进程和线程通信原理CPU密集型 和 IO密集型cas原理以及Atomic原子类分析改变线程状态的八个方法六种进程间通信方式进程和线程的区别系统调用进程的概念linux共享内存进程间通讯方式进程的调度线程同步的方式mmap和shm 区别

系统调用

阅读 : 132

系统调用

系统调用是用户程序与操作系统内核之间进行交互的桥梁。它允许用户程序请求操作系统提供的服务,例如文件操作、进程控制、通信等。系统调用的整个流程涉及用户空间和内核空间的切换,以及参数传递和结果返回。以下是系统调用的详细流程:

系统调用的触发

用户程序需要操作系统服务时,会通过系统调用来请求。这通常通过以下方式触发:

  1. 使用特定的指令:
    • 在 x86 架构中,通常使用 int 0x80(中断指令)或 syscall 指令来触发系统调用。
    • 在 ARM 架构中,使用 svc 指令。
    • 这些指令会中断用户程序的执行,将控制权转移到操作系统内核。
  2. 设置系统调用号和参数:
    • 在触发系统调用之前,用户程序需要将系统调用号(标识要调用的服务)和参数(如文件名、缓冲区地址等)放置在特定的寄存器或栈中。
    • 例如,在 x86 架构中,系统调用号通常放在 eax 寄存器中,参数放在 ebx、ecx、edx 等寄存器中。

从用户空间切换到内核空间

当用户程序触发系统调用时,CPU 会从用户态切换到内核态,同时操作系统会进行以下操作:

  1. 保存用户态上下文:
    • 操作系统会保存用户程序的上下文,包括寄存器状态(如程序计数器、栈指针等)和 CPU 的当前状态(用户态或内核态)。
    • 这些信息通常保存在内核为每个进程分配的内核栈中。
  2. 切换到内核态:
    • CPU 的特权级别从用户态(较低特权级别)切换到内核态(较高特权级别)。
    • 内核态允许访问系统的全部资源,包括硬件设备和内核内存。
  3. 查找系统调用表:
    • 操作系统根据系统调用号在系统调用表中查找对应的内核函数。
    • 系统调用表是一个数组,每个系统调用号对应一个内核函数指针。

执行内核函数

找到对应的内核函数后,操作系统会执行以下操作:

  1. 参数传递:
    • 内核函数会从寄存器或栈中获取用户程序传递的参数。
    • 如果参数是用户空间的地址(如文件名字符串),内核需要进行地址检查,确保这些地址是合法的。
  2. 执行内核函数:
    • 内核函数根据用户请求执行相应的操作,例如:
    • 打开文件时,内核会查找文件系统,分配文件描述符。
    • 写文件时,内核会将数据写入磁盘缓冲区。
    • 创建进程时,内核会分配内存和资源,创建新的进程控制块(PCB)。
  3. 处理错误和异常:
    • 如果操作失败(如文件不存在、权限不足),内核会设置错误码(如 errno)。

从内核空间返回用户空间

内核函数执行完毕后,操作系统需要将控制权返回给用户程序:

  1. 保存内核态上下文:
    • 操作系统保存内核态的上下文信息,包括内核函数的返回值(通常放在某个寄存器中)。
  2. 恢复用户态上下文:
    • 操作系统从内核栈中恢复用户程序的上下文,包括寄存器状态和程序计数器。
    • 这样用户程序可以从上次中断的地方继续执行。
  3. 切换回用户态:
    • CPU 的特权级别从内核态切换回用户态。
  4. 返回结果:
    • 内核将系统调用的结果(如文件描述符、返回值等)传递给用户程序。
    • 如果发生错误,用户程序可以通过错误码(如 errno)获取错误信息。

系统调用的完整流程示例

假设用户程序要调用 write() 系统调用来写文件,其流程如下:

  1. 用户程序准备参数:
    • 将系统调用号(如 1 表示 write)放入 eax 寄存器。
    • 将文件描述符、缓冲区地址和写入字节数分别放入 ebx、ecx 和 edx 寄存器。
  2. 触发系统调用:
    • 用户程序执行 int 0x80 指令,触发中断。
  3. 切换到内核态:
    • 操作系统保存用户态上下文,切换到内核态。
    • 根据系统调用号 1,查找系统调用表,找到 write() 的内核函数。
  4. 执行内核函数:
    • 内核函数从寄存器中获取参数(文件描述符、缓冲区地址等)。
    • 检查文件描述符是否有效,缓冲区地址是否合法。
    • 将数据从用户空间的缓冲区复制到内核空间的缓冲区。
    • 写入数据到磁盘缓冲区。
    • 如果成功,返回写入的字节数;如果失败,设置错误码。
  5. 返回用户态:
    • 操作系统保存内核态上下文,恢复用户态上下文。
    • 切换回用户态,将返回值放入用户程序的寄存器中。
  6. 用户程序继续执行:
    • 用户程序根据返回值判断写操作是否成功,并继续执行后续代码。

系统调用的性能开销

系统调用涉及用户空间和内核空间的切换,因此会产生一定的性能开销:

  1. 上下文切换开销:
    • 保存和恢复寄存器状态、切换特权级别等操作会消耗时间。
  2. 参数传递和检查开销:
    • 内核需要验证用户空间的地址是否合法,这可能涉及额外的内存访问。
  3. 内核函数执行开销:
    • 内核函数的执行时间取决于系统调用的复杂性(如 I/O 操作可能涉及磁盘 I/O 延迟)。
  4. 系统调用的优化:
    • 现代操作系统通过减少上下文切换的次数、使用更快的切换指令(如 syscall)等方式来优化系统调用的性能。
    • 一些系统调用(如 getpid())可以通过轻量级的机制(如 vsyscall 或 vdso)直接在用户空间执行,避免切换到内核态。

总之,系统调用是用户程序与操作系统交互的重要机制,其流程涉及用户空间和内核空间的切换、参数传递、内核函数执行以及结果返回。虽然系统调用会产生一定的开销,但它是实现操作系统功能的关键机制。