首页 > 其他分享 >Lec 04 系统调用

Lec 04 系统调用

时间:2024-11-12 13:08:36浏览次数:1  
标签:文件 Lec 04 -- 系统 调用 内核 进程

Lec 04 系统调用

(参考来源:上海交通大学并行与分布式系统研究所+操作系统课程ppt)
Creative Commons Attribution 4.0 License

Contents

4.1 系统调用

  • 硬件提供了一对指令svc/eret指令在用户态/内核态间切换
  • 系统调用
    (1) 用户与操作系统之间,类似于过程调用的接口
    (2) 通过受限的方式访问内核提供的服务

alt

4.1.2 AArch64下常见的Linux系统调用

alt

4.1.3 系统调用举例

int main()
{
    write(1, "hello, world\n", 13);
    _exit(0);
}

转换成汇编语言

  .section .rodata
  .LC0:
    .string "hello, world\n"
  .text
  .align 2
  .global main
  .type main, %function
main:
  // First, call write(1, "hello, world\n", 13)!
  movq x8, #0x40		// write is system call 64
  movq x0, #0x1		// Arg1:stdout has descriptor 1
  adrp x3, .LC0	
  add x1,x3,:lo12:.LC0	// Arg2:Hello world string
  movq x2, #0xd		// Arg3:string length
  svc			// Make the system call
  // Next, call exit(0)
  movq x8, #0x5d		// _exit is system call 93
  movq x0, #0x0		// Arg1:exit status is 0
  svc 			// Make the system call

4.1.3 系统调用的参数传递(常见软件的约定)

  • 最多允许8个参数
    (1) x0-x7寄存器
    (2) x8用于存放系统调用编号
  • 返回值存放于x0寄存器中

4.1.4 系统调用返回值与errno

  • 系统调用通过寄存器向应用传递返回值
    (1) 一般设置为 -errno
    (2) 库对系统调用的 wrapper code 会将系统调用的返回值转换为库函数形式的返回值

  • 寄存器放不下,只能通过内存传参
    (1) 将参数放在内存中,将指针放在寄存器中传给内核
    (2) 内核通过指针访问相关参数
    (3) 存在安全的隐患(后续课程会进一步介绍)

4.1.4 如何跟踪系统调用

alt

4.1.5 系统调用流程图

alt

4.2 系统调用优化(Virtual Dynamic Shared Object)

内核将一部分数据通过只读的形式共享给应用,允许应用直接读取。

4.2.1 动机

  • 系统调用的时延不可忽略
    (1) 尤其是调用非常频繁的情况
    (2) 系统调用实际执行逻辑很简单
  • 如何降低系统调用的时延?
    (1) 特权级切换造成的时间开销
    (2) 如果没有特权级切换,那么就不需要保存恢复状态

4.2.2 举例:gettimeofday

  • 内核定义
    (1) 在编译时作为内核的一部分
  • 用户态运行
    (2) 将gettimeofday的代码加载到一块与应用共享的内存页
    (3) 这个页称为:vDSO
    -- Virtual Dynamic Shared Object
    (4) Time 的值同样映射到用户态空间(只读)
    -- 只有在内核态才能更新这个值

4.2.3 Linux的vDSO

alt

4.3 系统调用优化:FLEX-SC

4.3.1 动机

  • 如何进一步降低系统调用的时延?
    (1) 不仅仅是 gettimeofday()
  • "时间都去哪儿了?"
    (1) 大部分是用来做状态的切换
    (2) 保存和恢复状态 + 权限的切换
    (3) Cache pollution
  • 是否有可能在不切换状态的情况下实现系统调用?

4.3.2 Flexible System Call

  • 一种新的syscall机制
    (1) 引入 system call page ,由 user & kernel 共享
    (2) 用户进程可以将系统调用的请求 push 到 system call page
    (3) 内核会从system call page poll system call 请求
  • Exception-less syscall
    (1) 将系统调用的调用和执行解耦,可分布到不同的CPU核

4.3.3 exception less system call

alt

举例

alt

Kernel 填充syscall的返回值

