Linux操作系统课件相关附件:6739

Download Report

Transcript Linux操作系统课件相关附件:6739

三 系统调用
 系统调用基础知识
 相关数据结构,代码及流程
 系统调用流程
 添加一个系统调用
1 系统调用基础
1. 为什么要分运行模式、运行空间?从而
需要系统调用?
User mode, user space
Kernel mode, kernel space
1. 提高系统安全性
2. 提供一个用户与内核交互的接口(通过它
内核为用户程序提供系统服务)。
3. 系统效率、应用开发效率
用户空间
模式切换
sys_foo...
模式切换
内核空间
地址空间:
3. 虚存管理机制。进程虚拟地址空间。
4. 进程空间可以划分为两部分:用户空间及系统空间。由
于系统只有一个内核运行,故所有进程的系统空间都映
射到单一内核地址空间。
上下文:
3. 用户级:正文,数据,用户栈及共享存储区
4. 寄存器:通用寄存器,程序计数器(IP),处理机状态
寄存器(EFLAGS),栈指针(ESP)
5. 系统级:进程控制块(task_struct),内存管理信息
(mm_struct, vm_area_struct, pgd, pmd, pte等),核心
栈等。
中断和异常
• 中断:其产生与当前执行的进程无关。
• 异常:一般由当前执行的进程引起。
出错(fault):保存当前EIP,即异常返回时会重新执
行该异常指令。
异常
陷入(trap):保存下一条指令的EIP,即异常返回时
不重新执行该异常指令。
可编程异常(programmable exception)或软中断:由
程序员用int指令触发。Linux用 int 0x80。
A system call is implemented by a “software interrupt” that
transfers control to kernel code; in Linux/i386 this is ``int 0x80''.
A system call is implemented by a “software interrupt”
that transfers control to kernel code; in Linux/i386
this is ``int 0x80''.
The specific system call number is stored in the EAX
register, and its arguments are held in the other
processor registers.
After the switch to kernel mode, the processor must
save all of its registers and dispatch execution to the
proper kernel function, after checking whether EAX is
out of range.
2. 相关数据结构、代码及流程
* arch/i386/kernel/entry.s汇编文件
SAVE_ALL: 压栈宏
RESTORE_ALL: 弹栈宏
sys_call_table: 系统调用表,依次保存所有系统调用代码的
指针,以便系统调用处理程序(system_call)索引。
system_call: 根据系统调用号在sys_call_table中找到具体的
内核系统调用代码,执行后将返回值保存到堆栈中。
ret_from_sys_call: 所有的中断处理、系统调用均通过该过
程返回到一个不确定的地方。
Sys_call_table
398 ENTRY(sys_call_table)
399
.long SYMBOL_NAME(sys_ni_syscall)
400
.long SYMBOL_NAME(sys_exit)
401
.long SYMBOL_NAME(sys_fork)
402
.long SYMBOL_NAME(sys_read)
403
.long SYMBOL_NAME(sys_write)
404
.long SYMBOL_NAME(sys_open)
/* 5 */
…...
635
.long SYMBOL_NAME(sys_ni_syscall) /* reserved for lremovexattr */
636
.long SYMBOL_NAME(sys_ni_syscall) /* reserved for fremovexattr */
637
638
.rept NR_syscalls-(.-sys_call_table)/4
639
.long SYMBOL_NAME(sys_ni_syscall)
640
.endr
系统调用索引
eax
eax
sys_call_table首地址
sys_call_table首地址
* 4
+
ENTRY(sys_call_table)
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_...)
.longSYMBOL_NAME(sys_exit)
SYMBOL_NAME(sys_ni_...)
.long
.longSYMBOL_NAME(sys_fork)
SYMBOL_NAME(sys_exit)
.long
.long
SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.longSYMBOL_NAME(sys_write)
SYMBOL_NAME(sys_read)
.long
.longSYMBOL_NAME(sys_open)
SYMBOL_NAME(sys_write)
.long
.long
…… SYMBOL_NAME(sys_open)
……
……
……
.long
SYMBOL_NAME(sys_getuid)
.long SYMBOL_NAME(sys_getuid)
压入用户 SS
压入用户 esp
压入EFLAGS
压入
cs
压入
cip
int 0x80
系
统
调
用
时
寄
存
器
变
化
压入 eax
(system_call);
Pushl %eax
SAVE_ALL
…
…
RESTORE_ALL
用户空间
返回用户空间
内核空间
Pushl %es ;
Pushl %ds ;
Pushl %eax;
Pushl %ebp;
Pushl %dei;
Pushl %esi;
Pushl %edx;
Pushl %ecx;
Pushl %ebx;
内核堆栈
……
Popl %es;
Addl $4,%esp
iret
* arch/i386/kernel/trap.c
trap_init 函数: 初始化中断描述符表IDT,向中断描述符表
里填入中断门,陷入门和调用门。
* arch/i386/kernel/unistd.h
定义所有的系统调用号,及与系统调用密切相关的关键的
宏。
arch/i386/kernel/traps.c
916 void __init trap_init(void)
{
……
923
set_trap_gate(0,&divide_error);
924
set_trap_gate(1,&debug);
925
set_intr_gate(2,&nmi);
926
set_system_gate(3,&int3);
……
942
set_trap_gate(19,&simd_coprocessor_error);
944
set_system_gate(SYSCALL_VECTOR,&system_call);
arch/i386/kernel/unistd.h - 系统调用编号
8
9
10
11
12
13
#define __NR_exit
#define __NR_fork
#define __ NR_read
#define __ NR_write
#define __ NR_open
#define __ NR_close
……
240 #define __NR_llistxattr
241 #define __NR_flistxattr
242 #define __NR_removexattr
243 #define __NR_lremovexattr
244 #define __NR_fremovexattr
1
2
3
4
5
6
233
234
235
236
237
arch/i386/kernel/unistd.h - 宏定义
#define _syscall0(type,name) \
type name(void) \
{\
long __res; \
__asm__ volatile (“int $0x80” \ volatile不允许优化
: "=a" (__res) \
生成的 汇编代码
: “”z(__NR##name))\
__syscall_return(type,__res); \
}
movl $__NR_##name, %eax /*为输入参数分配寄存器*/
#APP
int
$0x80
/*触发系统调用*/
#NO_APP
movl
%eax, __res
/*处理输出参数*/
用户程序
Int main()
{
…
Getuid();
…
}
3 系统调用流程
标准C库
Int getuid(boid)
{
Long __res;
…
Int $0x80;
…
}
中断处理
ENTRY(system_call)
Push1 %eax
SAVE_ALL
GET_CURRENT(%ebx)
…
Call sys_getuid16
…
RESTORE_ALL
内核例程
Asmlinkage long
Sys_getuid16(void)
{
Return high2lowuid
(current->uid);
}
4. 例:添加一个系统调用
要求:
在现有系统中添加一个系统调用,不用传递
参数,使用户的uid等于0。
添加一个系统调用mysyscall
 确定系统调用的名字:mysyscall,则
- 系统调用的编号名字为 __NR_mysyscall
- 内核中系统调用实现的名字:sysy_mysyscall
 利用标准C库进行包装,以便通知编译器。
#include <linux/unistd.h>
_syscall0(int, mysyscall)
int main()
{
mysyscall();
}
/* 通过_syscall0来定义mysyscall
 添加系统调用号,改写/usr/include/asm/unistd.h
240 #define __NR_llistxattr
241 #define __NR_flistxattr
242 #define __NR_removexattr
243 #define __NR_lremovexattr
244 #define __NR_fremovexattr
245 #define __NR_mysyscall
233
234
235
236
237
238
 在系统调用表中添加相应表项改写arch/i386/kernel/entry.S
398 ENTRY(sys_call_table)
399
.long SYMBOL_NAME(sys_ni_syscall)
……
636
.long SYMBOL_NAME(sys_ni_syscall)
637
.long SYMBOL_NAME(sys_mysyscall)
638
639
.rept NR_syscalls-(.-sys_call_table)/4
640
.long SYMBOL_NAME(sys_ni_syscall)
641
.endr
 (简化)实现系统调用:把代码添加在kernel/sys.c
asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = current->suid = current->fsuid = 0;
return 0;
}
/*asmlinkage:Linux所有系统调用的实现都使用这
个标志。*/
 重新编译内核,启动新内核。
 编写一段测试程序检验实验结果
#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意这里没有分号 */
int main()
{
mysyscall();
printf(“em…, this is my uid: %d. \n”, getuid());
}