5

深入理解Linux系统调用

 1 year ago
source link: https://www.51cto.com/article/746158.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

深入理解Linux系统调用

作者:码农的荒岛求生 2023-02-10 08:11:43
这样当CPU执行syscall执行时就会跳转到Linux内核中的write函数,同时在执行该函数时也能知道write函数所需要的参数是什么。​

​大家好,我是小风哥。

在前两篇文章《为什么计算机需要操作系统​》《​​系统调用与函数调用有什么区别​​》中我们了解了什么是系统调用、为什么需要系统调用、系统调用与函数调用有什么区别,那么在今天的文章中我们从理论来到现实,看看Linux中的系统调用是怎样实现的。

首先我们先来简单复习下之前讲解过的知识。

系统调用和普通的函数调用没有本质区别,普通的函数调用一般调用的是我们自己编写的函数或者其它库函数,而系统调用调用的则是内核中的函数,更学术一点的说法是这样的,所谓系统调用是指用户态程序请求操作系统提供的服务。

一提到服务,大家最先想到的一定是服务器,假设客户端是浏览器,浏览器发送http请求,服务器接收到请求后进行解析然后调用相应的hander,从本质上讲就是客户端触发了服务器端的某个函数的运行,这时我们说客户端请求了服务器端上的服务。

而系统调用与此类似,只不过用户态程序并不是通过http触发了操作系统中某个函数的运行,而是通过机器指令来触发的,因为用户态的App和操作系统运行在同一台计算机系统上,而客户端和服务器端运行在不同的计算机系统中(绝大部分情况下),因此客户端只能通过网络协议http来与服务器进行通信。

图片

更通俗的说法就是所谓系统调用是指用户态的某个函数调用内核中的某个函数。

接下来我们用一段简单的hello world程序看下系统调用,这段程序需要运行在x86_64下:

.datamsg:    .ascii "Hello, world!\n"    len = . - msg.text    .global _start_start:    movq  $1, %rax    movq  $1, %rdi    movq  $msg, %rsi    movq  $len, %rdxsyscall    movq  $60, %rax    xorq  %rdi, %rdisyscall

使用以下命令编译:

$ gcc -c test.S
$ ld -o test test.o

然后执行:

./test
Hello, world!

这段汇编代码成功的打印出了hello world,这段代码是什么意思呢?

注意看.data这一段,这里说的是程序定义了哪些数据,.text段是说程序中包含了哪些执行,我们之前提到进程的内存布局时总是说数据段以及代码段,这里的数据段指的就是汇编中的.data段、代码段指的就是汇编中的.text段,现在你应该明白了吧。

图片

在.text段我们看到了一条略显奇怪的指令,syscall,这条指令是什么意思呢?

我们来翻看一下intel的开发手册:

SYSCALL invokes an OS system-call handler at privilege level 0. It does so by loading RIP from the IA32_LSTAR MSR (after saving the address of the instruction following SYSCALL into RCX). (The WRMSR instruction ensures that the IA32_LSTAR MSR always contain a canonical address.)

这段话告诉我们intel处理器在执行syscall指令时会在内核态调用操作系统的某个函数,即syscall-call handler,这个过程就是所谓的系统调用,我们知道CPU执行某个函数时必须知道某个函数在内存中的地址,那么CPU是怎么知道某个syscall-call handler的内存地址呢?

原来syscall-call handler所在的内存地址存储在寄存器MSR中,那么又是谁将这个地址存储在了寄存器MSR中呢?很显然是操作系统,接下来以Linux为例来讲解。

Linux内核初始化时将syscall-call handler也就是Linux内核中entry_SYSCALL_64函数的地址写入寄存器MSR中:

wrmsrl(MSR_LSTAR, entry_SYSCALL_64);

其中syscall-call handler也就是entry_SYSCALL_64定义在了Linux源码中的arch/x86/entry/entry_64.S,上述初始化寄存器MSR的代码定义在了arch/x86/kernel/cpu/common.c。

