linux系统调用ppt
系统调用系统调用
1.2
本章内容本章内容
系统调用基础知识
数据结构和代码
系统调用getuid()的实现
添加一个系统调用mysyscall
1.3
一个简单的例子一个简单的例子
#include /* all system calls need this header */
int main(){
int i = getuid();
printf(“Hello World! This is my uid: %d\n”, i);
}
第1行:包括unistd.h这个头文件。所...
系统调用系统调用
1.2
本章内容本章内容
系统调用基础知识
数据结构和代码
系统调用getuid()的实现
添加一个系统调用mysyscall
1.3
一个简单的例子一个简单的例子
#include
/* all system calls need this header */
int main(){
int i = getuid();
printf(“Hello World! This is my uid: %d\n”, i);
}
第1行:包括unistd.h这个头文件。所有用到系统调用的
程序都需要包括它。
第2行:进行getuid()系统调用,并将返回值赋给变量i
1.4
为什么需要系统调用为什么需要系统调用
系统调用是内核向用户进程提供服务的唯一
,应用程序调用操作系统提供的功能模块(数
)。
用户程序通过系统调用从用户态(user mode)
切换到核心态(kernel mode),从而可以访问
相应的资源。这样做的好处是:
为用户空间提供了一种硬件的抽象接口,使编程更加
容易。
有利于系统安全。
有利于每个进程度运行在虚拟系统中,接口统一有利
于移植。
1.5
为什么需要系统调用为什么需要系统调用((续续))
1.6
为什么需要系统调用为什么需要系统调用((续续))
1.7
运行模式、地址空间、上下文运行模式、地址空间、上下文
运行模式(mode)
Linux使用了其中的两个:特权级0和特权级3 ,即内
核模式(kernel mode)和用户模式(user mode)
地址空间(space)
每个进程的虚拟地址空间可以划分为两个部分:用户
空间和内核空间。
在用户态下只能访问用户空间;而在核心态下,既可
以访问用户空间,又可以访问内核空间。
内核空间在每个进程的虚拟地址空间中都是固定的(
虚拟地址为3G~4G的地址空间)。
1.8
运行模式、地址空间、上下文运行模式、地址空间、上下文
上下文(context)。一个进程的上下文可以分
为三个部分:用户级上下文、寄存器上下文以及
系统级上下文。
用户级上下文:正文、数据、用户栈以及共享存储区
;
寄存器上下文:通用寄存器、程序寄存器(IP)、处
理机状态寄存器(EFLAGS)、栈指针(ESP);
系统级上下文:进程控制块task_struct、内存管理信
息(mm_struct、vm_area_struct、 pgd、pmd、pte
等)、核心栈等。
1.9
系统调用、系统调用、APIAPI和和CC库库
Linux的应用编程接口(API)遵循 POSIX
Linux的系统调用作为c库的一部分提供。c库中实现了
Linux的主要API,包括标准c库函数和系统调用。
应用编程接口(API)其实是一组函数定义,这些函数说明了如何获得
一个给定的服务;而系统调用是通过软中断向内核发出一个明确的请
求,每个系统调用对应一个封装例程(wrapper routine,唯一目的
就是发布系统调用)。一些API应用了封装例程。
API还包含各种编程接口,如:C库函数、OpenGL编程接口等
系统调用的实现是在内核完成的,而用户态的函数是在函
数库中实现的
调用printf() c库中的printf() c库中的write() sys_write()系统调用
应用程序
C库 内核
1.10
系统调用与操作系统命令系统调用与操作系统命令
操作系统命令相对应用编程接口更高一层,每个
操作系统命令都是一个可执行程序,比如ls、
hostname等,
操作系统命令的实现调用了系统调用
通过strace命令可以查看操作系统命令所调用的
系统调用,如:
strace ls
strace hostname
1.11
系统调用与内核函数系统调用与内核函数
内核函数在形式上与普通函数一样,但它是在内
核实现的,需要满足一些内核编程的要求
系统调用是用户进程进入内核的接口层,它本身
并非内核函数,但它是由内核函数实现的
进入内核后,不同的系统调用会找到各自对应的
内核函数,这些内核函数被称为系统调用的“服
务例程”
1.12
系统调用处理程序及服务例程系统调用处理程序及服务例程
当用户态的进程调用一个系统调用时,CPU切换
到内核态并开始执行一个内核函数
系统调用处理程序执行下列操作:
在内核栈保存大多数寄存器的内容
调用名为系统调用服务例程(system call service
routine)的相应的C函数来处理系统调用
通过ret_from_sys_call( )函数从系统调用返回
1.13
调用一个系统调用调用一个系统调用
…
xyz()
…
system_call:
…
sys_xyz()
…
ret_from_sys_call
:
…
iret
xyz(){
…
int 0x80
…
}
sys_xyz(
){
…
}
在应用程序
在libc标准库
系统调用
系统调用
调用系统调用
中的封装例程
处理程序
服务例程
(API)
用户态
内核态
新的系统调用sysenter指令,从用户
态到内核态快速切换方法
1.14
初始化系统调用初始化系统调用
内核初始化期间调用 trap_init() 函 数 建 立 IDT 中
128(0x80)号向量对应的表项:
set_system_gate(0x80, &system_call);
该调用把下列值装入该门描述符的相应域 :
segment selector:内核代码段__KERNEL_CS的段选择符
offest:指向system_call( )异常处理程序
type:置为15,表示该异常是一个陷入
DPL(描述符特权级):置为3,这就允许用户态进程调用这个异
常处理程序
为
int
0x80 做好了准备
1.15
数据结构和代码数据结构和代码
与系统调用相关的内核代码文件:
arch/i386/kernel/entry.S
系统调用时的内核栈
sys_call_table
system_call和ret_from_sys_call
arch/i386/kernel/traps.c //错误处理程序文件
include/linux/unistd.h
系统调用编号
宏定义展开系统调用
1.16
系统调用时的内核栈系统调用时的内核栈
用户空间ss
用户空间esp
EFLAGS
用户空间cs
用户空间eip
系统调用号
可用空间
可用空间
eip
函数返回地址
局部变量
用户栈内核栈
中断后及iret
返回前的esp
内核ss
向外返回
向内中断
中断前及iret
返回后的esp
用户ss
task_struct
2.6 kernel:
thread_info
1.17
系统调用时的内核栈系统调用时的内核栈((续续))
18 * Stack layout in 'ret_from_system_call':
19 * ptrace needs to have all regs on the stack.
20 * if the order here is changed, it needs to be
21 * updated in fork.c:copy_process, signal.c:do_signal,
22 * ptrace.c and ptrace.h
23 *
24 * 0(%esp) - %ebx
25 * 4(%esp) - %ecx
26 * 8(%esp) - %edx
27 * C(%esp) - %esi
28 * 10(%esp) - %edi
29 * 14(%esp) - %ebp
30 * 18(%esp) - %eax
31 * 1C(%esp) - %ds
32 * 20(%esp) - %es
33 * 24(%esp) - orig_eax
34 * 28(%esp) - %eip
35 * 2C(%esp) - %cs
36 * 30(%esp) - %eflags
37 * 34(%esp) - %oldesp
38 * 38(%esp) - %oldss
39 *
40 * "current" is in register %ebx during any slow entries.
arch/i386/kernel/entry.S
1.18
system_callsystem_call()()函数函数
system_call()函数实现了系统调用中断处理程序 :
1.
它首先把系统调用号和该异常处理程序用到的所有CPU寄存
器保存到相应的栈中,
SAVE_ALL
2.
把当前进程task_struct (thread_info)结构的地址存放
在ebx中
3.
对用户态进程传递来的系统调用号进行有效性检查。若调
用号大于或等于NR_syscalls,系统调用处理程序终止。
(sys_call_table)
4.
若系统调用号无效,函数就把-ENOSYS值存放在栈中eax寄
存器所在的单元,再跳到ret_from_sys_call()
5.
根据eax中所包含的系统调用号调用对应的特定服务例程
1.19
system_callsystem_call ((续续))
194 ENTRY(system_call)
195 pushl %eax # save orig_eax
196 SAVE_ALL
197 GET_CURRENT(%ebx)/*获取当前进程的task_strut指针
198 testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
199 jne tracesys
200 cmpl $(NR_syscalls),%eax
201 jae badsys
202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
203 movl %eax,EAX(%esp) # save the return value
2.6: GET_THREAD_INFO(%ebp)
1.20
system_callsystem_call ((续续))
ENTRY(ret_from_sys_call)
cli # need_resched and signals atomic test,关中断
cmpl $0,need_resched(%ebx)
jne reschedule
cmpl $0,sigpending(%ebx)
jne signal_return
restore_all:
RESTORE_ALL
1.21
SAVE_ALLSAVE_ALL定义定义
#define SAVE_ALL
cld; \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__KERNEL_DS),%edx; \
movl %edx,%ds; \
movl %edx,%es; \
使用内核
数据段
1.22
RESTORE_ALLRESTORE_ALL定义定义
#define RESTORE_ALL \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
1: popl %ds; \
2: popl %es; \
addl $4,%esp;\
3: iret; \
1.23
sys_call_tablesys_call_table
arch/i386/kernel/syscall_table.S
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used
for restarting */
.long sys_exit /* 1 */
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
…
.long sys_ioprio_get /* 290 */
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
1.24
sys_call_tablesys_call_table ((续续))
1.25
1.26
系统调用中参数传递系统调用中参数传递
每个系统调用至少有一个参数,即通过eax寄存
器传递来的系统调用号
用寄存器传递参数必须满足两个条件:
每个参数的长度不能超过寄存器的长度
参数的个数不能超过6个(包括eax中传递的系统调用
号),否则,用一个单独的寄存器指向进程地址空间
中这些参数值所在的一个内存区即可
在少数情况下,系统调用不使用任何参数
服务例程的返回值必须写到eax寄存器中
1.27
系统调用编号系统调用编号
Include/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_ioprio_get 290
#define __NR_inotify_init 291
#define __NR_inotify_add_watch 292
#define __NR_inotify_rm_watch 293
#define NR_syscalls 294
1.28
宏定义展开系统调用宏定义展开系统调用
宏定义syscallN()用于系统调用的格式转换和参数的传递。
Include/asm-i386/unistd.h
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), "d"
((long)(arg3))); \
__syscall_return(type,__res); \
}
第一个”:”是output
第二个”:”是input
1.29
系统调用中普通参数的传递系统调用中普通参数的传递
3个参数系统调用生成汇编代码
movl $__NR_##name, %eax //系统调用号给eax寄存器
movl arg1, %ebx
movl arg2, %ecx
movl arg3, %edx
#APP
int $0x80
#NO_APP
movl %eax, __res //最后处理输出参数
syscallN()第一个参数说明响应函数返回值的类型,第二个参数为系
统调用的名称(即name),其余的参数依次为系统调用参数的类型
和名称。例如:
_syscall3(int, open, const char * pathname, int flag, int mode)
说明了系统调用命令
int open(const char *pathname, int flags, mode_t mode)
1.30
系统调用参数与寄存器系统调用参数与寄存器
参数 参数在堆栈的位置 传递参数的寄存器
arg1 00(%esp) ebx
arg2 04(%esp) ecx
arg3 08(%esp) edx
arg4 0c(%esp) esi
arg5 10(%esp) edi
1.31
系统调用小结系统调用小结
程序执行系统调用大致可归结为以下几个步骤:
1、程序调用libc库的封装函数。
2、调用软中断 int 0x80 进入内核。
3、在内核中首先执行system_call函数,接着根据系
统调用号在系统调用表中查找到对应的系统调用服务
例程 。
4、执行该服务例程。
5、执行完毕后,转入ret_from_sys_call例程,从系
统调用返回
1.32
例:系统调用例:系统调用getuidgetuid()()的实现的实现
一个简单的程序,但包含系统调用和库函数调用
#include
/* all system calls need this header */
int main(){
int i = getuid();
printf(“Hello World! This is my uid: %d\n”, i);
}
假定定义了“宏”
_syscall0( int, getuid);
1.33
例:系统调用例:系统调用getuidgetuid()()的实现的实现((续续))
这个“宏”被getuid()展开后
int getuid(void)
{
long __res;
__asm__ volatile ("int $0x80"
: "=a" (__res)
: "" (__NR_getuid));
__syscall_return(int,__res);
}
显然,__NR_getuid(24)放入eax,并int 0x80
1.34
例:系统调用例:系统调用getuidgetuid()()的实现的实现((续续))
因为系统初始化时设定了
set_system_gate(SYSCALL_VECTOR,&system_call);
所以进入system_call
194 ENTRY(system_call)
195 pushl %eax # save orig_eax
196 SAVE_ALL
197 GET_CURRENT(%ebx)
198 testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
199 jne tracesys
200 cmpl $(NR_syscalls),%eax
201 jae badsys
202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)
203 movl %eax,EAX(%esp)
1.35
例:系统调用例:系统调用getuidgetuid()()的实现的实现((续续))
注意第202行,此时eax为24
查sys_call_table,得到call指令的操作数
sys_getuid16
调用函数sys_getuid16()
145 asmlinkage long sys_getuid16(void)
146 {
147 return high2lowuid(current->uid);
148 }
1.36
例:系统调用例:系统调用getuidgetuid()()的实现的实现((续续))
第202行完成后,接着执行第203行后面的指令
203 movl %eax,EAX(%esp)
204 ENTRY(ret_from_sys_call)
205 cli
206 cmpl $0,need_resched(%ebx)
207 jne reschedule
208 cmpl $0,sigpending(%ebx)
209 jne signal_return
210 restore_all:
211 RESTORE_ALL
1.37
例:系统调用例:系统调用getuidgetuid()()的实现的实现((续续))
第203行:返回值从eax移到堆栈中eax的位置
假设没有什么意外发生,于是
ret_from_sys_call直接到RESTORE_ALL,从
堆栈里面弹出保存的寄存器,堆栈切换,iret
进程回到用户态,返回值保存在eax中
printf打印出
Hello World! This is my uid:551
1.38
实验:添加一个系统调用实验:添加一个系统调用mysyscallmysyscall
实验内容:
在现有的系统中添加一个不用传递参数的系统调用。
这个系统调用的功能是实现遍历进程。
1.39
添加一个系统调用添加一个系统调用mysyscallmysyscall ((续续))
确定系统调用名字和编号:__NR_mysyscall,223
改写/usr/include/unistd.h和
/usr/include/asm/unistd.h
改写内核源代码树的include/asm/unistd.h
#define __NR_fcntl64l 221
/* 223 is unused*/
#define __NR_mysyscall 223
#define __NR_gettid 224
内核版本不一样,这个是
2.6.15 kernel
1.40
添加一个系统调用添加一个系统调用mysyscallmysyscall ((续续))
内核中实现该系统调用的程序的名字
sys_mysyscall
改写arch/x86/kernel/syscall_table_32.S
……
233 .long sys_mysyscall
234 .long sys_gettid
235 .long sys_readahead /* 225 */
……
.long sys_mysyscall /*2.6.15
1.41
添加一个系统调用添加一个系统调用mysyscallmysyscall ((续续))
把一小段程序添加在kernel/sys.c
asmlinkage int sys_mysyscall(void)
{
//在此处加入遍历进程的代码;
return 0;
}
记得重新编译内核,启动新内核
1.42
添加一个系统调用添加一个系统调用mysyscallmysyscall ((续续))
编写一段测试程序检验实验结果test.c
#include
_syscall0(int,mysyscall) /* 注意这里没有分号 */
int main()
{
mysyscall(); /* */
//在此加入输出遍历的进程信息的代码;
}
用gcc编译源程序
# gcc –o test test.c
运行程序
# ./test
内核版本不一样,这里是
2.6.15 kernel
1.43
test.ctest.c
在近期的Linux内核版本中,宏_syscall0()至_syscall6()
不再定义在/usr/include/asm/unistd.h中,因此需要使用
syscall()函数实现系统调用。
用户态测试程序可以用如下方法实现
#include
# include
#define __NR_ mysyscall 223
int main()
{
syscall(__NR_mysyscall) /*或syscall(223) */
//在此加入输出遍历的进程信息的代码;
}
系统调用号根据实验
具体数字而定
1.44
实验实验
实验3 添加系统调用
系统调用
本章内容
一个简单的例子
为什么需要系统调用
为什么需要系统调用(续)
为什么需要系统调用(续)
运行模式、地址空间、上下文
运行模式、地址空间、上下文
系统调用、API和C库
系统调用与操作系统命令
系统调用与内核函数
系统调用处理程序及服务例程
调用一个系统调用
初始化系统调用
数据结构和代码
系统调用时的内核栈
系统调用时的内核栈(续)
system_call()函数
system_call (续)
system_call (续)
SAVE_ALL定义
RESTORE_ALL定义
sys_call_table
sys_call_table (续)
幻灯片编号 25
系统调用中参数传递
系统调用编号
宏定义展开系统调用
系统调用中普通参数的传递
系统调用参数与寄存器
系统调用小结
例:系统调用getuid()的实现
例:系统调用getuid()的实现(续)
例:系统调用getuid()的实现(续)
例:系统调用getuid()的实现(续)
例:系统调用getuid()的实现(续)
例:系统调用getuid()的实现(续)
实验:添加一个系统调用mysyscall
添加一个系统调用mysyscall (续)
添加一个系统调用mysyscall (续)
添加一个系统调用mysyscall (续)
添加一个系统调用mysyscall (续)
test.c
实验
本文档为【linux系统调用ppt】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。