unistd.h
linux0.11 中,include/unistd.h 中定义了72个系统调用号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#ifdef __LIBRARY__ #define __NR_setup 0 /* used only by init, to get system going */ #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_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 #define __NR_chown 16 #define __NR_break 17 #define __NR_stat 18 #define __NR_lseek 19 #define __NR_getpid 20 #define __NR_mount 21 #define __NR_umount 22 #define __NR_setuid 23 #define __NR_getuid 24 #define __NR_stime 25 #define __NR_ptrace 26 #define __NR_alarm 27 #define __NR_fstat 28 #define __NR_pause 29 #define __NR_utime 30 #define __NR_stty 31 #define __NR_gtty 32 #define __NR_access 33 #define __NR_nice 34 #define __NR_ftime 35 #define __NR_sync 36 #define __NR_kill 37 #define __NR_rename 38 #define __NR_mkdir 39 #define __NR_rmdir 40 #define __NR_dup 41 #define __NR_pipe 42 #define __NR_times 43 #define __NR_prof 44 #define __NR_brk 45 #define __NR_setgid 46 #define __NR_getgid 47 #define __NR_signal 48 #define __NR_geteuid 49 #define __NR_getegid 50 #define __NR_acct 51 #define __NR_phys 52 #define __NR_lock 53 #define __NR_ioctl 54 #define __NR_fcntl 55 #define __NR_mpx 56 #define __NR_setpgid 57 #define __NR_ulimit 58 #define __NR_uname 59 #define __NR_umask 60 #define __NR_chroot 61 #define __NR_ustat 62 #define __NR_dup2 63 #define __NR_getppid 64 #define __NR_getpgrp 65 #define __NR_setsid 66 #define __NR_sigaction 67 #define __NR_sgetmask 68 #define __NR_ssetmask 69 #define __NR_setreuid 70 #define __NR_setregid 71 |
以及几个宏函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define _syscall0(type,name) \ type name(void) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name)); \ if (__res >= 0) \ return (type) __res; \ errno = -__res; \ return -1; \ } #define _syscall1(type,name,atype,a) \ type name(atype a) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(a))); \ if (__res >= 0) \ return (type) __res; \ errno = -__res; \ return -1; \ } #define _syscall2(type,name,atype,a,btype,b) \ type name(atype a,btype b) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \ if (__res >= 0) \ return (type) __res; \ errno = -__res; \ return -1; \ } #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; \ } #endif /* __LIBRARY__ */ |
这4个 syscall 函数可以用来展开成为系统调用函数,后面的数字表示函数有几个参数。它的形式参数
type | 函数返回类型 |
name | 函数名 |
atype | 第一个参数类型 |
a | 第一个参数 |
btype | 第二个参数类型 |
b | 第二个参数 |
ctype | 第三个参数类型 |
c | 第三个参数 |
使用syscall
以write为例, 在unistd.h中,write函数声明如下:
1 |
int write(int fd, const char * buf, off_t count); |
只要按照syscall的参数格式填上去
type | 函数返回类型 | int |
name | 函数名 | write |
atype | 第一个参数类型 | int |
a | 第一个参数 | fd |
btype | 第二个参数类型 | const char * |
b | 第二个参数 | buf |
ctype | 第三个参数类型 | off_t |
c | 第三个参数 | count |
lib/write.c 中是他的实现
1 2 3 4 |
#define __LIBRARY__ #include <unistd.h> _syscall3(int,write,int,fd,const char *,buf,off_t,count) |
展开后就变成了(为了展示清除额外手动添加了换行)
1 2 3 4 5 6 7 8 9 10 11 |
int write(int fd,const char * buf,off_t count) { long __res; __asm__ volatile ("int $0x80" : "=a" (__res) : "0" (__NR_write),"b" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(count)) ); if (__res>=0) return (int) __res; errno=-__res; return -1; } |
这是一个内嵌汇编,通过int 0x80 中断来实现系统调用。
第一个冒号后面是输出,输出的寄存器前面要有等号,”=a” 表示 输出存放在 eax 寄存器中,最终汇编结束时会赋值给 变量 __res
第二个冒号后面是输入,把输出和输入用到的寄存器按顺序编号(从输出寄存器开始从左到右,再从输入寄存器从左到右),”0″ 表示的是第一个寄存器,也就是输出用的eax,它的值被赋值为__NR_write, 也就是系统调用号。后面”b”, “c”, “d”表示寄存器ebx,ecx和edx,用来存放write的三个参数。可以用的寄存器符号表示表:
符号 | 表示 | 符号 | 表示 |
a | eax | m | 使用内存地址 |
b | ebx | o | 使用内存地址可以加偏移 |
c | ecx | I | 使用常数0-31 |
d | edx | J | 使用常数0-63 |
S | esi | K | 使用常数0-255 |
D | edi | L | 使用常数0-65535 |
q | 动态分配可寻址寄存器
eax, ebx, ecx, edx |
M | 使用常数0-3 |
r | 使用任意动态分配的寄存器 | N | 使用1字节常数0-255 |
g | 使用通用有效地址、
(可寻址寄存器或内存地址) |
O | 使用常数0-31 |
A | eax与edx联合(64位) | = | 输出操作数 |
+ | 操作数可读可写 | & | 早期会变的操作数,表示使用完操作数之前,内容会被修改 |
int 0x80
在初始化的main函数中的sched_init中,有下面的代码
1 2 3 4 5 |
void sched_init(void) { ... set_system_gate(0x80,&system_call); } |
这句话设置IDT表,将0x80中断号和system_call函数进行绑定,调用int 0x80时,就会执行system_call函数。
include/asm/system.h
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#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)) |
因此这个语句
1 |
set_system_gate(0x80,&system_call); |
宏展开后,变成了
1 |
_set_gate(&idt[0x80],15,3,&system_call); |
第一个参数表示描述符的地址,0x80号中断的描述符地址就是&idt[0x80] , 第二个参数描述符类型,第三个参数是描述符特权级dpl,第四个参数是偏移地址
再展开,变成了
1 2 3 4 5 6 7 8 9 10 11 12 |
__asm__ ( "movw %%dx,%%ax\n\t" "movw %0,%%dx\n\t" "movl %%eax,%1\n\t" "movl %%edx,%2" : : "i" ((short) (0x8000+(3<<13)+(15<<8))), "o" (*((char *) (&idt[0x80]))), "o" (*(4+(char *) (&idt[0x80]))), "d" ((char *) (&system_call)), "a" (0x00080000) ); |
IDT表的表项
代码中两个”o” 分别是表项的低4个字节和高4个字节,序号是%1和%2
第一个输入 (short) (0x8000+(3<<13)+(15<<8)) 的二进制是
1110 1111 0000 0000
放到dx中,也就是edx的低2个字节,赋值给%2 ,也就是设置了
而edx的高2个字节是system_call偏移地址的高2个字节
dex的低2个字节是system_call偏移地址的低2个字节,复制给ax(eax的低2个字节)
eax的高2个字节是0x0008
表项赋值为:
system_call
kernel/system
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
system_call: cmpl $nr_system_calls-1,%eax ja bad_sys_call push %ds push %es push %fs pushl %edx pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10,%edx # set up ds,es to kernel space mov %dx,%ds mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs call sys_call_table(,%eax,4) pushl %eax movl current,%eax cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je reschedule ret_from_sys_call: movl current,%eax # task[0] cannot have signals cmpl task,%eax je 3f cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 3f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx andl %ebx,%ecx bsfl %ecx,%ecx je 3f btrl %ecx,%ebx movl %ebx,signal(%eax) incl %ecx pushl %ecx call do_signal popl %eax 3: popl %eax popl %ebx popl %ecx popl %edx pop %fs pop %es pop %ds iret |
通过 call sys_call_table(,%eax,4) ,call的是 sys_call_table + 4 * eax,4表示每个函数地址是4个字节。
sys_call_table是一个函数表,在 include/linux/sys.h中定义
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 }; |
write 的 调用号是4,和这里对应的。