中断简介
Linux0.11使用的Intel i386芯片共有256个中断,表现为中断号0~255.
其中前0~31号中断已经由Intel预定义,其余中断号为可编程中断。
32~47号分别对应linux的16个硬件中断信号(包括时钟、键盘、软盘等)。
0x80中断即128号中断为linux系统调用软中断。
硬中断和软中断
软中断是执行中断指令(int n,n为中断号)主动产生的。而硬中断则是由外部引发的,具有随机性、突发性
硬中断中断号由中断控制器提供,而软中断中断号直接由指令指出。
系统调用
系统调用int 0x80是一个软中断,是应用程序与Linux内核交互的接口。
系统调用初始化过程
在init/main.c初始化主程序main()中调用的sched_init()调度初始化函数中最后一行
set_system_gate(0x80,&system_call);
将0x80软中断与系统调用入口函数system_call联系起来。
---------------------------------------------------------------------------------------------------------------------------------------------
init/main.c : main() : line-132-->kernel/sched.c : sched_init()-->set_system_gate(0x80,&system_call);
------------------------------------------------------------------------------------------------------------------------------------
set_system_gate()
为include/asm目录中system.h文件中的宏函数
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
"movw %0,%%dx\n\t" \
"movl %%eax,%1\n\t" \
"movl %%edx,%2" \
: \
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"o" (*((char *) (gate_addr))), \
"o" (*(4+(char *) (gate_addr))), \
"d" ((char *) (addr)),"a" (0x00080000))
该函数即为设置0x80号中断的中断描述符,附陷阱门描述符结构
DPL为优先级,3为用户态,0为内核态
TYPE即为图中第一行的8~11位
addr为中断处理函数地址
idt为中断描述符表,共256项,每项占8字节,在include/linux/head.h中定义
extern desc_table idt
typedef struct desc_struct {
unsigned long a,b;
} desc_table[256];
即把system_call函数的地址存入和优先级、中断类型的信息存入中断描述符表中0x80号中断
--------------------------------------------------------------------------------------------------------------------------------------------
int 0x80 --> kernel/system_call.s : 80 : _system_call --> include/linux/sys.h : 74 : sys_call_table[eax]()
------------------------------------------------------------------------------------------------------------------------------------
int 0x80在linux0.11中已经封装好系统调用接口,见include/unistd.h文件133~183行
共_syscall0、_syscall1、_syscall2、_syscall3四个宏函数,分别为无参~含有三个参数的系统调用
这里贴出无参的系统调用_syscall0代码
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \ //将输出赋给变量__res,"=a"表示强制使用ax寄存器
: "0" (__NR_##name)); \ //将__NR_name作为输入(系统调用号,在include/unistd.h定义),"0"表示使用与上一行一样的寄存器
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
举个调用该宏函数的例子进行讲解
在init/main.c:23 调用该函数
static inline _syscall0(int,fork)
调用该函数的作用为定义了一个
静态(static) 内联(inline) 返回型为int(对应宏函数的type参数) 函数名为fork(对应宏函数name参数) 的无参函数:static inline int fork(void){}
该函数使用了内联汇编触发0x80号中断
--------------------------------------------------------------------------------------------------------------------------------------------
system_call
为C语言调用的汇编函数,在kernel/system_call.s第80行
主要作用为将进程从用户态切换到内核态,然后调用该文件94行的call _sys_call_table(,%eax,4)
这条指令的含义为跳转到sys_call_table + eax*4地址去执行。
sys_call_table为在include/linux/sys.h定义的C语言-函数指针数组,数组中存放了72个系统调用函数指针。
eax为中断号
*4是因为32位机每个指针占四个字节
即实际指令为跳转到系统调用函数指针数组中执行对应的系统调用。
以下为函数指针数组的初始化
typedef int (*fn_ptr)(); //在include/linux/sched.h 38行
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid };
------------------------------------------------------------------------------------------------------------------------------------
欢迎大家指正、讨论