0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux中的系统调用是怎样实现

jf_78858299 来源:码农的荒岛求生 作者:码农的荒岛求生 2023-02-15 11:40 次阅读

那么在今天的文章中我们从理论来到现实,看看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

然后执行:

./testHello, 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函数所需要的参数是什么。

好啦,这篇就到这里,最后,我准备开通知识星球啦,我会把所有文章中留下的问题总结在这里,同时也鼓励大家在这里输出自己的深度、系统性的思考,沉淀出属于自己的知识。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11196

    浏览量

    208660
  • 函数
    +关注

    关注

    3

    文章

    4274

    浏览量

    62303
  • 系统调用
    +关注

    关注

    0

    文章

    28

    浏览量

    8317
收藏 人收藏

    评论

    相关推荐

    Linux系统调用实现与应用

    在计算机科学系统调用(System Call)是一种操作系统提供的服务,它允许应用程序通过软件中断的方式访问操作系统内核
    发表于 06-14 11:46 487次阅读

    Linux内核系统调用详解

    Linux内核设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过
    发表于 08-23 10:37 748次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>中</b><b class='flag-5'>系统</b><b class='flag-5'>调用</b>详解

    ARM Linux系统调用是如何实现的呢

    和执行程序。Unix系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口。系统
    发表于 05-06 10:42

    ARM linux系统调用实现原理

    大家都知道linux的应用程序要想访问内核必须使用系统调用从而实现从usr模式转到svc模式。下面咱们看看它的实现过程。
    发表于 05-30 11:24 2231次阅读

    基于linux系统实现的vivado调用VCS仿真教程

    VCS-MX的版本,可以混合编译Verilog和VHDL语言 由于在linux系统个人用户各种权限被限制,导致很多地方无法正常使用软件之间的协调工作。 为了以防万一,在此以个人用户去实现
    的头像 发表于 07-05 03:30 1.1w次阅读
    基于<b class='flag-5'>linux</b><b class='flag-5'>系统</b><b class='flag-5'>实现</b>的vivado<b class='flag-5'>调用</b>VCS仿真教程

    透了解系统调用助你成为Linux下编程高手

    Linux内核设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过
    的头像 发表于 05-11 11:27 3404次阅读
    透了解<b class='flag-5'>系统</b><b class='flag-5'>调用</b>助你成为<b class='flag-5'>Linux</b>下编程高手

    你知道Linux系统调用的原理

    系统调用是应用程序与操作系统内核之间的接口,它决定了程序如何与内核打交道的。无论程序是直接进行系统调用,还是通过运行库,最终还是会到达
    发表于 05-16 16:21 1463次阅读
    你知道<b class='flag-5'>Linux</b><b class='flag-5'>系统</b><b class='flag-5'>调用</b>的原理

    Linux系统调用的技巧

    1.linux系统调用的基本原理  linux系统调用形式与POSIX兼容,也
    发表于 04-02 14:36 379次阅读

    Linux系统调用是什么

    所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口获得操作系统内核提供的服务。例如,用户可以通过
    发表于 06-11 09:33 2331次阅读

    系统调用是如何实现的?

    这张图画了挺久的,主要是想让大家可以从全局角度,看下linux内核系统调用实现。 在讲具体的细节之前,我们先根据上图,从整体上看一下
    的头像 发表于 02-20 16:46 3932次阅读
    <b class='flag-5'>系统</b><b class='flag-5'>调用是</b>如何<b class='flag-5'>实现</b>的?

    关于Linux内核系统调用是如何实现的与结果

    在执行 entry_SYSCALL_64 函数时,内核代码会根据约定,先从rax寄存器获取想要执行的系统调用的编号,然后根据该编号从sys_call_table数组中找到对应的系统
    的头像 发表于 03-19 10:52 1535次阅读

    如何区分xenomai、linux系统调用/服务

    对于同一个POSIX接口应用程序,可能既需要xenomai内核提供服务(xenomai 系统调用),又需要调用linux内核提供服务(linux
    的头像 发表于 05-10 10:28 1982次阅读

    Linux内核系统调用概述及实现原理

    本文介绍了系统调用的一些实现细节。首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样
    的头像 发表于 05-14 14:11 2159次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>系统</b><b class='flag-5'>调用</b>概述及<b class='flag-5'>实现</b>原理

    Linux系统调用的具体实现原理

    文我将基于 ARM 体系结构角度,从 Linux 应用层例子到内核系统调用函数的整个过程来梳理一遍,讲清楚linux系统
    的头像 发表于 09-05 17:16 1044次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>系统</b><b class='flag-5'>调用</b>的具体<b class='flag-5'>实现</b>原理

    Linux系统调用概述

    控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)。在linux
    的头像 发表于 11-09 10:27 496次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>系统</b><b class='flag-5'>调用</b>概述