4.3.4 单核上的单线程/多线程系统调用

  • 单内核单线程
    (1) 用户应用将多个系统调用推至系统调用页表
    (2) 切换到内核线程,内核线程从系统调用页表拉取系统调用。
    (3) 执行完系统调用后切换到应用态
  • 单内核多线程
    (1) 线程1将系统调用推至系统调用页表,并且切换到下一个线程,直到所有线程推送结束。
    (2) 下陷到内核态。内核拉取页表中的system call。
    (3) 执行完所有系统调用后返回内核态。

4.4 从应用视角看操作系统抽象

alt

4.4.1 进程

1. 分时复用有限的CPU资源

alt

(1) CPU核心数量少于应用程序数量,如何运行?
(2) 单个CPU核心如何运行多个应用程序?

  • 分时复用CPU
    (1) 让多个应用程序轮流使用处理器核心
    (2) 何时切换:操作系统决定
    -- 运行时间片(例如100ms)
    (3) 高频切换:看起来是多个应用“同时”执行

alt

2. 应用程序与进程
  • 通常一个应用程序对应一个进程
    (1) 在shell中输入可执行文件的名称
    -- shell创建新进程,可执行文件在新进程中执行
    (2) 在图形界面双击应用图标
  • 多进程程序:应用程序亦可自行创建新进程
    (1) 创建新进程,在新进程中运行其他应用程序或与自己一样的程序
3. 进程在操作系统中的实现:状态数据
  • 操作系统提供进程的抽象用于管理应用程序
    (1) 进程标识号(Process ID, PID)
    (2) 运行状态
    -- 处理器上下文(CPU Context)
    (3) 地址空间
    (4) 打开的文件
4. 进程展示效果
  • 进程抽象为应用程序提供了“独占CPU”的假象
    (1) 程序开发不用考虑如何与其他程序共享CPU
    (2) 简化编程
  • 进程相关的系统调用
    (1) 创建进程
    (2) 让进程执行指定的程序
    (3) 退出进程
    (4) 进程间通信

4.5 进程切换

4.5.1 处理器上下文(CPU Context)

  • 操作系统为每个进程维护处理器上下文
    (1) 包含恢复进程执行所需要的状态
    (2) 思考:进程A执行到main函数任意一条指令,切换到进程B执行,一段时间后,再切回到进程A执行
    -- 为完成此过程,有哪些状态需要保存?
    (3) 具体包括:
    -- PC寄存器值,栈寄存器值,通用寄存器值,状态寄存器值

4.5.2 进程切换的时机

  • 异常导致的上下文切换
    (1) Timer中断(如基于时间片的多任务调度)

  • 用户执行系统调用并进入内核
    (1) 如:read/sleep等会导致进程阻塞的系统调用
    (2) 即使系统调用不阻塞执行,内核也可以决定执行上下文切换,而不是将控制权返回给调用进程

举例1

alt

举例2

alt

4.6 进程相关接口

4.6.1 获取进程ID

  • 进程 ID
    (1) 每个进程都有唯一的正数PID
  • Getpid()
    (1) 返回调用进程的PID
  • Getppid()
    (1) 返回调用进程父进程的PID
    (2) 父进程:创建该进程的进程

alt

4.6.2 Exit 函数

alt

exit函数终止进程并带上一个status状态

4.6.3 Fork 函数

alt

  • 调用一次
    (1) 在父进程中
  • 返回两次
    (1) 在父进程中,返回子进程的PID
    (2) 在子进程中,返回0
  • 返回值提供了唯一明确地区分父进程和子进程执行的方法

alt

alt

4.6.4 execve 函数

alt
alt

  • 加载和运行
    (1) filename:可执行文件名;argv:参数列表,envp:环境变量列表
  • execve 只调用一次,且永远不会返回
    (1) 仅仅在运行报错的时候,返回调用程序
    (2) 例:找不到filename标识的文件

