《Linux内核设计与实现》第五章学习笔记编程
姓名:王玮怡 学号:20135116函数
1、与内核通讯学习
在Linux中,系统调用是用户空间访问内核的惟一手段;除异常和陷入外,它们是内核的惟一合法入口。spa
2、API、POSIX、C库设计
一、API3d
通常状况下,应用程序经过在用户空间实现的应用编程接口(API)来编程,而不是直接经过系统调用。指针
二、POSIXblog
在Unix世界中,最流行的应用编程接口是基于POSIX标准的。接口
三、C库进程
C库实现了Unix系统的主要API,包括标准C库函数和系统调用接口。此外,C库提供了POSIX的绝大部分API。
四、POSIX、API、C库以及系统调用间的关系
3、系统调用(syscall)
一、系统调用号
在Linux中,每一个系统调用被赋予一个系统调用号。当用户空间的进程执行一个系统调用时,用系统调用号来指明到底执行哪一个系统调用。
*特色:
(1)一旦分配就不能再有任何改变,而且若是一个系统调用被删除,它所占有的系统调用号也不容许被回收利用,内核将系统调用表中已注册过的系统调用号记录存储在sys_call_table中
(2)“未实现”系统调用sys_ni_syscall(),只能返回-ENOSYS,专门针对无效的系统调用而设
二、Linux系统调用快速的缘由
(1)Linux上下文切换时间短,进出内核简洁高效
(2)系统调用处理程序和每一个系统调用自己十分简洁
4、系统调用处理程序
因为内核驻留在受保护的地址空间上,用户空间的程序没法直接执行内核代码,不能直接调用内核空间中的函数。应用程序通知内核的机制是靠软中断实现的:经过引起一个异常来促使系统切换到内核态去执行异常处理程序(系统调用处理程序)
在x86系统上预约义的软中断是中断号128,经过int $0x80指令触发该中断,切换到内核态执行第128号异常处理程序——system_call
一、指定恰当的系统调用
全部的系统调用陷入内核的方式为:陷入内核空间,并将系统调用号一并传给内核(x86上经过eax寄存器传递)。system_call()函数将系统调用号与NR_syscalls做比较:
(1)系统调用号大于或等于NR_syscalls:return -ENOSYS
(2)系统调用号小于NR_syscalls:执行相应的系统调用 call *sys_call_table(,%rax,8) (x86-64位乘8,x86-32位乘4)
二、参数传递
大部分系统调用除了系统调用号外还须要外部的参数输入,在x86-32系统上,ebx、ecx、edx、esi、edi按顺序存放前五个参数(须要六个及以上的状况很少见)。此外,给用户空间的返回值经过eax寄存器传递。
5、系统调用的实现
一、实现系统调用
(1)决定它的用途;(2)肯定新系统调用的参数、返回值和错误码;(3)设计接口
*注意:可移植性和健壮性
二、参数验证
(1)系统调用必须检查每一个参数,保证它们不只合法有效,并且正确。例如,与文件I/O相关的系统调用必须检查文件描述符是否有效;与进程有关的函数必须检查提供的PID是否有效。最重要的一种检查是检查用户提供的指针是否有效。在接收一个用户空间的指针前,内核必须保证:
(2)内核提供了两个方法来完成必须的检查和内核空间与用户空间之间的数据来回拷贝:
*注意:copy_to_user()和copy_from_user()都有可能引发阻塞
(3)调用者可使用capable()函数检查是否有权能对指定的资源进行操做,若是返回非0值则有权操做,返回0则无权操做
6、调用上下文
内核在执行系统调用时处于进程上下文。current指针指向当前任务,引起系统调用那个进程。在进程上下文中,内核能够休眠(例如在系统调用阻塞或显式调用schedule()时)而且能够被抢占。
系统调用返回的时候,控制权仍然在system_call()中,它最终会负责切换到用户空间,并让用户进程继续执行下去。
一、绑定一个系统调用的最后步骤
注册成正式的系统调用的步骤:
(1)在系统调用表的最后加一个表项(从0开始算起)
(2)系统调用号必须定义于<asm/unistd.h>中
(3)系统调用必须被编译进内核映象(不能被编译成模块)
二、从用户空间访问系统调用
Linux自己提供了一组宏,用于直接对系统调用进行访问:_syscalln(),其中n的范围为0到6,表明须要传递给系统调用的参数个数。对每一个宏来讲,都有2+2*n个参数,第一个参数对应着系统调用的返回值类型,第二个参数是系统调用名称,再之后是按照系统调用参数的顺序排列的每一个参数的类型和名称。
三、采用系统调用做为实现方法的利弊和代替方法
(1)创建一个系统调用的好处:
(2)创建系统调用的弊端:
(3)代替方法: