转自:http://os.51cto.com/art/200512/13510.htm

现在,您或许正在查看设备驱动程序,并感到奇怪:“函数 foo_read() 是如何被调用的?”或者可能疑惑: “当我输入 cat /proc/cpuinfo 时,cpuinfo() 函数是如何被调用的?”内核完成引导后,控制流就从相对直观的“接下来调用哪个函数?”改变为取决于系统调用、异常和中断。

什么是系统调用?

字面上讲,系统调用(也称为“syscall”)就是一条类似于“add”或者“jump”的指令。从更高的层面上讲,系统调用是用户级程序要求操作系统为它做某些事情的途径。如果您正在编写程序,需要读取某个文件,那么要使用一个系统调用来要求操作系统为您读取那个文件。

系统调用详述

这里是系统调用的工作原理:首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号(稍后对此进行详述)。注意,所有这些都是由库函数自动完成的,除非您是使用汇编编程。参数设置完成后,程序执行“系统调用”指令。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器跳转到一个新的地址,并开始执行那里的代码。新地址的指令会保存程序的状态,计算出应该调用哪个系统调用,调用内核中实现那个系统调用的函数,恢复用户程序状态,然后将控制权返还给用户程序。系统调用是设备驱动程序中定义的函数最终被调用的一种方式。这就是系统调用如何工作的一个简短说明。

接下来,我们将为那些对内核事实上如何完成感到好奇的这些人提供详尽的细节。不要担心您是否完全理解所有细节 —— 只需要记住这是内核中的函数最终被调用的一个途径 —— 没有任何神秘之处。您可以追踪控制流在内核中的全部历程 —— 有时会有些困难,但是您可以做得到。

系统调用示例:1

这里非常适合于开始根据理论展示一些代码。我们将研究 read() 系统调用的过程,首先从系统调用指令被执行的时候开始。使用 PowerPC 体系结构作为代码体系结构相关部分的示例。在 PowerPC 上,当执行一个系统调用时,处理器跳转到地址 0xc00。那个位置的代码是在文件 arch/ppc/kernel/head.S 中定义的。类似如下:


/* System call */
. = 0xc00
SystemCall:
EXCEPTION_PROLOG
EXC_XFER_EE_LITE(0xc00, DoSyscall) /* Single step - not used on 601 */
EXCEPTION(0xd00, SingleStep, SingleStepException, EXC_XFER_STD)
EXCEPTION(0xe00, Trap_0e, UnknownException, EXC_XFER_EE)

这段代码所做的事情是,保存一些状态,然后调用另一个名为 DoSyscall 的函数。

EXCEPTION_PROLOG 是一个宏,负责从用户空间到内核空间的切换,这需要保存用户进程的寄存器状态。使用此例程的地址和函数 DoSyscall 的地址来调用 EXC_XFER_EE_LITE。最后,某些状态将会被保存,DoSyscall 将会被调用。后面的两行在地址 0xd00 和 0xe00 保存两个异常向量。

EXC_XFER_EE_LITE 类似如下:


#define EXC_XFER_EE_LITE(n, hdlr) \
EXC_XFER_TEMPLATE(n, hdlr, n+1, COPY_EE, transfer_to_handler, \
ret_from_except)

EXC_XFER_TEMPLATE 是另一个宏,代码类似如下:


#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret) \
li r10,trap; \
stw r10,TRAP(r11); \
li r10,MSR_KERNEL; \
copyee(r10, r9); \
bl tfer; \
i##n: \
.long hdlr; \
.long ret

li 表示 立即加载(load immediate),即某个在编译时已知的常量保存在某个寄存器中。首先,trap 加载到寄存器 r10 中。在接下来的一行中,那个值存储在由 TRAP(r11) 给出的地址中。TRAP(r11) 以及接下来两行去做一些硬件相关的位操作。然后,我们调用 tfer 函数(transfer_to_handler 函数),它会处理更多内部事务并将控制转交给 hdlrDoSyscall)。注意, transfer_to_handler 通过链接寄存器加载处理程序的地址,因此您看到的是 .long DoSyscall,而不是 bl DoSyscall

系统调用示例:2

现在我们来研究 DoSyscall。它位于 arch/ppc/kernel/entry.S 文件中。这个函数最终使用系统调用编号将系统调用表的地址和索引加载到它。操作系统使用系统调用表将系统调用编号翻译为特定的系统调用。

系统调用表名为 sys_call_table,在 arch/ppc/kernel/misc.S 中定义。系统调用表包含有实现每个系统调用的函数的地址。例如,read() 系统调用函数名为 sys_read。 read() 系统调用编号是 3,所以 sys_read() 位于系统调用表的第四个条目中(因为系统调用起始编号为 0)。从地址 sys_call_table + (3 * word_size) 读取数据,得到 sys_read() 的地址。

DoSyscall 找到正确的系统调用地址后,它将控制权转交给那个系统调用。我们来看定义 sys_read() 的位置,即 fs/read_write.c 文件。这个函数会找到关联到 fd 编号(传递给 read() 函数的)的文件结构体。那个结构体包含指向用来读取特定类型文件数据的函数的指针。进行一些检查后,它调用与文件相关的 read() 函数,来真正从文件中读取数据并返回。与文件相关的函数是在其他地方定义的 —— 比如套接字代码、文件系统代码,或者设备驱动程序代码。这是特定内核子系统最终与内核其他部分协作的一个方面。

