系统调用

  • ​​与内核通信​​
  • ​​API、POSIX和C库​​
  • ​​系统调用​​
  • ​​系统调用号​​
  • ​​系统调用的性能​​
  • ​​系统调用处理程序​​
  • ​​指定恰当的系统调用​​
  • ​​参数传递​​
  • ​​系统调用的实现​​
  • ​​实现系统调用​​
  • ​​参数验证​​
  • ​​系统调用上下文​​

本系列博客追寻《Linux内核设计与实现-Robert Love》,各个Linux机中的内核源代码不一,因此直接下载官网内核源码

参考书籍:《Linux内核设计与实现-Robert Love》

目录

描述

arch

特定体系结构的源码

block

块设备I/O层

crypty

加密API

Documentation

内核源码稳文档

drivers

设备驱动程序

firmware

使用某些驱动程序而需要的设备固件

fs

VFS和各种文件系统

include

内核头文件

init

内核引导和初始化

ipc

进程间通信代码

kernel

像调度程序这样的核心子系统

lib

通用内核函数

mm

内存管理子系统和VM

net

网络子系统

samples

示例,示范代码

script

编译内核所需脚本

security

Linux安全模块

sound

语音子系统

usr

早期用户空间代码

tools

在Linux开发中有用的工具

virt

虚拟化基础结构

与内核通信

系统调用在用户空间和硬件设备之间添加了一层中间层,该层主要作用有三个:

  • 为用户空间提供了一层硬件的抽象接口(不用管磁盘类型和文件系统类型)
  • 系统调用保证了系统的稳定和安全(权限,用户类型进行裁决)
  • 实现虚拟内存。

系统调用是用户空间访问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口

Linux的系统调用比大部分OS少得多

API、POSIX和C库

API:一组API应用编程接口定义了一组应用程序使用的编程接口,他们可以实现一个系统调用,或者多个系统调用。

POSIX:在UNIX中,最流行的应用编程接口是基于POSIX的

C库:C库实现了UNIX系统的主要API,包括标准C库函数和系统调用接口,所有C库程序都可以使用C库,此外,C库提供了POSIX的大部分API

系统调用

要访问系统调用(syscall),通常通过C库中定义的函数调用来实现。此外,系统调用还会通过一个long类型的返回值表示成果或者错误(负为错误,0为正确)

系统调用在出现错误的时候C库会把错误码写入error全局变量,通过perror()库函数,可以把该变量翻译成用户可以理解的错误字符串

命名规则:用户态的xxx函数到达内核态会变成sys_xxx函数,这是Linux中所有系统调用都应该遵守的命名规则

系统调用号

在Linux中,每个系统调用都被赋予一个系统调用号。当用户空间的进程执行一个系统调用的时候,这个系统调用号就会指明执行哪个系统调用号(进程不会题记系统调用的名称)

内核记录了系统调用表的所有已注册的系统调用的列表

系统调用的性能

Linux系统调用比其他许多操作执行的要快

系统调用处理程序

用户空间不能直接执行内核代码,由用户态到内核态的机制是软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序(此时的异常处理程序实际上就是系统调用处理程序)

在x86是通过int $0x80

指定恰当的系统调用

因为所有的系统调用陷入内核的方式都一样,所有必须把系统调用号一并传给内核。(在x86上,系统调用号是通过eax寄存器传递给内核的)

参数传递

有时候还需要把参数传递给内核,最简单的方法就是和系统调用号一样用寄存器,下x86-32系统上,ebx、ecx、edx、esi和edi按照顺序存放前五个参数

返回值放在eax寄存器

系统调用的实现

实现系统调用

用途、调用参数、返回值和错误码、多样接口

参数验证

检查参数是否合法有效

系统调用上下文

内核在执行系统调用的时候处于进程上下文,current指向前任务,即引发系统调用的那个进程。

在进程上下文中,内核可以休眠并且可以被抢占

继续加油!