系统调用的工作机制

所有的程序员在写程序的时候都离不开通过库函数的方式和系统调用打交道


什么是用户态和内核态?(从CPU指令级别的角度)

一般现代CPU都有几种不同的指令执行级别,什么样的程序可以执行什么的指令
在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这时CPU执行级别就对应着内核态
而在相应的低级别执行状态下,代码的掌控范围会受到限制。只能在对应级别允许的范围内活动
举例:intel x86 CPU有四种不同的执行级别0-3,Linux只使用了其中的0级3级分别来表示内核态用户态


如何区分用户态和内核态?(从进程地址空间的角度)

cs寄存器的最低两位表明了当前代码的特权级
CPU每条指令的读取都是通过cs:eip这两个寄存器:
      其中  cs是代码段选择寄存器,eip是偏移量寄存器
上述判断由硬件完成

在32位x86的机器上,有4G的进程地址空间(逻辑地址),在内核态的时候全都可以访问,在用户态的时候,只能访问0x00000000-0xbfffffff的地址空间。也就是说0xc0000000以上的地址空间只能在内核态下访问


中断处理是从用户态进入内核态主要的方式

当从用户态切换到内核态的时候,必须用户态的寄存器上下文保存起来,同时设置内核态的寄存器内容
中断/int指令会在堆栈上保存一些寄存器的值
      如:用户态栈顶地址、当时的状态字、当时的 cs:eip 的值
同时设置内核态的栈顶地址、内核态的状态字,中断处理程序的入口地址 cs:eip 的值(对于系统调用来讲,它是指向system_call函数)


中断/int指令发生后第一件事就是保护现场

保护现场就是进入中断程序保存需要用到的寄存器的数据

当进入到中断处理程序后,一开始就执行SAVE_ALL,把其它的一些寄存器的值push到内核堆栈里面去

SAVE_ALL

中断处理结束前最后一件事是恢复现场

恢复现场就是退出中断程序恢复寄存器的数据

当中断处理程序结束之后,它会RESTORE_ALL,把保存的用户态的寄存器再pop出来到当前的CPU里面,最后iret,iret指令与中断信号(包括int指令)发生时CPU做的动作刚好相反

RESTORE_ALL

中断处理的完整过程

interrupt(ex:int 0x80)
save cs:eip/ss:esp/eflags(current) to kernel stack, then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack)
SAVE_ALL
....  // 内核代码,完成中断服务,发生进程调度
RESTORE_ALL
iret
pop cs:eip/ss:esp/eflags from kernel stack

SAVE_ALL....如果发生了进程调度,那么当前的状态都会暂时保存在系统里面,当下一次发生进程调度切换回当前进程的时候,就会接着把它执行完,RESTORE_ALL....


以系统调用为例,看中断具体是怎么执行的

系统调用通过软中断向内核发出一个明确的请求,是操作系统为用户态进程与硬件设备进行交互提供的一组接口

封装例程 (wrapper routine),唯一目的就是发布系统调用,让程序员在写代码的时候不需要用汇编指令来触发一个系统调用,而是直接调用一个函数就可以触发一个系统调用

应用编程接口(application program interface, API) 只是一个函数定义。一般每个系统调用对应一个封装例程,库再用这些封装例程定义出给用户的API。但并不是每个API都对应一个特定的系统调用,API可能直接提供用户态的服务,例如一些数学函数。一个单独的API可能调用几个系统调用,不同的API可能调用了同一个系统调用

应用程序、封装例程、系统调用处理程序及系统调用服务例程之间的关系

User Mode 用户态      Kernel Mode 内核态

xyz()函数,是系统调用对应的API,这个应用程序编程接口里面封装了一个系统调用,这个系统调用会触发一个int 0x80的中断,0x80这个中断向量对应着system_call这个内核代码的起点,这个内核代码里面会有SAVE_ALL,然后执行到sys_xyz()中断服务程序,进入程序里面处理,在中断服务程序执行完之后会ret_from_sys_call,在return的过程中可能会发生进程调度(这是一个进程调度的时机),如果没有进程调度,就会iret,回到用户态接着执行

Summary

系统调用的三层皮:API、中断向量对应的system_call、中断服务程序sys_xyz

当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,在Linux中是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常。(Intel Pentium II中引入了sysenter指令(快速系统调用),2.6已经支持)

内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数,使用eax寄存器(系统调用号将xyz()和sys_xyz()关联起来了)


系统调用的参数传递方法

普通函数调用的时候,可以采用把参数压栈的方式传递参数。但是从用户态到内核态,怎么传递参数呢?

system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号

一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)
这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号
进入sys_call之后,立即将eax的值压入内核堆栈

寄存器传递参数具有如下限制:
1)每个参数的长度不能超过寄存器的长度,即32位
2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx, ecx,edx,esi,edi,ebp)
超过6个怎么办?

如果超过6个,就把某一个寄存器作为一个指针,指向一块内存,进入到内核态之后可以访问到所有的地址空间,通过内存来传递参数


通过库函数API使用系统调用获取系统当前时间

通过库函数API使用系统调用获取系统当前时间

用汇编方式触发系统调用获取系统当前时间

用汇编方式触发系统调用获取系统当前时间

系统调用传递第一个参数使用ebx,这里是NULL
使用eax传递系统调用号,这里time是13
系统调用的返回值使用eax存储,和普通函数一样


(完)


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 安大大 + 原创作品转载请注明出处 + 《Linux操作系统分析》MOOC课程 用户态、内核态和中断处理过程 程序...
    夏天的篮球阅读 1,665评论 0 0
  • 简介 lab3 将主要实现能运行被保护的用户模式环境(protected user-mode environmen...
    找不到工作阅读 6,927评论 1 8
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,784评论 0 27
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,598评论 1 19
  • 晨起,风雨大作。路遇一对母女。小姑娘被母亲的怀抱与母亲手中的伞隔绝在风雨之外。 公交车上,各种湿哒哒的雨伞,总会有...
    岁月嘉礼阅读 216评论 0 0