操作系统 | 1.2操作系统接口 和 系统调用

参考来源:哈工大,李治军老师,操作系统公开课

学习之前的问题:什么是接口?
--> 我们使用水龙头接水时,我们根本不需要知道水是怎么从水厂来的,只要打开水龙头这个 “接口”,就可以取到水了。
--> 对于操作系统来说,用户不需要关心硬件是怎么工作的,这些由操作系统来完成,而我们只需要使用操作系统提供的“接口”就可以了。

1. 什么是操作系统的接口?

  • 用户是怎么使用计算机的?
    • 命令行
    • 图形按钮
    • 应用程序

(1) 命令行是怎么回事?

1_1_图21

(2) 图形按钮又是怎么回事?

1_1_图22

  • 消息机制:不断的从消息循环队列中不断取消息的循环,每一个消息都对应消息处理函数

(3) 那么,什么是操作系统的接口?

  • 根据上面两个例子,我们发现:
    • 用户使用计算机的方法:普通C代码 + 重要函数
  • 操作系统提供了这些重要函数。
    • 接口就是操作系统提供的这些重要函数。。。接口表现为函数调用,由于函数是系统提供,因此,操作系统接口就是系统调用。
小总结:操作系统接口就是系统调用 system_call

(4) 操作系统的接口都有哪些?

  • POSIX:Portable Operating System Interface of Unix(IEEE制定的一个标准族)
  • 简单看几个操作系统接口
    • 1_1_图23
    • 如果想要查看所有的接口,那么查找POSIX手册即可。

2. 系统调用的实现

系统调用的直观实现,类比其他系统调用问题

(1) 实现一个 whoami 系统调用

1_1_图24

  • 在内核中,有一个字符串存储着这台计算机的用户名。whoami系统调用就是要把这个字符串打印在屏幕上。

  • 仔细思考下面的问题
    • main(){whoami}在内存中被取值执行,系统内核whoami(){printf(100,8);}以及"lizhijun"都是在内存中,那么为什么不能直接跳转到内存100的地方找到这个字符串然后打印输出呢?
      • 如果可以这样做的话,那么系统是不安全的。因为你的root账号和密码就不安全了。因此,不能随意的调用数据,不能随意的jmp。
  • 问题1:同样在内存中,那么什么机制可以阻止jmp呢?
  • 问题2:不让jmp,那么怎么才能使用内核中的函数呢?

(2) 解决问题1:用户态、内核态和特权级

  • 补充一些知识:
    • 1_1_图26

将内核程序和用户程序隔离

  • 1_1_图25
    • 段寄存器CS的低两位:CPL (进程当前段的特权级)
    • 段选择子的低两位:RPL (进程对段访问的请求权限)
      • 处理器总共提供了6个段寄存器来保存段选择子;因此同一时刻,可能存在多个RPL(每个段选择子都有一个RPL),但只要段选择子CS中的RPL才等于CPL。
      • 这是因为:CS寄存器也叫“代码段寄存器”。
    • 段寄存器DS的低两位:DPL (目标段的特权级)
    • 当 DPL>=max{CPL,RPL}时,可以访问。
  • 在GDT表中,whoami()的DPL被置为0,为内核态,对应内存属于内核段。而用户程序main(){whoami();}的CPL是3,因此不能直接访问内核。

(3) 解决问题2:中断门(中断方法)

jmp和mov都不能进入内核,只有中断才能进入内核。

<1> 硬件提供了“主动进入内核的方法”

1_1_图27

<2> 系统调用的实现

1_1_图28

<3> 系统调用实现细节

1_1_图29

  • 对比 <2>... 和 <3>... 中的图片内容对C语言的宏函数进行展开,得到write函数
    • 可以看到的是:type=int, name=write, atype=int, a=fd, btype=const char* , b=buff, ctype=off_t, c=count.
    • 因此,函数头是int write(int fd, const char* buff, off_t=count){...}
    • 函数体是内嵌汇编
      • int write(int fd, const char* buff, off_t=count){
            把 _NR_write 置给 eax;
            把 fd 置给 ebx;
            把 buff 置给 ecx;
            把 count 置给 edx;
            ...
        }
        
      • 代码核心内容也就是将系统调用号置给eax,调用int中断,进入内核。
  • _NR_write 是系统调用号
    • 系统调用必须通过 int 0x80 调用号进入内核。但是,许多程序都要调用内核,并且调用函数也不相同,那么怎么调用想要的函数呢?这就需要系统调用号

<4> int 0x80中断的处理

1_1_图30

  • 核心就是在IDT中断向量表中的80号进行初始化.
    • 将IDT中80中断的DPL置为3。
    • 将段选择符设置为8,因此,最低两位的CPL是00(内核态)。
    • 段选择符+处理函数入口偏移,找到处理函数。
    • ...中断返回时,会把CS的最低两位重新置为3,变为用户态。

<5> 中断处理程序:system_call

1_1_图31

  • sys_call_table就是系统调用函数表
  • 4*%eax 其中,4表示每个函数占4字节,%eax的内容是系统调用号
    • 这里,%eax被置为了4,说明 sys_write 函数在第五位(从0开始)。

<6> _sys_call_table

1_1_图32

3. 总结

操作系统的接口:系统调用

问题一:操作系统怎么阻止你进入内核?
    将分段的内存设置特权级,用户态在用户段,内核态在内核段。根据特权级,有访问权限。

问题二:用户怎么调用操作系统内核代码(或者说使用系统调用函数)
  --->以用户调用C语言的 printf() 函数为例:
    1. 此时,CPL=3,处于用户态;
    2. 通过库函数,将 printf() 变成 int ox80代码
    3. int 0x80代码 在系统初始化时,做成了一个system_call
    4. 在中断处理时,将IDT中 0x80 处的 DPL 置为3,能够访问IDT的0x80;
    5. 然后把CPL置为0,进入内核态。
    6. 查表sys_call_table,根据系统调用号,找到你要调用的函数sys_write。
    7. 中断结束时,又将 CPL重新置为3,进入用户态。
思考问题:都有哪些提权的方法?
  • 调用门
  • 中断门
  • 陷阱们
  • 任务门
    由于现在只是希望初步的了解操作系统,因此不做深入的学习。

posted on 2020-10-02 20:09  wangxx06  阅读(869)  评论(0)    收藏  举报

导航