4.6.5 Linux下的僵尸进程

  • 进程终止后,内核不会立刻销毁该进程
    (1) 不再运行,但仍然占用内存资源
  • 进程以终止态存在,等待父进程回收
  • 当父进程回收终止的子进程
    (2) 内核把子进程的exit状态传递给父进程
    (3) 内核移除子进程,此时子进程才被真正回收
  • 终止状态下还未被回收的进程就是僵尸进程
  • 如果父进程在自己终止前没有回收僵尸子进程
    (4) 内核会安排init进程回收这些子进程
  • init进程
    (5) PID为1
    (6) 在系统初始化时由内核创建

4.6.6 waitpid函数

alt
alt

返回值:成功返回子进程 PID,出错返回 -1

  • pid>0:等待集合中只有pid子进程
  • pid=-1:等待集合包括所有子进程
  • 如果没有子进程:返回-1,errno = ECHILD
  • 如果等待被中断:返回-1,errno = EINTR
  • options=0
    (1) 挂起调用进程,等待集合中任意子进程终止
    (2) 如果等待集合中有子进程在函数调用前已经终止,立刻返回
    (3) 返回值是导致函数返回的终止子进程pid
    (4) 该终止子进程被内核回收
  • options=WNOHANG
    (1) 如果等待集合中没有终止子进程,立刻返回0
  • options=WUNTRACED
    (2) 除了返回终止子进程的信息外,还返回因信号而停止的子进程信息
  • options=WNOHANG|WUNTRACED
    (1) 带回被回收子线程的exit状态
    (2) status指针不为NULL
    (3) status包含导致子进程进入终止状态的信息
    (4) wait.h文件包含了若干宏定义,用于解释status

4.6.7 小结

alt

4.7 内存

alt
alt

4.7.1 虚拟内存

  • 虚拟地址空间
    (1) 应用进程使用虚拟地址访问内存
    (2) 所有应用进程的虚拟地址空间都是统一的(方便开发)

  • 地址翻译
    (1) CPU按照OS配置的规则把虚拟地址翻译成物理地址
    (2) 翻译对于应用进程是不可见的(无需关心)

4.7.2 虚拟内存和物理内存

alt

虚拟内存具有独立而统一的地址空间

alt

5. ELF 文件格式

5.1 目标文件

  • 可执行目标文件
  • 可重定位目标文件(.o)
  • 共享目标文件(.so)
    (1) 特殊的可重定位目标文件
    (2) 可以加载到内存中
    (3) 支持动态链接:加载时或运行时

5.2 ELF:可执行可链接格式

  • 目标文件的标准二进制格式
    统一格式,又被称为ELF二进制文件
  • 用于BSD Unix和Linux
    最早用于AT&T System

5.3 ELF格式:可重定位目标文件

alt

(1) ELF 头部(ELF Header)

  • ELF文件的第一个部分
  • 通常用于存元数据
    -- Magic number (‘0x7f’ ‘E’ ‘L’ ‘F’)
    -- 类型(.o, .so, 可执行)
    -- 机器架构
    -- 字节顺序(大小端)
    -- 节头部表的位置(文件内偏移)

(2) 节头部表(Section Header Table)

  • 节(section)
    -- ELF文件中除了头部和头部表划分为若干区域
    -- 每一个节在文件中时一块连续的字节(可能为空)
    -- 互不重叠
  • 每一个节都有一个节头部描述
    -- 节头部的一项

alt

  • sh_name
    -- 节名称(在.strtab节中的偏移)
  • sh_addr
    -- 节在加载到内存后,在内存的起始地址
  • sh_offset
    -- 节在文件中的偏移(字节数)
  • sh_size
    -- 节的大小(字节数)
  • sh_addralign
    -- 对齐要求

(3) ELF字符串表(.strtab)

  • 记录一系列C风格字符串
    -- 以'\0’结尾
  • 表示符号名或节名
    -- 使用时记录字符串在表中的偏移(index)

alt

(4) 用以调试的节

  • .debug
    -- 调试符号表,包括变量(全局、局部)、typedef、C源文件
    -- gcc –g生成
  • .line
    -- C源文件的行数与.text节中指令的映射

