Chinaunix首页 | 论坛 | 博客
  • 博客访问: 89800
  • 博文数量: 31
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 142
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-08 13:05
文章分类

全部博文(31)

文章存档

2014年(17)

2013年(11)

2012年(3)

我的朋友

分类: LINUX

2013-12-18 10:28:55

        linux下的系统调用如何从用户态进入内核态?这个问题一直以来都是模模糊糊,最近阅读了《程序员的自我修养》第十二章:系统调用与API,终于对此有了一个算是比较清醒和深刻的认识:总结了一下,画了一个图大致如下(注:没有内核态返回内核态流程):
        
        其中x86有两种方式从用户态陷入内核态:int 0x80系统调用以及新型的sysenter指令。本文暂且基于经典的int 0x80方式。对于用户态,地球人都知道我们写的C程序要调用到glibc库,与我们所理解的库调用并没有多大差异。但是对于glibc库如何陷入内核态,就需要详细的了解一下。
        首先,glibc对系统调用做了一系列的封装,典型如fork()函数,比如我们所调用的fork()函数,在glibc中实际上定义的是如下一个宏:
        _syscall0(pid_t, fork);
        这里有一系列的__syscallN宏定义,其中的N范围从0-6,这是因为x86在linux下支持的最多的参数个数为6个。每个宏对应着相应个数的参数。fork()没有参数,所以是_syscall0。这个宏的参数pid_t是返回值的类型,fork是函数的名字,用于下一步的扩展。其中i386版本的_syscall0宏定义如下:
        
#define _syscall0(type,name)     \
{   long __res;                          \
    __asm__ volatile("int 0x80"    \
        : "=a" (__res)                  \
        : "0"   (__NR_##name));  \
    __syscall_return(type, __res);\
}        
        这就是采用gcc内联汇编的形式编写的_syscall0宏,通过这个宏,可以很明显的看出来是通过int 0x80方式触发中断,进入内核态中断处理。并指明使用eax来传递参数和返回值。而此处的参数就是系统调用号,所以由此可知,触发中断进入内核时,所有的系统调用号都是通过eax传递,这样在内核态,想知道是什么系统调用,直接查询eax寄存器就可以得到系统调用号了。
        进入中断,查询中断号,根据安装的中断处理程序就可以得到这是一个系统调用,于是查询系统调用表,根据eax中传递的系统调用号就可以得到对应的系统调用。
        内核处理完成之后,最后通过__syscall_return从用户态返回内核态。当然,返回值还是通过eax传递返回的。

        至于用户态和内核态的堆栈的切换,记住其中最重要的一个宏SAVE_ALL,就可以查询到其中的切换了,此处也就不讲述。
阅读(1000) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~