《Linux内核设计与实现》读书笔记(三)--系统调用
来源:互联网 发布:大尺度知乎收藏 编辑:程序博客网 时间:2024/06/06 00:57
系统调用:为保证系统稳定可靠,避免应用程序肆意妄行。用户进程与内核进行交互的一组接口。
1、与内核通信
系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层有三个主要作用:
- 为用户空间提供了一种硬件的抽象接口;
- 保证了系统的稳定和安全;
- 保证每个进程都运行在虚拟系统中。
系统调用是用户空间访问内核的唯一手段;除异常和陷入外,它们是内核唯一的合法入口。
用户程序,系统调用,内核,硬件设备的调用关系如下图:
2、API、POSIX和C库
应用程序通过在用户空间实现的应用编程接口(API)而不是直接通过系统调用来编程。一个API定义了一组应用程序使用的编程接口,可对应多个系统调用。
POSIX是一套应用编程接口(API)的实现标准。
C库实现了linux系统的主要API,包括标准C库函数和系统调用接口。
从程序员角度看,系统调用无关紧要,只需要跟API打交道就可以了,相反,内核只跟系统调用打交道;库函数及应用程序怎么使用系统调用,内核无需关心;但内核必须时刻牢记系统调用所有潜在的用途,并保证它们有良好的通用性和灵活性。
3、系统调用
要访问系统调用(syscall),通常通过C库中定义的函数调用来进行。
asmlinkage:用于函数声明的限定词,一个编译指令,通知编译器仅从栈中提取该函数的参数。所有系统调用都需要这个限定词。
3.1 系统调用号
在Linux中,每个系统调用都被赋予一个系统调用号。通过这个独一无二的号可以准确的关联系统调用。
系统调用号非常重要,一旦分配就不能再有任何变更。系统调用就算被删除,其所占用的系统调用号也不允许被回收利用。
内核记录了系统调用表中的所有已注册过的系统调用的列表,存储在sys_call_table中。该表为每一个有效的系统调用指定了唯一的系统调用号。
3.2 系统调用的性能
Linux系统调用比其他许多操作系统执行的要快。Linux很短的上下文切换是一个重要原因,进出内核都被优化得简洁高效。同时系统调用处理程序和每个系统调用本身都非常简洁。
4、系统调用处理程序
用户空间通知内核的机制:依靠软中断实现:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序system_call()。
4.1 指定恰当的系统调用
在x86上,系统调用号通过eax寄存器传递到内核。在陷入内核之前,用户空间就把相应系统调用所对应的号放入eax寄存器中。
system_call()函数通过将给定的系统调用号与NR_syscalls做比较来检查其有效性。如果它大于或等于NR_syscalls,该函数就返回-ENOSYS。否则,就执行相应的系统调用:
call *sys_call_table(,%rax,8);
4.2 参数传递
除了系统调用号以外,大部分系统调用都还需要一些外部的参数输入。类似系统调用号的做法,也把这些参数存放在寄存器里。在x86-32系统上,ebx、ecx、edx、esi核sdi按照顺序存放前五个参数。
5、系统调用的实现
5.1 实现系统调用
实现一个新的系统调用首先要决定它的用途。每个系统调用都应该有一个明确的用途。
确定新系统调用的参数、返回值和错误码。系统调用的接口应该力求简洁,参数尽可能少。稳定!!!
设计接口时要尽量为将来多做考虑。系统调用设计的越通用越好。不要假设这个系统调用现在怎么用将来也一定就是这么用。永远记住Uinx的格言:提供机制而不是策略!
实现新的系统调用时,要时刻注意可移植性和健壮性,不但要考虑当前,还要为将来做打算。基本的Unix系统调用经受住了时间的考验;他们中的很大一部分现在还和30年前一样适用和有效。
5.2 参数验证
系统调用必须仔细检查它们所有的参数是否合法有效。系统调用在内核控件执行,如果任由用户将不合法的输入传递给内核,那么系统的安全和稳定将面临极大的考验。
最重要的检查就是检查用户提供的指针是否有效。在接受一个用户空间的指针之前,内核必须保证:
- 指针指向的内存区域属于用户空间。
- 指针指向的内存区域在进程的地址空间里。
- 如果是读,该内存应被标记为可读;如果是写,该内存应被标记为可写;如果是可执行,该内存被标记为可执行。
copy_to_user():向用户空间写入数据;
copy_from_user():从用户空间读取数据。
6、系统调用上下文
内核在执行系统调用的时候处于进程上下文。current指针指向当前任务,即引发系统调用的那个进程。
在进程上下文中,内核可以休眠并且可以被抢占。(一)能够休眠说明系统调用可以使用内核提供的绝大部分功能;(二)在进程上下文中能被抢占其实表明,像用户空间内的进程一样,当前的进程同样可以被其他进程抢占。
6.1 绑定一个系统调用的最后步骤
当编写完一个系统调用后,把它注册成一个正式的系统调用是件琐碎的工作:
- 首先,在系统调用表的最后加入一个表项;
- 对于所支持的各种体系结构,系统调用号都必须定义于《asm/unistd.h》中;
- 系统调用必须被便衣进内核映像(不能被编译成模块)。
6.2 从用户空间访问系统调用
系统调用通常靠C库支持。用户程序通过包含标准头文件并和C库链接,就可以使用系统调用。
6.3 系统调用的利弊
建立一个新的系统调用的好处:
- 系统调用创建容易且使用方便;
- Linux系统调用的高性能显而易见。
缺点:
- 需要一个系统调用号,而这需要一个内核在处于开发版本的时候由官方分配给你;
- 系统调用被加入稳定内核后就被固化了,为避免应用程序的崩溃,它的接口不允许做改动;
- 需要将系统调用分别注册到每个需要支持的体系结构中去;
- 在脚本中不容易调用系统调用,也不能从文件系统直接访问系统调用;
- 由于需要系统调用号,因此在主内核树之外是很难维护和使用系统调用的;
- 如果仅仅进行简单的信息交换,系统调用就大材小用了。
替代方案:
实现一个设备节点,并对此实现read()和write()。使用ioctl()对特定的设置进行操作或者对特定的信息进行检索。
- 像信号量这样的某些接口,可以用文件描述符来表示,因此也就可以按上述方式对其进行操作;
- 把增加的信息作为一个文件放在sysfs的合适位置。
- 《Linux内核设计与实现》读书笔记(三)--系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- 《Linux内核设计与实现》读书笔记(五)- 系统调用
- Linux内核设计与实现读书笔记(4)-系统调用
- 系统调用——linux内核设计与实现读书笔记
- Linux内核的设计与实现 读书笔记(5)系统调用
- 《Linux内核设计与实现》读书笔记之系统调用
- linux内核设计与实现(系统调用、中断处理读书笔记)
- 《Linux内核设计与实现》--系统调用
- linux内核设计与实现:系统调用
- linux内核设计与实现(第五章)----系统调用
- 《Linux内核设计与实现》读书笔记(三)- Linux的进程
- 《Linux内核设计与实现》读书笔记(三)- Linux的进程
- python3 批量编码格式转换
- Windows下编译FFmpeg-2.6.1详解
- dbcp,c3p0,druid数据库连接池的浅析
- 数据类型使用错误
- P2440数字消除游戏
- 《Linux内核设计与实现》读书笔记(三)--系统调用
- STM32研发思考
- 初赛问题求解选集
- 责任链模式
- POJ 3280 Cheapest Palindrome(区间DP)
- 语音信号处理中怎么理解分帧?
- 文件的归档与压缩
- 【笔记】抽象数据型及算法复杂性
- 2017 Pycharm激活码