五、系统调用(1)
来源:互联网 发布:php如何解决ajax跨域 编辑:程序博客网 时间:2024/06/05 00:58
运行模式
Inter系列处理器有实模式和保护模式。刚启动处于实模式只能使用实地址访问内存。保护模式下可以使用段页机制,虚地址寻址等,保护模式下还提供4个特权级,linux只使用特权级0(内核模式)和特权级3(用户模式)。
地址空间
Linux虚存管理机制下,进程使用虚拟地址,进程都有自己的虚拟空间,通过地址转换机制转换成对物理地址的引用。每个进程的虚拟地址空间可以划分为两个部分:用户空间和系统空间。用户模式下只能访问用户空间,核心模式下可以访问这两个空间。系统空间中每个进程的虚拟地址都是3G-4G,所有进程的系统空间都会映射到单一内核地址空间。使得内核可以访问任何进程的地址空间。
进程执行系统调用时,通过特殊指令使系统陷入内核,并将控制权交给内核,由内核代替进程完成操作。完成后内核执行一组特征指令返回到用户态。
上下文
- 用户级上下文:正文、数据、用户栈、共享存储区等
- 寄存器上下文:通用寄存器、程序寄存器、状态寄存器、栈等
- 系统级上下文:进程控制块、内存管理信息、核心栈等
发生进程调度时,操作系统必须对上面全部上下文进行切换。系统调用进行的是模式切换(只进行寄存器上下文切换)比进程切换容易得多。
地址装换
Linux更偏向使用页机制,但是分段是Intel硬件机制所规定的所有使用两级地址转换。硬件除了使用自己的寄存器外还使用驻留在内存的描述符表、页表等数据结构来进行地址转换。
逻辑地址转线性地址
段选择符:索引部分+TI部分+RPL部分(当前特权级)。RPL决定当前进程访问相应段的权限;TI部分选择局部描述符表(LDT)还是全局描述符表(GDT)。描述表中每个描述符描述一个段的段基址,加偏移地址就唯一确定一个线性地址。
线性地址转物理地址
Linux分页机制采用两级分页,段机制描述可大可小的存储块,分页机制管理的是固定大小的页(一般为4K)。整个物理空间和线性空间都看成是由一个一个页面组成。通过分页机制可以把线性空间的一个页面映射到物理空间的一个页。arch/i386/kernel/entry.S
进程执行int指令陷入到内核,系统对堆栈进行切换,首先获取核心堆栈信息,再把用户堆栈信息保存到核心栈,这几步由硬件完成。
//寄存器入栈和出栈#define SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx; \//__KERNEL_DS在include/asm-i386/segment.h中定义为//#define __KERNEL_DS 0x18,即使用内核堆栈 movl $(__KERNEL_DS),%edx; \ movl %edx,%ds; \ movl %edx,%es;#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; \//iret返回指令,如果同级别,则弹出eip,cs,EFLAGS;//如果是不同级别,则还需弹出堆栈指针esp,ss3: iret; \
系统调用表依次保存着所有系统调用的函数指针
.dataENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork) .long SYMBOL_NAME(sys_read) .long SYMBOL_NAME(sys_write) .long SYMBOL_NAME(sys_open) /* 5 */ .............. .long SYMBOL_NAME(sys_ni_syscall) /* reserved for fremovexattr *///'.'表示当前地址,sys_call_table为数组首地址//(.-sys_call_table)/4表示已用系统调用数//NR_syscalls-(.-sys_call_table)/4为没有定义的系统调用数 .rept NR_syscalls-(.-sys_call_table)/4//往剩余空间填充sys_ni_syscall .long SYMBOL_NAME(sys_ni_syscall) .endr
//linux核心栈的位置在task_struct之后的两个页面(8192处),//通过-8192与上栈指针得到task_struct结构指针#define GET_CURRENT(reg) movl $-8192, reg andl %esp, reg - 陷入进内核机器会自动保存和转化堆栈ENTRY(system_call) pushl %eax #保存系统调用号 SAVE_ALL GET_CURRENT(%ebx)//得到当前进程结构控制块的指针 testb $0x02,tsk_ptrace(%ebx) # 若是受监视进程则跳转 jne tracesys cmpl $(NR_syscalls),%eax//判断是否是可用系统调用 jae badsys //根据系统调用号查询系统调用表,调用对应函数 call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value//从系统调用返回,判断是否需要重新调度//是否有进程向其发送信号,执行处理函数//中断也从这里返回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_returnrestore_all: RESTORE_ALL
系统调用实现过程
- arch/i386/kernel/traps.c中trap_init函数初始化中断描述符表。
- include/asm-i386/hw_irq.h中定义#define SYSCALL_VECTOR 0x80
- 即在中断描述表的第0x80项填入陷阱门描述符,使控制安全转移到system_call
- 这就保证每次执行int 0x80时,系统把控制权转移到system_call
void __init trap_init(void){ ......... set_system_gate(SYSCALL_VECTOR,&system_call); ......... cpu_init();}
- include/asm-i386/unistd.h定义了NR开头系统调用对应的数字
- 后面会通过宏把用户调用的系统转换成以NR开头,再转换成对应数字通过eax寄存器作为syscall_table的索引
#define __NR_exit 1#define __NR_fork 2#define __NR_read 3#define __NR_write 4#define __NR_open 5#define __NR_close 6
include/asm-i386/unistd.h中宏定义转换
- _syscall0中0表示无参数的系统调用
- type是返回值类型,name是函数名称
- volatile 表示编译器不作优化,int $0x80进入软中断
- ‘=’表示__res为输出参数,‘a’表示占用寄存器eax
- 下一行无‘=’表示输入,此时系统调用name变为__NR_name
#define _syscall0(type,name) \type name(void) \{ \long __res; \__asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name)); \__syscall_return(type,__res); \}int pause(void){ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_pause)); \ __syscall_return(int,__res); \}
- _syscall4中4表示有4个参数,每个参数有一个类型
- 参数通过寄存器ebx,ecx等传递
- SAVE_ALL那里把寄存器值压栈,一方面保护环境,另外把系统调用的参数也入栈,则系统调用处理函数可以从栈中得到参数
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \{ \long __res; \__asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)),"S" ((long)(arg4))); \__syscall_return(type,__res); \}
- 五、系统调用(1)
- NFS系统read调用过程(五)
- 五、系统调用(2)-例子分析
- [文件系统]文件系统学习笔记(五)---mount系统调用(1)
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 走马观花: Linux 系统调用 open 七日游(五)
- 分布式系统阅读笔记(五)-----远程调用
- Linux内核分析(五)系统调用过程解析
- 走马观花: Linux 系统调用 open 七日游(五)
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- xv6源码分析(五):异常、中断、系统调用机制
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- Linux内核分析(五):系统调用深入分析
- Linux系统调用列表(五)进程间通信
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- CardView卡片式布局
- (MFC)窗体屏蔽ESC、按钮添加信息提示
- 罗德里格斯转换公式推导
- php调用java的jar包方法
- ZOJ Problem Set
- 五、系统调用(1)
- iBooks 书籍存放位置
- 使用Maven创建Springmvc+Mybatis+Velocity项目
- 永久解决vs2013+qt+opencv中代码下面很多红色波浪线
- phpcms 允许英文目录有空格
- 企业行业云计算解决方案
- lodash源码分析之chunk的尺与刀
- 2017最新全栈python第2期系列视频教程#92Day
- ARM处理器内核列表