LINUX内核设计思想之系统调用
来源:互联网 发布:js 数组 remove 编辑:程序博客网 时间:2024/05/21 17:08
通过系统调用,是用户空间访问内核的唯一手段,除异常和陷入之外,它们是内核唯一的合法入口.
5.1 系统调用的示意代码
例如获取当前进程的PID,用户空间只需要调用如下代码:
User Space:
getpid()
实质上,内核是这样响应此系统调用的.
Kernel Space:
Asmlinkage long sys_getpid(void)
{
Return current->tgid;
}
注:
首先,函数声明中的asmlinkage的意义是用于通知编译器仅从栈中提取该函数的参数.所有的系统调用都需要这个限定词;
其次,系统调用getpid()在内核被定义为sys_getpid().LINUX中所有系统调用都应该遵守这种前缀(sys_)命名.
5.1.1 系统调用号
在LINUX中,每个系统调用被赋予一个唯一的系统调用号,用于关联相应的系统调用.这个系统调用号是被用来指明到底是执行哪个系统调用,否则,内核早就混乱了.
内核记录了系统调用表中的所有已经注册过的系统调用列表.存储在sys_call_table中.它与系统结构有关,一般在entry.s中定义.
5.2 系统调用的实现原理
用户空间的程序无法直接执行内核的代码,它们是没办法去直接调用内核空间中的函数的.所以,应用程序应该必某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,让内核代表用户执行.通知内核的机制是靠软中断实现的.
5.2.1 指定恰当的系统调用
因为所有的系统调用陷入内核的方式都一样,很明显仅仅陷入内核空间是不够的,还需要指定相应的系统调用.前面已经说过,每个系统调用都有一个唯一的系统调用号.因此,把系统调用号也传递下去索引便可找到相应的系统调用.根据系统调用号索引到相应的系统调用函数这个工作由system_call()函数来完成.
函数system_call()首先会将给定的系统调用号与NR_syscalls作比较来检查其有效性;其次,执行相应的系统调用,示意代码如下:
Call *sys_call_table()
X86的system_call源码(linux2.6.10)如下:
ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
# system call tracing in operation
testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # store the return value
syscall_exit:
示意代码如下:
read() ----> [read()封装例程---->]system_call()[base on "sys_call_table"]---->sys_read()
5.3 如何添加自己的系统调用函数
由上述分析可知,LINUX从用户空间到内核代表用户空间程序执行的流程如下:
用户编写自己的程序-->系统调用-->找到相应的系统调用函数代表用户程序执行.如上述的read()示意代码.
一、用户编写自己的程序去引发系统调用有两种实现方式:C库和LINUX本身提供的宏(_syscalln);(如上述的read())
二、System_call()校验系统调用号的合法性;
三、实现代表用户空间程序执行的系统调用函数.(如上述的sys_read())
基于上述三个步骤,知道我们要添加我们自己的系统调用函数涉及到三部分:
.用户可以陷入系统调用的代码实现;
.添加系统调用号;
.实现代表用户空间执行的系统调用函数
5.3.1 用户可以陷入系统调用的代码实现
通常,系统调用靠C库支持.但是,如果是我们定制的系统调用,C库是不可能已经支持的.这时候,我们只能用LINUX本身提供的宏(_syscalln)来实现.示意代码如下:
#define __NR_foo 283
__syscall0(long,foo)
int main()
{
Long stack_size;
Stack_size = foo();
Printf("The kernel stack size is %ld\n",stack_size);
Return 0;
}
[注:]_syscalln中的"n"是有命名规则的,(2+2*n)表征此函数参数的个数.
5.3.2 添加系统调用号
修改系统调用表:
Arch/i386/kernel/entry.s
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
... ...
.long sys_mq_timedreceive /* 280 */
.long sys_mq_notify
.long sys_mq_getsetattr
.long sys_foo
Asm-i386/unistd.h
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
... ...
#define__NR_foo283
5.3.3 实现代表用户程序执行的系统调用函数体
例如我们可以将其实现放在kernel/sys.c文件中.如下:
Asmlinkage long sys_foo(void)
{
Return THREAD_SIZE;
}
- LINUX内核设计思想之系统调用
- 把握linux内核设计思想(一):系统调用
- LINUX内核设计思想之初识LINUX
- Linux内核设计基础(七)之系统调用
- 《Linux内核设计与实现》读书笔记之系统调用
- LINUX内核设计思想之进程管理
- LINUX内核设计思想之进程调度
- Linux内核之系统调用
- Linux内核之系统调用
- linux内核之系统调用
- linux内核之系统调用
- LINUX内核设计思想之内核同步介绍
- LINUX内核设计思想之内核同步方法
- 《Linux内核设计与实现》--系统调用
- linux内核设计与实现:系统调用
- Linux内核学习之系统调用
- linux内核分析之系统调用
- linux内核介绍之系统调用过程
- android事件监听的两种方式
- 倒序数
- 64bit CPU 知识 (IA32,IA64,EM64T,AMD64)
- ubuntu 永久修改Mac地址
- 十道海量数据处理面试题与十个方法大总结
- LINUX内核设计思想之系统调用
- Android中BindService方式使用的理解
- 集群工具chukwa和ganglia
- serv-u 乱码解决
- wait和waitpid函数
- DatabaseMetaData获取数据库信息和表信息(表的列名,类型,主键等)SQL java
- [Usaco2009 Jan]安全路经Travel dijkstra + 并查集
- SharePoint2013网站添加切换用户登录
- nm命令介绍