现在我们知道了,当CPU执行syscall时会无脑跳转到寄存器MSR中保存的函数地址,也就是entry_SYSCALL_64函数,那么很显然的,所有系统调用的入口都是entry_SYSCALL_64函数,那么操作系统该怎么区分到底是调用的read系统调用还是write等系统调用?

原来,操作系统中给每种系统调用分配了一个序号,就像Linux中这样:

0  common  read      sys_read
1  common  write      sys_write
2  common  open      sys_open
3  common  close      sys_close
4  common  stat      sys_newstat
5  common  fstat      sys_newfstat
6  common  lstat      sys_newlstat
7  common  poll      sys_poll
8  common  lseek      sys_lseek
9  common  mmap      sys_mmap
...

可以看到,0号系统调用表示的是内核中的read函数,1号系统调用表示的内核中的write函数,在进行系统调用时会将表示系统调用类别的序号写入通用寄存器中。

从上面这个表格中可以看到write系统调用的序号是1,因此在hello world程序中我们将1写入寄存器rax中:

movq  $1, %rax

这条指令就表示我们将要调用第1号系统调用,也就是sys_write,hello world程序中后续三条机器指令的函数是:

# 写入文件描述符1
movq  $1, %rdi


# 保存指向字符串的指针
movq  $msg, %rsi


# 写入数据的大小
movq  $len, %rdx

实际上这四条机器指令都是为执行syscall进行的铺垫,也就是执行syscall所需要的参数,可以看到我们进行系统调用传递参数时都是通过寄存器来完成的。

这样当CPU执行syscall执行时就会跳转到Linux内核中的write函数,同时在执行该函数时也能知道write函数所需要的参数是什么。​


Recommend

  • 12
    • 微信 mp.weixin.qq.com 4 years ago
    • Cache

    深入理解Linux VFS和Page Cache

    戳蓝字「TopCoder 」关注我们哦!

  • 5

    Linux阅码场linux上下文切换1周前我们都知道操作系统的一个重要功能就是进行进程管理,而进程管理就是在合适的时机选择合...

  • 4

    深入理解Linux用户空间的锁机制 浏览:3002次  出处信息     1.         缘起

  • 3

    大家好,我是飞哥!开篇我先考大家一个小问题,如果你的服务器上已经有个进程在 listen 6000 这个端口号了。那么该服务器上其它进程是否还能 bind 和 listen 该端口呢?我相信一定会有一部分同学会答说是不能的。因为很多人都遇到过“Address alrea...

  • 3
    • mengtnt.com 2 years ago
    • Cache

    深入理解LINUX内核

    深入理解LINUX内核 03月 16日, 2022 1 minute read 最近在读《深入理解LINUX内核》这本书让我想起来大学时代的《深入理解计算机系统》、《编译原理》、《操作系统》这几本红宝书。这种类型的书,由于设计的技术细节过...

  • 7

    有时会出现这样的情况,磁盘空间显示已经被占满,但是在查看磁盘的具体文件占用情况时,发现磁盘仍...

  • 0
    • ictar.github.io 2 years ago
    • Cache

    深入理解 Go | 函数调用

    深入理解 Go | 函数调用 发表于 2020-03-24...

  • 1

    from: 互联网,了解下备个份而已啦~!unix 请看个 man 1、Page Cache 1.1 Page Cache 是什么? 为了理解 Page Cache,我们不妨先看一下 Linux 的文件 I/O 系统,...

  • 5

    硬盘格式化我们知道,一个硬盘必须要经过格式化之后才能使用。那么,格式化到底做了什么呢? 本质上,硬盘格式化可以分为两个步骤,分别是: 低级格式化,或称 物理格式化。

  • 9
    • www.51cto.com 1 year ago
    • Cache

    深入理解 Linux 上的虚拟内存

    深入理解 Linux 上的虚拟内存 作者:Linux迷 2022-08-21 16:52:27 Linux 发行版要求您在安装期间设置虚拟内存空间(交换分区),但大多数初学者并不知道这有多大用处。以下是您需要了解的有关 Linux 上的虚拟内存...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK