操作系统(linux0.11)的系统调用

什么是系统调用

Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户自己提供,运行于用户态.

为什么需要系统调用

一般来说,进程是不能访问内核的,避免应用程序不正确的使用资源,导致系统奔溃.CPU硬件决定着内存访问权限(保护模式,内存段页管理).在linux中系统调用是用户空间访问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口

应用程序如何调用系统调用

  1. 把系统调用的编号存入 EAX
  2. 把函数参数存入其它通用寄存器
  3. 触发 0x80 号中断(int 0x80)

操作系统实现系统调用的基本过程

  1. 应用程序调用库函数(API)
  2. API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态
  3. 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用)
  4. 系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数
  5. 中断处理函数返回到 API 中
  6. API 将 EAX 返回给应用程序

linux0.11 系统调用实现(close)

应用程序调用系统调用

在lib/close.c有着close的实现

#define __LIBRARY__
#include 
_syscall1(int,close,int,fd)

_syscall1是一个宏,在include/unistd.h中定义。将_syscall1(int,close,int,fd)进行宏展开,可以得到:

int close(int fd) 
{ 
    long __res;      
    __asm__ volatile ("int $0x80" 
        : "=a" (__res) 
        : "0" (__NR_close),"b" ((long)(fd)));      
    if (__res >= 0)
        return (int) __res; 
    errno = -__res; 
    return -1; 
}

这是段内联汇编,将宏NR_close存入存入EAX,将参数fd存入EBX,进行80中断调用调用返回后,从EAX取出返回值,存入res,再通过对res的判断决定传给API的调用者什么样的返回值.其中NR_close就是系统调用的编号,在include/unistd.h中定义

#define __NR_close  6
int 0x80中断的处理

在操作系统启动时,会初始化IDT,设置0x80对应的中断描述符

void sched_init(void)
...
{set_system_gate(0x80,&system_call);};

set_system_gate是个宏,include/asm/system.h中定义为

#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))

填写IDT,将system_call函数地址写到0x80对应的中断描述符中,当发生中断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) #sys_call_table+4*%eax
    pushl %eax
    movl current,%eax
    cmpl $0,state(%eax)     # state
    jne reschedule
    cmpl $0,counter(%eax)       # counter
    je reschedule

主要为call sys_call_table(,%eax,4)这句话,其中eax放的是系统调用号,sys_call_table是函数指针数组的起始地址,跳到sys_call_table+4*%eax地址执行,即内核实现的sys_close函数

你可能感兴趣的:(操作系统(linux0.11)的系统调用)