最近有点事情,立刻要开学了,因此学习的脚步就慢下来了。这一篇主要是来讲操做系统的系统调用的,像C语言的printf深刻到内部就是一个有关屏幕输出的系统调用linux
以前提过操做系统是对硬件的抽象,也是软硬件之间的一层。以前好比若是咱们想要在屏幕上输出一些字符,就须要一些指令操做,而后把数据放到显存上。可是在有了操做系统后,就不须要这样作,也不能这样作了。这时候只要操做系统提供一个接口来让咱们完成这个任务git
由操做系统实现提供的全部系统调用所构成的集合即程序接口或应用编程接口(Application Programming Interface,API)。是应用程序同系统之间的接口。github
在硬件设计上,经过区份内核态和用户态来把内核程序和用户程序隔离开编程
CS寄存器最低的两位为0便是内核态,为3是用户态数组
可是系统调用的代码是处在内核态的,因此就须要提供一种方法来可以让用户程序进入内核态来实现系统调用缓存
在X86里,INT指令就是硬件用来提供由用户态进入内核态的方法,因此系统调用的实现就能够变为:函数
稍微说一下C里的内联汇编,以避免以后忘记。学习
gcc的内联汇编通常都是这个格式spa
asm ( 汇编指令
: 输出操做数 // 非必需
: 输入操做数 // 非必需
: 其余被污染的寄存器 // 非必需
);
复制代码
第一部分就是汇编指令操作系统
第二部分是输出操做数,都是 "=?"(var) 的形式, var能够是任意内存变量(输出结果会存到这个变量中), ?通常是下面这些标识符 (表示内联汇编中用什么来代理这个操做数):
a,b,c,d,S,D 分别表明 eax,ebx,ecx,edx,esi,edi 寄存器 r 上面的寄存器的任意一个(谁闲着就用谁) m 内存 i 当即数(常量,只用于输入操做数) g 寄存器、内存、当即数 都行 在汇编中用%序号来表明这些输入/输出操做数,序号从0开始。为了与操做数区分开来,寄存器用两个%引出,如:%%eax
第三部分是是输入操做数,都是 "?"(var) 的形式, ? 除了能够是上面的那些标识符,还能够是输出操做数的序号,表示用 var 来初始化该输出操做数,上面的程序中 %0 和 %1 就是一个东西,初始化为 1(a的值)。
第四部分标出那些在汇编代码中修改了的、 又没有在输入/输出列表中列出的寄存器, 这样 gcc 就不会擅自使用这些"危险的"寄存器。 还能够用 "memory" 表示在内联汇编中修改了内存, 以前缓存在寄存器中的内存变量须要从新读取。
#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))
set_system_gate(0x80,&system_call);
复制代码
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
复制代码
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
复制代码
#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; \ }
复制代码
这样对于一个系统调用就会变成
printf 用户调用
⬇
int 0x80 库函数的实现
⬇
进入内核
system_call 中断调用
⬇
sys_ 系统调用