读取函数结束后,从 sys_read() 返回到 DoSyscall(),它将控制权切换给 ret_from_except(在 arch/ppc/kernel/entry.S 中定义)。它会去检查那些在切换回用户空间之前需要完成的任务。如果没有需要做的事情,那么就通过 restore 函数恢复用户进程的状态,并将控制权交还给用户程序。

就是这样!read() 调用就完成了!幸运的话,您会得到数据。

在关键的位置加入 printk,可以更深入地研究 syscalls。一定要限制这些 printk 的输出的数量。例如,如果向 sys_read() syscall 添加 printk,应该像这样去做:


static int mycount = 0; if (mycount < 10) {
printk ("sys_read called\n");
mycount++;
}

[Kernel]理解System call系统调用的更多相关文章

  1. 深入理解Solaris X64系统调用

    理解系统调用的关键在于洞悉系统调用号是联系用户模式与内核模式的纽带.而在Solaris x64平台上,系统调用号被保存在寄存器RAX中,从用户模式传递到内核模式.一旦进入内核模式,内核的sys_sys ...

  2. 深入理解Linux的系统调用【转】

    一. 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定的方式把系统调用给用户,一 ...

  3. [kernel]----理解kswapd的低水位min_free_kbytes

    1. min_free_kbytes 先看官方解释: This is used to force the Linux VM to keep a minimum number of kilobytes ...

  4. 灵魂拷问:你真的理解System.out.println()执行原理吗?

    原创/朱季谦 灵魂拷问,这位独秀同学,你会这道题吗?  请说说,"System.out.println()"原理...... 这应该是刚开始学习Java时用到最多一段代码,迄今为止 ...

  5. JVM相关 - 深入理解 System.gc()

    本文基于 Java 17-ea,但是相关设计在 Java 11 之后是大致一样的 我们经常在面试中询问 System.gc() 究竟会不会立刻触发 Full GC,网上也有很多人给出了答案,但是这些答 ...

  6. 【Linux】【Basis】【Kernel】Linux常见系统调用

    一,进程控制 1)getpid,getppid--获取进程识别号 #include <sys/types.h> #include <unistd.h> pid_t getpid ...

  7. 对于linux下system()函数的深度理解(整理)

    原谅: http://blog.sina.com.cn/s/blog_8043547601017qk0.html 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同 ...

  8. 转:对于linux下system()函数的深度理解(整理)

    这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数中调用的命令也都一切正常.就没理这个bug,以为 ...

  9. (笔记)Linux下system()函数的深度理解(整理)

    注:从其它地方转的非常好的一篇文章,值得深究! 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数 ...

随机推荐

  1. photoshop cs6 Mac版本

    地址:http://trials2.adobe.com/AdobeProducts/PHSP/13/osx10/Photoshop_13_LS3.dmg 说明:http://www.nowmac.co ...

  2. ASP.NET HTTP500错误怎么办

    如图所示,HTTP500错误. 第一步:"开始"->"设置"->"控制面板"->"管理工具"-> ...

  3. SQL Server 之 与 OVER() 函数

    在SQL SERVER 2005/2008支持两种排名开窗函数和聚集开窗函数. 一. OVER() 函数 语法结构:OVER( [ PARTITION BY ... ] [ ORDER BY ... ...

  4. Vue 源码 基础知识点

    1.数据类型判断 const _toString = Object.prototype.toString function toRawType(value) { return _toString.ca ...

  5. knockoutjs -- applyBinding & Observables

    applyBindings ko.applyBindings(myViewModel); // Knockout调用applyBindings激活myViewModel(即把myViewModel和V ...

  6. ubuntu——主题更新,Ubuntu-tweak安装

    1.首先打开终端 2.在终端中输入sudo apt-add-repository ppa:tualatrix/ppa 回车后输入密码等一会,导入密钥 3.再输入sudo apt-get update ...

  7. Android 混淆打包不混淆第三方jar包

    项目由于要公布,所以要混淆打包. 混淆打包流程: 1.在proguard-project.txt文件里加入不须要混淆的类和第三方的jar包   这个是保持自己包中不须要混淆的类,假设有些类调用了jni ...

  8. MYSQL 什么时候用单列索引?什么使用用联合索引?(收集)

    我一个表 students 表,有3个字段 ,id,name,age 我要查询 通过 name 和age,在这两个字段 是创建 联合索引?还是分别在name和age上创建 单列索引呢? 多个字段查询什 ...

  9. PHP-WebService中Endpoint、Disco、WSDL都是做什么的?

    Endpoint: http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx   web服务的URI地址,你访问之后,就会出现web服务的相 ...

  10. Android Design与Holo Theme详解

    在 国内,有个很有意思的现状.一方面,几个国内最大的公司/企业的客户端/应用依旧冥顽不灵,丝毫不愿意遵循 Android Design,以各种扯淡的理由坚持使用 iOS UI 或者 Metro UI, ...