彩票网站开发合法吗,包头网站网站建设,建立设计网站富阳,专门做鞋子的网站系统调用1 API、POSIX和C库2 系统调用系统调用号3 系统调用处理程序指定恰当的系统调用参数传递4 系统调用的实现参数验证5 系统调用上下文绑定一个系统调用的最后步骤从用户空间访问系统调用为什么不通过系统调用的方式实现1 API、POSIX和C库 
API#xff1a;应用编程接口。一…
系统调用1 API、POSIX和C库2 系统调用系统调用号3 系统调用处理程序指定恰当的系统调用参数传递4 系统调用的实现参数验证5 系统调用上下文绑定一个系统调用的最后步骤从用户空间访问系统调用为什么不通过系统调用的方式实现1 API、POSIX和C库 
API应用编程接口。一个API定义了一组应用程序使用的编程接口。它们可以实现成一个系统调用也可以通过调用多个系统调用来实现而完全不使用任何系统调用也不存在问题。在Unix世界中最流行的应用编程接口是基于POSIX标准的。Linux是与POSIX兼容的。 
Linux的系统调用像大多数Unix一样作为C库的一部分。C库实现了Unix系统的主要API包括标准C库函数和系统调用。 
Unix的系统调用抽象出了完成某种确定目的的函数。至于这些函数怎么用完全不需要内核去关心提供机制需要提供什么功能而不是策略怎样实现这些功能。 
2 系统调用 
系统调用通常通过函数进行调用在Linux中常称作syscalls。系统调用还会通过一个long类型的返回值来表示成功或者失败使用long类型是为了与64位的硬件体系结构保持兼容。 
函数声明中如果用asmlinkage限定词表示通知编译器仅从栈中提取函数的参数所有的系统调用都需要这个限定词。Linux所有的系统调用都应该遵守的命名规则系统调用getpid()在内核中被定义为sys_getpid()要在系统调用加上sys_。 
系统调用号 
Linux中每个系统调用都被赋予一个系统调用号。这样通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候这个系统调用号就被用来指明到底是要执行那个系统调用进程不会提及系统调用的名称。 
系统调用号一旦分配就不能再有任何变更否则编译好的应用程序就会崩溃。此外如果一个系统调用被删除它所占的系统调用号也不允许被回收利用否则以前编译过的代码再调用这个系统调用实际上调用的就是另一个系统调用了。Linux有一个系统调用sys_ni_syscall()它除了返回ENOSYS外不做任何其他的工作这个错误号就是专门针对无效的系统调用而设的。 
内核记录了系统调用表中的所有已被注册过的系统调用的列表存储在sys_call_table中。它与结构体系有关需要将系统调用分别注册到每个需要支持的体系结构去一般定义在entry.s中。2.6.10版本的位置在arch/结构体系/kernel/entry.S。这个表中为每一个有效的系统调用指定了唯一的系统调用号。 m32r体系结构  
3 系统调用处理程序 
用户空间的程序无法直接执行内核代码他们不能直接调用内核空间中的函数所以应用程序应该以某种方式通知系统告诉内核自己需要执行一个系统调用希望系统切换到内核态通知内核的机制是靠软中断实现的通过一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序就是系统调用程序。x86系统上的软中断是int 0x80指令产生的。这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序而该程序正是系统调用处理程序叫system_call()。x86处理器增加了一条叫做sysenter的指令。与int指令相比这条指令提供了更快、更专业的陷入内核执行系统调用的方式。 
指定恰当的系统调用 
因为所有的系统调用陷入内核方式都一样因此必须把系统调用号一并传给内核告诉内核去执行什么系统调用。在x86上系统调用号是通过eax寄存器传递给内核的。在陷入内核之前用户空间就把相应系统调用所对应的号放入eax中。这样系统调用处理程序一旦运行就可以从eax中得到数据。 
system_call()函数通过将给定的系统调用号与NR_syscalls做比较来检查其有效性。如果大于等于NR_syscalls该函数返回ENOSYS。否则就执行相应的系统调用。 
call *sys_call_table(,%eax,4)由于系统调用表中的表项是32位4字节类型存放的所以内核需要将给定的系统调用号乘以4然后用所得的结构在该表中查询其位置。  
参数传递 
除了系统调用号外大部分系统调用都还需要一些外部的参数输入。所以在发送异常的时候应该把这些参数从用户空间传给内核。最简单的办法就是像传递系统调用号一样把这些参数存放到寄存器里。在x86系统上ebx、ecx、edx、edi和esi按照顺序存放前五个参数。需要6个或6个以上参数的应该用一个单独的寄存器存放指向所有这些参数在用户空间地址的指针。 
用户空间返回值也通过寄存器传递在x86系统上它存放在eax寄存器中。 
4 系统调用的实现 
实现一个新的系统调用的第一步是决定它的用途。它要做什么每个系统调用都应该有一个明确的用途。在Linux中不提倡采用多用途的系统调用一个系统调用根据参数来选择完成不同的工作。 
新系统调用的参数、返回值和错误码又该是什么系统调用的接口应该简洁参数尽可能的少。 
设计接口的时候要尽量为将来多做考虑。 
当写一个系统调用的时候要时刻注意可移植性和健壮性不但要考虑当前还要为将来做打算。 
参数验证 
系统调用必须仔细检查它们所有的参数是否合法有效。系统调用在内核空间执行如果任由用户将不合法的输入传递给内核那么系统的安全和稳定将面临巨大的考验。 
内核提供了两个方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。为了向用户空间写入数据内核提供了copy_to_uesr()它需要三个参数。第一个参数是进程空间中的目的内存地址。第二个是内核空间内的源地址。最后一个参数是需要拷贝的数据长度。为了从用户空间读取数据内核提供了copy_from_user()。该函数把第二个参数指定的位置上的数据拷贝到第一个参数指定的位置上拷贝的数据长度由第三个参数决定。 
注意内核无论何时都不能轻率地接受来自用户空间的指针 
5 系统调用上下文 
在执行系统调用的时候处于进程上下文。current指针指向当前任务即引发系统调用的那个进程。 在进程上下文中内核可以休眠并且可以被抢占。当系统调用返回的时候控制权仍然在system_call()中它最终负责切换到用户空间并让用户进程继续执行下去。 
绑定一个系统调用的最后步骤 
当编写完一个系统调用后把它注册成一个正式的系统调用是件琐碎的工作 
首先在系统调用表的最后加入一个表项。每种支持该系统调用的硬件体系都必须做这样的工作。从0开始算起系统调用在该表中的位置就是它的系统调用号。如第10个系统调用分配到的系统调用号为9。对于所支持的各种体系结构系统调用号都必须定义于include/asm/unistd.h中。系统调用必须被编译进内核映像不能编译成模块。这只要把它放进kernel/下的一个相关文件就行了。 
我们通过一个虚构的系统调用foo()来仔细观察这些步骤。首先我们把sys_foo加入到系统调用表中去。对于大多数体系结构中该表位于entry.s中形式如下  我们把新的系统调用加到这个表的末尾 
.long sys_foo虽然没有明确地指定编号但我们加入这个系统调用被次序分配给了285这个系统调用号。对于每种需要支持的体系结构我们都必须将自己的系统调用加入到其系统调用表中去。每种体系结构不需要对应相同的系统调用号。系统调用号是专属体系结构ABI应用程序二进制结构的部分。 
接下来我们把系统调用号加入到include/asm/unistd.h中它的格式如下  然后我们在该列表中加入下面这行 
#define _NR_foo 			285同时#define NR_syscalls 285这个要改成#define NR_syscalls 286。因为系统调用处理程序system_call()函数通过将给定的系统调用号与NR_syscalls做比较来检查其有效性。如果大于等于NR_syscalls该函数返回ENOSYS。否则就执行相应的系统调用。 
最后我们来实现foo系统调用。无论何种配置该系统调用都必须编译到核心的内核映像中去所以我们把它放在kernel/sys.c文件中。再次编译内核就可以了。 
从用户空间访问系统调用 
Linux提供了一组宏用于直接对系统调用进行访问。它会设置好寄存器并调用陷入指令。这些宏是_syscalln()其中n的范围是0到6代表需要传递给系统调用的参数个数。举个例子open系统调用的 定义为 
long open(const char *filename,int flags,int mode);直接调用该系统调用的宏形式为 
_syscall3(long,open,const char *,filename,int flags,int mode)这样程序就可直接使用open。对于每个宏都有22*n个参数第一个参数是返回值类型。第二个参数是系统调用的名称。 
为什么不通过系统调用的方式实现 
建立一个新的系统调用非常容易但却绝不倡导。 
建立一个新的系统调用的好处 
系统调用创建容易且使用方便Linux系统调用的高性能显而易见 
问题是 
你需要一个系统调用号而这需要在一个内核处于开发版本的时候由官方分配给你系统调用被加入稳定内核后就被固化了需要将系统调用分别注册的每个需要的体系结构去在脚本中不容易调用系统调用也不能从文件系统直接访问系统调用