diff --git a/articles/20220405-riscv-syscall-and-library.md b/articles/20220405-riscv-syscall-and-library.md new file mode 100644 index 0000000000000000000000000000000000000000..1c555506c553b4c0f59edfa12d8799915c3fbf0a --- /dev/null +++ b/articles/20220405-riscv-syscall-and-library.md @@ -0,0 +1,101 @@ +> Author: TanyoKwok
+> Date: 2022/04/05
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) + +# Syscalls vs Library Routines + +
+ +
+ +在操作系统上运行的每一个程序,比如打开一个文件,几乎都会执行两类调用过程: + +- [System calls](https://en.wikipedia.org/wiki/System_call): + 系统调用提供用户程序与操作系统之间的接口。大部分系统交互式操作需要在内核态执行, + 比如设备 IO 操作和进程间通信。 +- [Library routines](https://en.wikipedia.org/wiki/Library_(computing)): + 库程序通常表示用于用于处理特定任务的可重复使用的子程序。 + +在这篇文章中,我们将主要介绍下这两类的程序的关系。 + +## 内核空间与用户空间 + +在现代操作系统中,虚拟内存通常会被分为**内核空间和用户空间**。这种内存空间分隔的目的 +主要是为了对内存和硬件数据进行保护以防止恶意或错误软件的破坏。 + +内核空间是为执行特权内核程序,内核扩展和设备驱动而预留的内存空间。 +用户空间则是普通用户程序和驱动运行的内存空间。 + +我们所接触的大部分程序和软件都是在用户空间下执行的。用户程序所操作的数据和设备, +可能来自于寄存器,内存系统,文件系统和外设。为了与这些设备 IO 交互,用户程序必须通过特定的 +syscall 来与内核进行交互。但是用户程序通常并不会直接调用 syscall,而是通过可复用的 +library routine 来调用 syscall。 + +## Syscalls and Library Routines + +Syscalls 是应用程序操作系统内核之间最基础的接口。操作系统内核提供了一系列 syscalls,具体可以参考 +**[syscalls(2) — Linux manual page](https://man7.org/linux/man-pages/man2/syscalls.2.html)。** + +一个 library Routine 在调用 syscall 过程中,会完成从用户态到内核态的切化,通常如下: + +- 将唯一的系统调用号和参数拷贝到系统指定的寄存器位置 +- 陷入系统内核态 +([entry.S:handle_exception](https://github.com/torvalds/linux/blob/v5.16/arch/riscv/kernel/entry.S#L21)), +在此之前保存 CPU 寄存器状态([entry.S:L42](https://github.com/torvalds/linux/blob/v5.16/arch/riscv/kernel/entry.S#L42)) +- 执行系统调用 +([entry.S:L169](https://github.com/torvalds/linux/blob/v5.16/arch/riscv/kernel/entry.S#L169)), +加载参数和系统调用号到 `a0-a7` 寄存器,并放回结果到 `a0` 寄存器 +([entry.S:L221](https://github.com/torvalds/linux/blob/v5.16/arch/riscv/kernel/entry.S#L221)) +- 在返回用户态之前,恢复 CPU 寄存器状态 +([entry.S:L268](https://github.com/torvalds/linux/blob/v5.16/arch/riscv/kernel/entry.S#L268)) +- 如果在返回用户态时,系统调用返回错误码,则设置 `errno` + +```text +Arch/ABI Instruction System Ret Ret Error Notes + call # val val2 +─────────────────────────────────────────────────────────────────── +riscv ecall a7 a0 a1 - +x86-64 syscall rax rax rdx - 5 + +Arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes +────────────────────────────────────────────────────────────── +riscv a0 a1 a2 a3 a4 a5 - +x86-64 rdi rsi rdx r10 r8 r9 - +``` + +根据 +**[syscall(2) — Linux manual page](https://man7.org/linux/man-pages/man2/syscall.2.html)** +的 "Architecture calling conventions" 一节所描述,RISC-V 用于存储系统调用号的寄存器为 `a7`, +返回值寄存器为 `a0`。`a0-a5` 寄存器用于存储参数。 + +以 [read(2)](https://man7.org/linux/man-pages/man2/read.2.html) 为例, +RISC-V 系统调用寄存器状态设置如下: + +```c +ssize_t read ( int fd, void *buf, size_t count ); +a0(result) = a7(63)( a0(fd), a1(*buf), a2(count) ) +``` + +`a7` 存储 `read` 系统调用号,`a0-a2` 存储 `read` 系统调用的实参;在系统调用返回时,`a0` 作为返回结果的寄存器。 + +## VDSO(Virtual Dynamic Shared Object) + +VDSO 是为了优化系统调用实现的而生的一种机制,这种机制下 kernel 和 cpu 的升级不会影响到 libc 库的实现。VDSO 机制中的 vsyscalls 可以完全在用户空间中运行。它们从 VDSO 页访问数据,这些数据要么是静态的,要么是由内核在 VDSO 内核读写映射页中修改的。典型的 vsyscall 调用的例子有: `getpid` 和 `gettimeofday`。 + +## Syscalls 访问用户空间 + +Syscalls 作为系统和用户程序的接口,在有些情况下需要访问用户空间来交互信息。在内核中访问用户空间必须通过特定的 API,包括 `get_user()`, `put_user()`, `copy_from_user()` ,`copy_to_user()`。调用这些 API 会同时检查用户地址是否有效,如果不是有效的地址则处理相应的异常。这些 API 是内核安全和自我保护的一种方式。 + +## 小结 + +以上内容是理解操作系统 library routines 和 assembly 的基础。这篇文章主要意图是帮助梳理 syscall 和用户 library 之间的关系,区别,以及它们之间如何进行交互。如果读者对相关内容感兴趣,可以参考更加具体的文献资料。 + +## 参考资料 + +1. [syscall(2) — Linux manual page](https://man7.org/linux/man-pages/man2/syscall.2.html) +2. [syscalls(2) — Linux manual page](https://man7.org/linux/man-pages/man2/syscalls.2.html) +3. [Linux syscall and RISC-V assembly](https://popolon.org/gblog3/?p=1049&lang=en) +4. [Adding a New System Call](https://www.kernel.org/doc/html/latest/process/adding-syscalls.html) +5. [System Calls](https://linux-kernel-labs.github.io/refs/heads/master/lectures/syscalls.html) +6. [User space and the libc interface](https://www.win.tue.nl/~aeb/linux/lk/lk-3.html) +7. [[PATCH 3/9] RISC-V: Generic library routines and assembly](https://lore.kernel.org/lkml/20170704195102.3974-4-palmer@dabbelt.com/) diff --git a/articles/images/syscall/Linux_kernel_System_Call_Interface_and_glibc.svg b/articles/images/syscall/Linux_kernel_System_Call_Interface_and_glibc.svg new file mode 100644 index 0000000000000000000000000000000000000000..96fa2a68f5c5ff3ab8f3e93e603cc77deda79658 --- /dev/null +++ b/articles/images/syscall/Linux_kernel_System_Call_Interface_and_glibc.svg @@ -0,0 +1,1246 @@ + + + + + Linux kernel System Call Interface (SCI) and GNU C Library (glibc) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Linux kernel System Call Interface (SCI) and GNU C Library (glibc) + + 2014-02-25 + + + Shmuel Csaba Otto Traian Xerxes + + + + + Shmuel Csaba Otto Traian Xerxes + + + en-US + + + Linux kernel + GNU C Library + System Call Interface + POSIX + source code portability + Linux system calls + + + Illustrates the coaction of Linux kernel, GNU C Library and POSIX-compatible as well as Linux-only applications + + + Ivan T. Bowman et al."Conceptual Architecture of the Linux Kernel", "Concrete Architecture of the Linux Kernel", "Linux as a Case Study: Its Extracted Software Architecture" + + + + https://commons.wikimedia.org/wiki/File:Linux_kernel_System_Call_Interface_and_glibc.svg + + + + + + + + + + + + + + + + + + + + + + + + Linuxprocessscheduler + + + Linuxmemorymanager + + IPCmanager + I/Ointerface + + Networkinterface + + Virtualfilesystem + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ApplicationPOSIX-compatible + Linux-specificApplication + + + system calls + + + + system calls + + + + functioncalls + + + + functioncalls + + by Shmuel Csaba Otto Traian; GNU FDL 1.3 & CC-BY-SA 3.0; created 2014-02-27, last updated 2014-03-25 + +