(5) 代码与数据节

  • .text:代码
  • .rodata:只读数据
  • .data:初始化的全局变量和静态变量
  • .bss:未初始化的全局变量和静态变量
    -- “Block Started by Symbol”,“Better Save Space”
    -- 不占文件空间,但是在节头部表中有记录
    -- 运行时分配内存,默认为0

5.4 可执行目标文件

alt

alt

  • ELF头部(ELF header)
    -- 整体信息
    -- 程序入口 (e_entry)
    -- 程序头部表信息
    • 在文件中的起始位置(e_phoff)
    • 大小(e_ehsize)
    • 每个条目的大小(e_phentsize)
    • 条目数量(e_phnum)

alt

程序头部表

  • Program (Segment) Header Table
    p_type
  • PT_LOAD (1): 可加载段
    p_flags
    -- 运行时权限 (rwx)
  • p_offset
    -- 段在文件中的起始偏移
  • p_filesz
    -- 段在文件中的大小( 段在内存中的大小p_memsz)
  • p_vaddr
    -- 段在内存中的起始(虚拟)地址
  • p_paddr (不常用,x86、ARM 下不用)
  • p_memsz
    -- 段在内存中的大小(p_filesz)
  • p_align
    -- 段起始地址的对齐要求
    通常为2^12 (4K) 或 2^21 (2M)

alt

alt

6. 文件

6.1 UNIX 文件

  • Unix 文件是一串字节序列。
    -- \(B_0,B_1,\ldots,B_k,\ldots,B_m\)
  • 所有的IO设备都被抽象成文件
    -- 如:网络设备,硬盘,终端
    -- Unix提供一个基于文件的底层应用接口,即UNIX I/O
  • 所有输入,输出都是通过读,写文件完成
    -- 所有的输入输出都具有统一的表现形式

6.2 文件类型1

  • 普通文件(regular file):包含任意数据
    -- 从应用程序的角度来看有两种
    • 文本文件:仅包含ASCII和UNICODE字符
    • 二进制文件:除了文本文件以外的所有
      -- 从内核的角度来看没有区别

6.3 文件类型2

  • 目录(directory)也是一个文件
    -- 由有链接(links)构成
    -- 每个链接将一个文件名映射到一个文件(或目录)
    -- 每个目录至少有两个链接:
    • . (dot):到文件夹本身的链接
    • . . (dot-dot):到上一层文件夹的链接
      -- 目录相关指令:mkdir、ls、rmdir等

6.4 目录层级

  • Linux内核使用层次化目录来组织所有文件
    -- /:代表根目录
    -- 每个文件都是根目录直接或间接的后代

alt

6.5 文件类型3

  • 套接字(Socket)也是文件,用于跨网络进程交互
  • 其他文件类型包括:
    -- 命名管道(named pipes)
    -- 符号连接(symbolic links)
    -- 字符/块设备(character/block devices)
    ...

6.6 打开文件

  • 应用准备访问一个IO设备
    -- 内核打开相关文件,并返回一个非负整数,作为文件标识符(file descriptor, fd)
    • fd代表该文件,用于之后对文件进行操作
      -- 内核跟踪记录每个进程的所有打开文件的信息
    • 对于每个打开文件,维护一个文件内偏移k
    • 应用可以通过seek函数,显式改变当前文件内偏移k
    • 应用只需要记录内核返回的文件标识符

alt

6.7 关闭文件

  • 不再需要访问文件
  • 内核操作如下
    -- 释放在文件打开时创建的数据结构
    -- 把文件标识符返回到可用的标识符池
  • 进程终止时的默认行为
    -- 内核关闭所有打开的文件
    -- 内核释放内存资源

alt

6.8 读写文件

  • 读操作:
    -- 从文件中复制m>0个字节到内存中
    • 从当前文件的位置k开始,并更新k+=m
      -- 如果从k开始到文件末尾的长度小于m,触发条件end-of-file(EOF)
    • EOF可以被应用检测
    • 但是文件末尾实际上不存在EOF字符

alt

alt

6.9 文件:对所有设备的抽象

  • 存储设备
    -- File
  • 网络设备
    -- socket
  • 其他设备
    -- 同样是fd

alt

6.10 操作系统设备的示例

alt

alt

