关于linux:读书笔记Linux内核设计与实现5

6次阅读

共计 2669 个字符,预计需要花费 7 分钟才能阅读完成。

零碎调用

零碎调用就是一组用于用户过程与内核交互的接口。实现:

  1. 应用程序 受限 地拜访硬件设施(硬件的形象接口),基于权限进行拜访裁决。
  2. 创立过程间通信机制
  3. 申请操作系统其它资源的能力。
    零碎调用是用户控件拜访内核的惟一形式,除了异样和陷入之外。

API、POSIX 和 C 库

API:应用程序通过在用户控件实现的利用编程接口。通过 API 进行零碎调用,而不间接进行零碎调用,不须要和零碎调用一一对应。
POSIX 指标是提供基于 Unix 的可移植操作系统规范。依据 POSIX 定义的 API 函数和零碎调用都有间接的关系。
linux 的零碎调用能够作为 C 库的一部分进行提供,C 库实现了 Unix 零碎次要 API,提供了绝大部分 POSIX API。
程序员只跟 API 打交道,Unix 的接口设计准则是只提供机制而不提供策略。

应用 Linux 零碎调用

程序员通过 API 调用进行零碎调用,关注输出,输入,全局错误码 errno 变量。通过 perror()库函数,把 errno 变量进行打印。定义零碎调用:

asmlinkage long sys_getpid(void)

限定词 asmlinkage 是一个编译指令,告诉编译器仅从栈中提取该函数的参数,所有零碎调用都须要这个限定词。

函数返回值 long,兼顾 32 位和 64 位,零碎调用在用户空间和内核空间有不同的返回值类型,在用户空间为 int,内核空间 long。

零碎调用号
  1. 每个零碎调用对应一个零碎调用号。
  2. 过程不会提及零碎调用的名称。
  3. 一旦调配就不能扭转,否则零碎解体,不兼容。
  4. 零碎被删除或者废除后,Linux 有一个未实现零碎调用 sys_ni_syscall(),除了返回 -ENOSYS 外不做任何工作。
  5. sys_call_table 零碎调用表中记录了所有注册过的零碎调用列表。
  6. 不同体系结构,零碎调用表不同。x86 在 arch/i386/kernel/syscall_64.c
零碎调用性能

Linux 零碎调用性能比其余操作系统要好,因为他有更短的上下文切换,而且零碎调用处理程序和每个零碎调用自身都比拟简洁。

零碎调用处理程序

用户态过程通过软中断的形式告诉内核须要执行一个零碎调用:引发一个异样,促使零碎切换到内核态去执行异样处理程序,该程序就是零碎调用处理程序,中断号 128,int $0x80 指令触发中断,system_call(),与硬件体系结构严密相干
,x86-64 用汇编写的 entry_64.S,后减少了 sysenter 指令。

陷入内核的同时,须要把零碎调用号一并传给内核,x86 上通过寄存器 eax.system_call()将零碎调用号与 NR_syscalls 作比拟,大于或等于 NR_syscalls,函数返回 -ENOSYS,否则就执行相应的零碎调用:

call *sys_call_table(,%rax,8)

因为零碎调用表中的表项是以 64 位(8 字节)类型寄存的,所以内核须要将给定的零碎调用号 *4, 而后将所得后果在该表中查问地位(????),x86-32 零碎上,代码很相似,只是用 4 代替 8。

零碎调用的参数传递通过寄存器进行传递 ebx, ecx, edx, esi 和 edi 寄存前 5 个参数,6 个或以上,用一个独自寄存器寄存指向这些参数在用户空间地址的指针。

返回值也是通过寄存器进行传送,eax 寄存器中。

零碎调用实现

在 linux 设计和实现一个零碎调用是难题,退出内核不麻烦。

  1. 决定用处,调用参数,返回值,错误码。力求简洁,稳固,向后兼容,可移植性。许多零碎调用通过提供标记参数以确保向前兼容,标记并不是用来让单个零碎调用具备多个不同的行为,而是为了即便减少新的性能和选项,也不毁坏向后兼容或不须要减少新的零碎调用。别对机器的字节长度和字节序做假如。
  2. 参数验证,用户输出,用户指针(拜访权限问题, 指向区域必须是用户控件,指针指向的内存区域在过程的地址空间内,可读可写可执行的权限分清)copy_from_user(), copy_to_user(): 目标地址,源地址,内存长度。可能引起阻塞,当蕴含用户数据的页被换到硬盘上而不是物理内存上时,过程休眠,晓得缺页处理程序将该页从硬盘从新换回物理内存。权限验证:通过 capable() 函数查看是否有权对特定资源进行操作,非 0 有权操作。
  3. 零碎调用上下文,如果执行零碎调用,内核就处于过程上下文,current 指针指向当前任务。

    • 过程上下文中,内核能够休眠(零碎调用阻塞或者 schedule()调用)
    • 能够被抢占,新过程能够应用雷同的零碎调用,所以要留神该零碎调用是否可重入。
    • 零碎调用返回时,控制权依然在 system_call() 中,他最终会负责切换回用户空间,并让用户过程继续执行上来。
  4. 绑定零碎调用

    • 零碎调用表 entry.s 中减少一个表项,0 开始 -end
    • 与体系结构强相干,<asm/unistd.h>
    • 零碎调用必须被编译进内核映像,不能被编译成模块,放在 kernel/ 下一个相干文件就可了,如sys.c
    ENTRY(sys_call_table)
     .long sys_restart_syscall
    ....
    • 没有明确指向编号,默认从 0 -
    • 须要对每个体系结构减少该零碎调用
    • 零碎调用号减少到 <asm/unistd.h> 中,
    #define __NR_restart_systemcall 0
    ...
  5. 从用户空间拜访零碎调用

    • 通过 c 库进行调用,新增的零碎调用可能不反对
    • 能够应用宏对系统调用进行拜访,他会设置好寄存器并调用陷入指令。
    _syscalln()//0-6, 代表传递给零碎调用的参数个数
    long open(const char *filename, int flags, int mode)
    
    #define NR_open 5 //<asm/unistd.h> 中定义,是零碎调用号
    _syscall3(long, open, const char, *filename, int, flags, int, mode)
    • 该宏会被扩大为内嵌汇编的 c 函数。将零碎调用号和参数压入寄存器并触发软中断来陷入内核。把下面的宏放在应用程序中就行。
  6. 不举荐通过零碎调用的形式实现
    零碎调用在 linux 中容易创立和使用方便以及高性能。
    问题是

    • 须要一个零碎调用号,官网调配
    • 退出稳固内核后就被固化,不能做扭转
    • 须要将他别离注册到不同体系结构中,
    • 脚本中不容易应用零碎调用,也不能从文件系统中拜访零碎调用
      代替办法
    • 实现一个设施节点,对此实现 readwrite,应用 ioctl 对特定的设置进行操作或者对特定的信息进行检索。
    • 像信号量这样的某些接口,能够用文件描述符来示意,因而也就能够依照上述办法对他进行操作
    • 把减少的信息作为一个文件放在 sysfs 的适合地位。
正文完
 0