美文网首页
一个系统调用的例子

一个系统调用的例子

作者: 刘思宁 | 来源:发表于2018-07-21 20:27 被阅读254次
    from @unsplash

    我们经常利用各种 shell 命令(比如,cat,head),以及各种编程语言中IO方法(比如 go 中的 ioutil.ReadFile) 中的来读取文件,这些被封装好的接口调用起来很方便,但是他们的到底是怎么读取文件的呢?

    我们来仔细看看这个四个字:读取文件

    先说什么是文件。要知道,文件是操作系统封装出来的一个概念。你可能会觉得文件是由硬盘管理的,但硬盘只知道自己身上存了一些磁性材料,不知道什么是文件。而且在 UNIX 系统中,很多文件根本就跟硬盘没有关系[1](我们有机会再谈)。不论是哪一种,操作系统中的“文件”最终都来自操作系统对于硬件的封装。

    既然这个概念是由操作系统控制的,那么操作系统应该对”文件“有充分的解释权。于是,要想做到读取,很自然就会想到要和操作系统进行交互。

    如果不考虑底层发生的事情,我们只看浅层的因果逻辑:输入命令,得到结果。

    $ cat /path/to/file
    you get contents in the file.
    

    但刚刚我们已经说了,读取的过程中,一定会发生和操作系统的交互,这个交互到底是什么呢?

    这里面会发生很多事情,但是我们一点一点的去探索。我们先来看稍微深层一点的逻辑(这个逻辑的细节适用于 POSIX 标准下的操作系统)。

    1. cat 会调用一个包含<unistd.h>库的 c 程序;这个库定义了 read 方法ssize_t read(int, void \*, size_t);
    2. c 程序调用 read 方法[2],read方法的底层执行一些操作系统中的方法,把要执行的 system call 用一个数字表示(比如 sys_read 对应的数字是 3),并存储在某个位置,比如一个寄存器中。
    3. 接下来运行 trap 指令,把进程的控制权交给 kernel。
    4. kernel 根据存下来的 system call 数字,找到对应的执行代码(handler),这个过程叫做 dispatch。
    5. handler 处理完成后(也就是,读取了特定字节后),进程控制权交回到user,把 read 方法中封装的其他操作执行完,然后开始执行 read 之后的程序,比如关闭文件

    这就是从 cat 指令开始,完成 read 这个 system call 的大致过程。

    an illustration on system call

    但这里面有大量的细节其实还是没有讲清楚,比如

    1. cat 方法是怎么调用的 c 程序?
    2. system call 在不同的机器上实现方式不同,但是具体到某个机器上,是怎么实现的呢?
    3. trap 指令是什么指令?
    4. 以及这里的每一个技术名词,都需要仔细考察。

    因此,我们把上面的过程再走一遍,但这次更近距离地观察,让更多的细节暴露出来。

    1. cat 程序开始执行,走着走着,来到了一个 c 程序中,这个 c 程序引入了一个叫做 unistd.h 的头文件
    2. 这个头文件是 POSIX 标准的一部分,里面定义了很多方法的调用方式,其中就有我们要用到的 read 方法——ssize_t read(int, void \*, size_t);
    3. c 程序执行到了 read 方法那里。read 方法需要的 3 个参数先是被压入调用栈中,然后 read 方法开始执行
    4. 执行中的 read 方法调用了另外一个程序指令,把 sys_read 这个系统调用的编码查了出来(3),并把这个编码放在了一个寄存器中
    5. read 方法的定义中一定包含了一个 TRAP 指令。TRAP 指令的执行使得进程从 user mode 变为 kernel mode
    6. 进入到 kernel mode 的程序,要从哪里开始执行呢?TRAP 指令会给出一个特定的地址,操作系统就从那里读取下一条指令
    7. 操作系统读取到这条被指定的指令,从而开始以下工作:从寄存器中查出系统调用的编码,并根据这个编码找到下一步要去调用哪些代码(这一步叫做 dispatch,发送的意思)
    8. 这些被调用的代码叫做 handler,他实际完成读去文件的操作
    9. 完成读取之后,进程返回到 user mode,继续执行 TRAP 之后的指令,知道完成 read 方法
    10. read 方法已经完成,指定被压入调用栈的 3 个参数现在用不到了,他们会被移除
    11. 然后,程序可以执行 c 程序中的下一段代码了

    这个观察更仔细的版本,部分解决了我们之前一个问题:TRAP 指令是什么。总结一下就是,TRAP 指令是一个让进程进入 kernel mode,并且只能指定特定的内存地址让计算机去找下一条指令。其他方面,他和普通的计算机指令是差不多的。

    当然,我们还可以有更深入的版本,这条追查的路,可以一路深入到物理和数学的层面(电子和计算)。但今天,对于我们目的:认识一个系统调用的例子,已经起到了一定的作用。

    下次,我们会研究 read 之外其他的一些系统调用。


    1. modern operating system, page.44

    2. 这里有一个例子,https://stackoverflow.com/q/13322299/6633748

    相关文章

      网友评论

          本文标题:一个系统调用的例子

          本文链接:https://www.haomeiwen.com/subject/lakkmftx.html