标签:文件,Lec,04,--,系统,调用,内核,进程
From: https://www.cnblogs.com/mumujun12345/p/18541614

相关文章

  • Lec 03 系统指令集架构
    Lec03系统指令集架构(参考来源:上海交通大学并行与分布式系统研究所+操作系统课程ppt)CreativeCommonsAttribution4.0LicenseContents3.1回顾:特权级的必要性一台计算机上同时运行多个应用程序,如何保证不同应用间的隔离?如果所有的应用均能完全控制硬件计算资源,则会......
  • lec 02 arm汇编语言基础
    Lecture02:ARM汇编基础Contents为什么学习ARM/ISA汇编从C到汇编理解arm汇编理解机器执行1为什么学习汇编和指令集架构?1.令人困惑的应用表现2.指令集架构ISA(InstructionSetArchitecture)CPU向软件(应用程序和操作系统)提供的接口。理解软件在CPU上的运行(OS设......
  • 介绍Go中的for select case和chan 和goroutine
    好的,让我们详细分析您提供的代码片段:for{select{casemsg:=<-sensorChan://处理消息的代码}}1.整体结构概述这段代码使用了Go语言的并发控制结构,结合无限循环(for)、选择语句(select)、以及case分支来处理从sensorChan通道接收......
  • vm安装ubuntu 20.04 server并用vscode进行ssh远程连接
    最近写专业课作业需要用到vscode进行SSH连接ubuntuserver,自己摸索了一会终于实现效果,秉承着“取之于网络,还之于网络”的开源精神写了一篇总结博客,欢迎大家参考,如有不足之处也欢迎大家指出。文章参考:VSCodeSSH远程连接Ubuntu_visualstudio连接到本地unbuntu远程系统-CSDN博......
  • 如何使用YOLOv8进行训练变电站电力设备缺陷数据集 共6004张图像 train test val比例为
    表计读数异常、表计外壳破损、异物鸟巢、空中漂浮物、表盘模糊、表盘破损、绝缘子破裂、地面油污、硅胶桶变色变电站电力设备缺陷数据集共6004张图像traintestval比例为7:2:1有txt和yaml两种格式数据集共7种标签,包括表计读数异常、表计外壳破损、异物鸟巢、空中漂浮......
  • 【Playwright + Python】系列(九)Playwright 调用 Chrome 插件,小白也能事半功倍
    哈喽,大家好,我是六哥!今天我来给大家分享一下如何使用playwight调用chrome插件,面向对象为功能测试及零基础小白,我尽量用大白话的方式举例讲解,力求所有人都能看懂,建议大家先收藏,以免后面找不到。......
  • 904. 水果成蓝
    题目自己第一次写的,结果超时了classSolution{public:inttotalFruit(vector<int>&fruits){intr=1;intres=1;while(r<fruits.size()){intl=r-1;intn=1;//代表不同的种类个数......
  • P8162 [JOI 2022 Final] 让我们赢得选举 (Let's Win the Election) 题解
    P8162[JOI2022Final]让我们赢得选举(Let'sWintheElection)题解朴素的想法是先抓一部分人,再一起去发表演讲。这样就要按\(b\)的值从小到大排序,枚举选择的一部分\(b\)值,在后面挑选一些最小的\(a\)选择即可。但这样显然是错误的。观察到\(n\le500\),显然是\(O(n^3......
  • Idea调用WebService
    Idea调用WebService​WebService是一种基于网络的技术,它允许不同的应用程序在互联网上相互通信。要进行WebService对接,以下是一些关键步骤和注意事项:一、理解WebService的基本概念定义:WebService是一种基于标准化协议和格式的应用程序接口(API),它使用XML和HTTP来进行通信......
  • LangGraph进阶:条件边与工具调用Agent实现
    在前两篇文章中,我们讨论了LCEL和AgentExecutor的局限性,以及LangGraph的基础概念。今天,我们将深入探讨LangGraph的高级特性,重点关注条件边的使用和如何实现一个完整的工具调用Agent。条件边的高级用法条件边是LangGraph中最强大的特性之一,它允许我们基于状态动态决定执行流......