文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

深入理解Linux系统调用

2024-12-13 15:00

关注

在前两篇文章《为什么计算机需要操作系统​》《​​系统调用与函数调用有什么区别​​》中我们了解了什么是系统调用、为什么需要系统调用、系统调用与函数调用有什么区别,那么在今天的文章中我们从理论来到现实,看看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函数所需要的参数是什么。​

来源:码农的荒岛求生内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