系统调用:用户级函数如何通过INT 80中断进入操作系统内核
一个用户函数进入系统内核的过程以printf()打印内核中的一段字符串为例printf()是用户函数无法进入内核,因此需要进行系统调用,进入内核的方式是使用int 0x80中断printf()函数想要进入系统内核是通过系统调用write()实现/** 位置:linux/lib/write.c*/#define __LIBRARY__#include <unistd.h>_syscall
以printf()
打印内核中的一段字符串为例
-
printf()
是用户函数无法进入内核,因此需要进行系统调用,进入内核的方式是使用int 0x80
中断 -
printf()
函数想要进入系统内核是通过系统调用write()
实现/* * 位置:linux/lib/write.c */ #define __LIBRARY__ #include <unistd.h> _syscall3(int,write,int,fd,const char *,buf,off_t,count)
-
_syscall3
被展开成一段包含int 0x80
中断汇编代码的C代码,见下文代码/* * 位置:include/unistd.h */ ... #define __NR_write 4 ... /* * 定义了一个返回值 long型变量:__res * __asm__ 表示准备内嵌汇编代码 volatile表示编译器不对代码进行优化 * "int $0x80" 表示调用int 0x80中断 */ #define _syscall3(type,name,atype,a,btype,b,ctype,c) \ type name(atype a,btype b,ctype c) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \ if (__res>=0) \ return (type) __res; \ errno=-__res; \ return -1; \ } /* // 指明%ebx中存放了输入参数a,也即实际的操作为:将参数a赋值给%ebx中 // 指明%ecx中存放了输入参数b,也即实际的操作为:将参数b赋值给%ecx中 * 注意:AT&T语法中 * $表示立即数 %表明这是一个寄存器 * 17行:"=a" 指定输出操作数的典型规则,'=' 是输出操作数的特有字段,表明只写; * 17行:'a'表明先将结果输出到%eax中,然后再由%eax输出到__res中 * 18行:输入参数的规则 * 18行:"0"表明和输出寄存器是同一个寄存器,也即%eax,意思是输入参数__NR_##name会存放在%eax中 * 18行:"b"((long)(a))表明输入参数a会放在寄存器%ebx中,也即会将参数a先存储到%ebx中 * * 综上:该内嵌代码的意思是: * 调用int 0x80中断 * 输入参数是__NR_##name、a、b、c * 返回值先存放在%eax中,然后再赋值到__res中 */
因此
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
其展开后结果如下所示: -
通过
int 0x80
中断,代码将进入系统内核操作系统的已经做好准备工作如下示:
①
main.c
中初始化②在函数
sched_init()
中设置系统调用中断门,引导int 0x80
中断,可以看到该中断需要靠system_call
函数处理③
int 0x80
中断的处理,set_system_gate()
其实就是宏_set_gate()
④宏
_set_gate()
也是嵌入汇编指令,其中的addr
是&system_call
,是个地址;该宏主要就是初始化好了IDT表
中int 0x80
对应的表项- 上述的
&idt
即为IDT表
的起始位置,所以&idt[n]
(这里n=0x80
)就表示中断0x80
对应的表项在IDT表中位置 - 设置好
CS=8
,其中CPL=0
- 设置好
IP=addr
⑤如此一来,当
int 0x80
中断来的时候,根据CS = 8 也即段选择子为8,就可以去GDT表中找到真正要执行的代码的段基地址,然后结合IP的偏移,找到INT 0x80
的实际处理函数system_call
- 上述的
-
下面我们进入
system_call
代码,也即真正的中断处理程序linux/kernel/system_call.s
在程序中,跳到一个表里面的一个函数地址_sys_call_table
是一个函数地址表,其中write对应的处理函数的地址就放在表的%eax
索引处 -
然而
sys_write
才是真正的处理函数,可以对内核中的数据进行读取,也就是所谓的系统调用
更多推荐
所有评论(0)