[Kernel]理解System call系统调用
转自: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
中定义的。类似如下:
|
这段代码所做的事情是,保存一些状态,然后调用另一个名为 DoSyscall
的函数。
EXCEPTION_PROLOG
是一个宏,负责从用户空间到内核空间的切换,这需要保存用户进程的寄存器状态。使用此例程的地址和函数 DoSyscall
的地址来调用 EXC_XFER_EE_LITE
。最后,某些状态将会被保存,DoSyscall
将会被调用。后面的两行在地址 0xd00
和 0xe00
保存两个异常向量。
EXC_XFER_EE_LITE
类似如下:
|
EXC_XFER_TEMPLATE
是另一个宏,代码类似如下:
|
li
表示 立即加载(load immediate),即某个在编译时已知的常量保存在某个寄存器中。首先,trap
加载到寄存器 r10
中。在接下来的一行中,那个值存储在由 TRAP(r11)
给出的地址中。TRAP(r11)
以及接下来两行去做一些硬件相关的位操作。然后,我们调用 tfer
函数(transfer_to_handler
函数),它会处理更多内部事务并将控制转交给 hdlr
(DoSyscall
)。注意, 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
,应该像这样去做:
|
[Kernel]理解System call系统调用的更多相关文章
- 深入理解Solaris X64系统调用
理解系统调用的关键在于洞悉系统调用号是联系用户模式与内核模式的纽带.而在Solaris x64平台上,系统调用号被保存在寄存器RAX中,从用户模式传递到内核模式.一旦进入内核模式,内核的sys_sys ...
- 深入理解Linux的系统调用【转】
一. 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定的方式把系统调用给用户,一 ...
- [kernel]----理解kswapd的低水位min_free_kbytes
1. min_free_kbytes 先看官方解释: This is used to force the Linux VM to keep a minimum number of kilobytes ...
- 灵魂拷问:你真的理解System.out.println()执行原理吗?
原创/朱季谦 灵魂拷问,这位独秀同学,你会这道题吗? 请说说,"System.out.println()"原理...... 这应该是刚开始学习Java时用到最多一段代码,迄今为止 ...
- JVM相关 - 深入理解 System.gc()
本文基于 Java 17-ea,但是相关设计在 Java 11 之后是大致一样的 我们经常在面试中询问 System.gc() 究竟会不会立刻触发 Full GC,网上也有很多人给出了答案,但是这些答 ...
- 【Linux】【Basis】【Kernel】Linux常见系统调用
一,进程控制 1)getpid,getppid--获取进程识别号 #include <sys/types.h> #include <unistd.h> pid_t getpid ...
- 对于linux下system()函数的深度理解(整理)
原谅: http://blog.sina.com.cn/s/blog_8043547601017qk0.html 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同 ...
- 转:对于linux下system()函数的深度理解(整理)
这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数中调用的命令也都一切正常.就没理这个bug,以为 ...
- (笔记)Linux下system()函数的深度理解(整理)
注:从其它地方转的非常好的一篇文章,值得深究! 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数 ...
随机推荐
- 浅析GitLab Flow的十一个规则
使用 Git 版本控制,是对使用它之前的所有版本控制方式的一种改进.然而,很多组织最终以太过混乱或过于复杂的流程来结束.这个问题对于刚从其他版本控制系统转过来的组织来说特别突出. 在本文中我们会列出 ...
- ssh 的安装
新安装的ubuntu 虚拟机,没有ssh时(ssh 连接不上),时ssh服务没装. 安装openssh-server,就可以. ------------------------------------ ...
- function/bind
1.函数指针指向一类函数,这类函数的类型一样,也就是函数的返回类型和形参表一样. 2.不同的函数类型要使用不同的函数指针,才能指向它,有没有好的办法呢? 类比思考下,交换方法,对不同的类型要写不同的s ...
- PHPNOW如何添加虚拟主机
1 打开PHPNow控制面板,输入0,点回车 2 新增主机名称(你可以输入127.0.0.2到127.0.0.255),点击回车之后要求输入主机别名,不要写,直接回车,再要求输入网站目录,也不选,再回 ...
- angularjs中ng-repeat的使用
第一个例子:使用ng-repeat最简单的例子 <html ng-app="myApp"> <head> <title>angularjs-de ...
- JQuery 之 跳出循环
1.跳出each循环 return false 跳出循环 return true 进入下一个循环 2.跳出for循环 break;直接退出for这个循环.这个循环将不再被执行! continue;直接 ...
- nginx-rtmp流媒体服务器搭建【转】
nginx-rtmp https://github.com/pengqiuyuan/nginx-rtmp nginx-rtmp 流媒体服务器的搭建(采集桌面,手机直播) 在线Demo,直播自己的pc机 ...
- 【转】如何成为Python高手
http://www.aqee.net/how-to-become-a-proficient-python-programmer/ 这篇文章主要是对我收集的一些文章的摘要.因为已经有很多比我有才华的人 ...
- Laravel 隐藏域如何实现?
Laravel 隐藏域如何实现? 在 Blade 模板中,我们可以使用 method_field 方法来创建隐藏域. {{ method_field('DELETE') }} 其转化为 HTM ...
- bzoj-1492 货币兑换Cash (2)——CDQ分治
题意: 略 见上一篇 题解: 方程还是那个方程f[i]=A[i] * X[j] + B[i] * Y[j]. 化简为Y[i]=(-A[i]/B[i]) * X[i] + f[i]/B[i]这一坨: 既 ...