zoukankan      html  css  js  c++  java
  • 《Linux内核设计与实现》第五章读书笔记

    第五章  系统调用

    5.1与内核通信

    1. 系统调用

    让应用程序受限的访问硬件设备
    提供创建新进程并与已有进程通信的机制
    提供申请操作系统其他资源能力
    是用户空间进程和硬件设备之间的中间层

    2. 系统调用的功能

    1)硬件的抽象接口:用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备,简化了用户程序的开发

    2)保证系统的稳定与安全:基于某些规则的访问控制

    3)增强系统的稳定性

    • 在Linux中,系统调用是用户空间访问内核的唯一手段
    • 除异常和陷入外,他们是内核唯一的合法入口

    5.2 API、POSIX和C库

    1.API可以在各种不同的操作系统实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。

    2.在Unix世界中,最流行的应用编程接口是基于POSIX标准的。

    3. C库包括:标准C库函数、系统调用接口

    4. 注意:

    程序员<--->API
    内核<--->系统调用

    5.关系图:

    5.3系统调用

    1. 介绍:在linux中常称作syscall,通常通过C库中定义的函数调用来进行。

    • 注意:返回值是long型变量,如果出错,C库会将错误代码写入errno全局变量。
    • 系统调用再出现错误的时候C库会把错误码写入errno全局变量。通过调用perror()库函数,可以把该变量翻译成用户可以理解的错误字符串。
    • SYSCALL_DEFINE0只是一个宏,它定义一个无参数的系统调用。

    2. 定义系统调用:

    asmlinkage long sys_getpid(void)
    • 限定词:asmlinkage
    • 函数返回值类型:long
    • 符合命名规则的命名:sys_getpid

    3. 系统调用在用户空间和内核空间有不同的返回值类型,在用户空间为int在内核空间为long。

    4. 系统调用号一旦分配就不能再有任何变更。

    • 如果一个系统调用被删除,它所占用的系统调用号也不允许被回收利用
    • Linux有一个“未实现”系统调用sysnisyscall(),它除了返回―ENOSYS外不做任何其他工作,这个错误号就是专门针对无效的系统调用而设的

    5.4 系统调用处理程序

    1. 内核记录了系统调用表中的所有已注册过的系统调用的列表,存储在syscalltable中。在×86-64中,它定义于arch/i386/kernel/syscall_64.c文件中。

    2.注意:

    x86系统,ebx,ecx,edx,esi,edi按顺序存放前五个参数。
    需要6个及以上参数,应用一个单独的寄存器存放指向这些参数在用户空间地址的指针。
    返回值存放在eax。
    

    3. Linux系统执行快的原因:

    • 很短的上下文切换时间
    • 系统调用处理程序和每个系统调用本身也十分简洁

    5.5 系统调用的实现

    1.参数验证

    • 参数合法有效并正确:不应让内核访问无权访问的资源
    • 最重要的检查:用户提供的指针是否有效。内核必须保证指针:

      指向的内存区域属于用户空间;
      指向的内存区在进程的地址空间里;
      指向的内存区在内存的访问权限范围中。
    • 两个方法检查在两空间之间数据的来回拷贝:
      copy_to_user():向用户空间写入数据
      copy_from_user():从用户空间读取数据
    • 针对是否有合法权限的检查
      capable():是否有权对指定的资源进行操作
      返回0:无权操作

    2.关系图:

    5.6 系统调用上下文

    1. 进程上下文:内核在执行系统调用的时候处于进程上下文

    current指针指向当前任务。
    在进程上下文中,内核可以休眠、被抢占。
    当系统调用返回时,控制权仍然在system_call()中,负责切换到用户空间,并让用户进程继续执行下去

    2. 绑定一个系统调用的最后步骤:编写完系统调用之后,将其注册成一个正式的系统调用

    在系统调用表中加入表项;
    系统调用号定义于<asm/unistd.h>中;
    编译进内核映像,放入kernel/下的相关文件。

    3. 从用户空间访问系统调用

    1. _syscalln():Linux提供的一组宏,用于直接对系统调用进行访问。会设置好寄存器并调用陷入指令。
    2. n的范围:0~6,代表传递给系统调用的参数个数。
    3. 对每个宏来说,都有2+2*n个参数。
    第一个参数:对应系统调用返回值类型
    第二个参数:系统调用的名称
    按系统调用参数顺序排列的每个参数的类型和名称 
    eg.
    long open(const char *filename, int flags, int mode)
    #define NR_open 5
    _syscall3(long, open, const char*, filename, int, flags, int, mode)

    4. 为什么不通过系统调用的方式实现?

    (1)建立一个系统调用的好处

    创建容易、使用方便
    Linux系统调用的高性能

    (2)问题是:

    系统调用号需要在内核处于开发版本时官方分配
    系统调用加入稳定内核后被固化,接口不允许做改动
    需要将系统调用分别注册到每个需要支持的体系结构中去
    脚本中不容易调用,不能从文件系统直接访问
    主内核树之外难以维护和使用
    

    (3)替代方法

    某些接口可以用文件描述符表示
    把增加的信息作为文件放在sysfs的合适位置
    

    =======================================================

    总结

    我觉得在这一章中,最应该重点理解的还是系统调用的内涵,以及它和API的区别。也提及补充了系统调用的优势之处。这也让我们从底层能够比较系统地去理解内核运行机制,它是如何提供基本功能的,其中包含的那些层层包装递进的过程。

  • 相关阅读:
    词法分析程序
    0909关于编译原理
    深度学习中图像检测的评价标准
    【 记忆网络 1 】 Memory Network
    ssm又乱码
    百度地图标注没了
    Fragment与Activity交互(使用Handler)
    在android里用ExpandableListView实现二层和三层列表
    java中outer的使用
    android中使用Http下载文件并保存到本地SD卡
  • 原文地址:https://www.cnblogs.com/paperfish/p/5302243.html
Copyright © 2011-2022 走看看