系统调用的三层机制
用户态、内核态与系统调用
Inter x86 CPU定义了4种不同的执行级别,数字越小特权越高。Linux系统采用了其中的0、3两个特权级别。
- 用户态:Ring3,运行于用户态的代码则要受到处理器的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问。
- 内核态:Ring0,在处理器的存储保护中,核心态,或者特权态(与之相对应的是用户态),是操作系统内核所运行的模式。运行在该模式的代码,可以无限制地对系统存储、外部设备进行访问。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。
系统调用是处于用户态的进程主动请求切换到内核态的一种方式。用户态的进程通过系统调用申请使用操作系统提供的系统调用服务例程来处理任务。而系统调用的机制,其核心仍是使用了操作系统为用户特别开发的一个中断机制来实现的,即软中断。
在执行系统调用时,进程的执行步骤如下图所示:
实现系统调用
在实验楼中,我编写了一个使用系统调用的程序。该程序使用的系统调用为alarm()
功能是设置一个它可以在进程中设置一个定时器,当定时器指定的时间到时,它向进程发送SIGALRM信号。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler(int sign)
{
printf("hello world!
");
}
int main()
{
int i;
signal(SIGALRM, handler);
alarm(3);
for(i=0;i<5;i++)
{
printf(" sleep %d
", i+1);
sleep(1);
}
return 0;
}
其编译执行后发现用户权限不够,使用root权限执行。
使用内嵌汇编语言实现系统调用
将上文中使用的系统调用alarm()
通过汇编语言实现:
查找系统调用表可以发现alarm的系统调用号为27
asm volatile(
"movl $3,%%%ebx
"
"movl $0x1b, %%eax
"
"int $0x80
"
:"=a" (ret)
);
首先将系统调用的参数3写入ebx;
然后将系统调用号1B写入eax;
执行中断 0x80;
这样就可以执行系统调用alarm。