当前位置: 首页 > news >正文

深圳 服装 网站建设吕梁建站公司

深圳 服装 网站建设,吕梁建站公司,怎样免费建设网站,老房改造 装修公司参考#xff1a; 《Linux内核设计与实现》 0 摘要 linux的系统调用过程#xff1a; 层次如下#xff1a; 用户程序------C库#xff08;即API#xff09;#xff1a;INT 0x80 -----system_call-------系统调用服务例程--------内核程序 先说明一下#… 参考 《Linux内核设计与实现》 0 摘要 linux的系统调用过程 层次如下 用户程序------C库即APIINT 0x80 -----system_call-------系统调用服务例程--------内核程序 先说明一下我们常说的用户API其实就是系统提供的C库。 系统调用是通过软中断指令 INT 0x80 实现的而这条INT 0x80指令就被封装在C库的函数中。 软中断和我们常说的硬中断不同之处在于软中断是由指令触发的而不是由硬件外设引起的。 INT 0x80 这条指令的执行会让系统跳转到一个预设的内核空间地址它指向系统调用处理程序即system_call函数。 注意系统调用处理程序system_call 并不是系统调用服务例程系统调用服务例程是对一个具体的系统调用的内核实现函数而系统调用处理程序是在执行系统调用服务例程之前的一个引导过程是针对INT 0x80这条指令面向所有的系统调用的。简单来讲执行任何系统调用都是先通过调用C库中的函数这个函数里面就会有软中断 INT 0x80 语句然后转到执行系统调用处理程序 system_call system_call 再根据具体的系统调用号转到执行具体的系统调用服务例程。 system_call函数是怎么找到具体的系统调用服务例程的呢通过系统调用号查找系统调用表sys_call_table软中断指令INT 0x80执行时系统调用号会被放入 eax 寄存器中system_call函数可以读取eax寄存器获取然后将其乘以4生成偏移地址然后以sys_call_table为基址基址加上偏移地址就可以得到具体的系统调用服务例程的地址了 然后就到了系统调用服务例程了。需要说明的是系统调用服务例程只会从堆栈里获取参数所以在system_call执行前会先将参数存放在寄存器中system_call执行时会首先将这些寄存器压入堆栈。system_call退出后用户可以从寄存器中获得被修改过的参数。   另外系统调用通过软中断INT 0x80陷入内核跳转到系统调用处理程序system_call函数然后执行相应的服务例程。但是由于是代表用户进程所以这个执行过程并不属于中断上下文而是进程上下文。因此系统调用执行过程中可以访问用户进程的许多信息可以被其他进程抢占可以休眠。 当系统调用完成后把控制权交回到发起调用的用户进程前内核会有一次调度。如果发现有优先级更高的进程或当前进程的时间片用完那么会选择优先级更高的进程或重新选择进程执行。 1       系统调用意义 linux内核中设置了一组用于实现系统功能的子程序称为系统调用。系统调用和普通库函数调用非常相似只是系统调用由操作系统核心提供运行于核心态而普通的函数调用由函数库或用户自己提供运行于用户态。   一般的进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些这就是为什么它被称作保护模式。为了和用户空间上运行的进程进行交互内核提供了一组接口。透过该接口应用程序可以访问硬件设备和其他操作系统资源。这组接口在应用程序和内核之间扮演了使者的角色应用程序发送各种请求而内核负责满足这些请求(或者让应用程序暂时搁置)。实际上提供这组接口主要是为了保证系统稳定可靠避免应用程序肆意妄行惹出大麻烦。   系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层主要作用有三个 1 它为用户空间提供了一种统一的硬件的抽象接口。比如当需要读些文件的时候应用程序就可以不去管磁盘类型和介质甚至不用去管文件所在的文件系统到底是哪种类型。 2系统调用保证了系统的稳定和安全。作为硬件设备和应用程序之间的中间人内核可以基于权限和其他一些规则对需要进行的访问进行裁决。举例来说这样可以避免应用程序不正确地使用硬件设备窃取其他进程的资源或做出其他什么危害系统的事情。 3 每个进程都运行在虚拟系统中而在用户空间和系统的其余部分提供这样一层公共接口也是出于这种考虑。如果应用程序可以随意访问硬件而内核又对此一无所知的话几乎就没法实现多任务和虚拟内存当然也不可能实现良好的稳定性和安全性。在Linux中系统调用是用户空间访问内核的惟一手段除异常和中断外它们是内核惟一的合法入口。   2       API/POSIX/C库的关系 一般情况下应用程序通过应用编程接口(API)而不是直接通过系统调用来编程。这点很重要因为应用程序使用的这种编程接口实际上并不需要和内核提供的系统调用一一对应。一个API定义了一组应用程序使用的编程接口。它们可以实现成一个系统调用也可以通过调用多个系统调用来实现而完全不使用任何系统调用也不存在问题。实际上API可以在各种不同的操作系统上实现给应用程序提供完全相同的接口而它们本身在这些系统上的实现却可能迥异。   在Unix世界中最流行的应用编程接口是基于POSIX标准的其目标是提供一套大体上基于Unix的可移植操作系统标准。POSIX是说明API和系统调用之间关系的一个极好例子。在大多数Unix系统上根据POSIX而定义的API函数和系统调用之间有着直接关系。   Linux的系统调用像大多数Unix系统一样作为C库的一部分提供如下图所示。C库实现了 Unix系统的主要API包括标准C库函数和系统调用。所有的C程序都可以使用C库而由于C语言本身的特点其他语言也可以很方便地把它们封装起来使用。  从程序员的角度看系统调用无关紧要他们只需要跟API打交道就可以了。相反内核只跟系统调用打交道库函数及应用程序是怎么使用系统调用不是内核所关心的。   关于Unix的界面设计有一句通用的格言“提供机制而不是策略”。换句话说Unix的系统调用抽象出了用于完成某种确定目的的函数。至干这些函数怎么用完全不需要内核去关心。区别对待机制(mechanism)和策略(policy)是Unix设计中的一大亮点。大部分的编程问题都可以被切割成两个部分:“需要提供什么功能”(机制)和“怎样实现这些功能”(策略)。  3       系统调用的实现 3.1    系统调用处理程序 您或许疑惑 “当我输入 cat /proc/cpuinfo 时cpuinfo() 函数是如何被调用的”内核完成引导后控制流就从相对直观的“接下来调用哪个函数”改变为取决于系统调用、异常和中断。   用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间中的函数因为内核驻留在受保护的地址空间上。如果进程可以直接在内核的地址空间上读写的话系统安全就会失去控制。所以应用程序应该以某种方式通知系统告诉内核自己需要执行一个系统调用希望系统切换到内核态这样内核就可以代表应用程序来执行该系统调用了。   通知内核的机制是靠软件中断实现的。首先用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常产生一个事件这个事件会致使处理器切换到内核态并跳转到一个新的地址并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。   新地址的指令会保存程序的状态计算出应该调用哪个系统调用调用内核中实现那个系统调用的函数恢复用户程序状态然后将控制权返还给用户程序。系统调用是设备驱动程序中定义的函数最终被调用的一种方式。  3.2    系统调用号 在Linux中每个系统调用被赋予一个系统调用号。这样通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候这个系统调用号就被用来指明到底是要执行哪个系统调用。进程不会提及系统调用的名称。   系统调用号相当关键一旦分配就不能再有任何变更否则编译好的应用程序就会崩溃。Linux有一个“未实现”系统调用sys_ni_syscall()它除了返回一ENOSYS外不做任何其他工作这个错误号就是专门针对无效的系统调用而设的。   因为所有的系统调用陷入内核的方式都一样所以仅仅是陷入内核空间是不够的。因此必须把系统调用号一并传给内核。在x86上系统调用号是通过eax寄存器传递给内核的。在陷人内核之前用户空间就把相应系统调用所对应的号放入eax中了。这样系统调用处理程序一旦运行就可以从eax中得到数据。其他体系结构上的实现也都类似。   内核记录了系统调用表中的所有已注册过的系统调用的列表存储在sys_call_table中。它与体系结构有关一般在entry.s中定义。这个表中为每一个有效的系统调用指定了惟一的系统调用号。sys_call_table是一张由指向实现各种系统调用的内核函数的函数指针组成的表 ENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old setup() system call*/ .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork) .long SYMBOL_NAME(sys_read) .long SYMBOL_NAME(sys_write) .long SYMBOL_NAME(sys_open) /* 5 */ .long SYMBOL_NAME(sys_close) .long SYMBOL_NAME(sys_waitpid) 。。。。。 .long SYMBOL_NAME(sys_capget) .long SYMBOL_NAME(sys_capset)      /* 185 */ .long SYMBOL_NAME(sys_sigaltstack) .long SYMBOL_NAME(sys_sendfile) .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ .long SYMBOL_NAME(sys_vfork)      /* 190 */   system_call()函数通过将给定的系统调用号与NR_syscalls做比较来检查其有效性。如果它大于或者等于NR syscalls,该函数就返回一ENOSYS。否则就执行相应的系统调用。       call *sys_ call-table(%eax, 4) 由于系统调用表中的表项是以32位(4字节)类型存放的所以内核需要将给定的系统调用号乘以4然后用所得的结果在该表中查询其位置   3.3    参数传递 除了系统调用号以外大部分系统调用都还需要一些外部的参数输人。所以在发生异常的时候应该把这些参数从用户空间传给内核。最简单的办法就是像传递系统调用号一样把这些参数也存放在寄存器里。在x86系统上ebx, ecx, edx, esi和edi按照顺序存放前五个参数。需要六个或六个以上参数的情况不多见此时应该用一个单独的寄存器存放指向所有这些参数在用户空间地址的指针。   给用户空间的返回值也通过寄存器传递。在x86系统上它存放在eax寄存器中。接下来许多关于系统调用处理程序的描述都是针对x86版本的。但不用担心所有体系结构的实现都很类似。   3.4    参数验证 系统调用必须仔细检查它们所有的参数是否合法有效。举例来说与文件I/O相关的系统调用必须检查文件描述符是否有效。与进程相关的函数必须检查提供的PID是否有效。必须检查每个参数保证它们不但合法有效而且正确。   最重要的一种检查就是检查用户提供的指针是否有效。试想如果一个进程可以给内核传递指针而又无须被检查那么它就可以给出一个它根本就没有访问权限的指针哄骗内核去为它拷贝本不允许它访问的数据如原本属于其他进程的数据。在接收一个用户空间的指针之前内核必须保证 ²      指针指向的内存区域属于用户空间。进程决不能哄骗内核去读内核空间的数据。 ²      指针指向的内存区域在进程的地址空间里。进程决不能哄骗内核去读其他进程的数据。 ²      如果是读该内存应被标记为可读。如果是写该内存应被标记为可写。进程决不能绕过内存访问限制。   内核提供了两个方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。注意内核无论何时都不能轻率地接受来自用户空间的指针!这两个方法中必须有一个被调用。为了向用户空间写入数据内核提供了copy_to_user()它需要三个参数。第一个参数是进程空间中的目的内存地址。第二个是内核空间内的源地址。最后一个参数是需要拷贝的数据长度(字节数)。   为了从用户空间读取数据内核提供了copy_from_ user()它和copy-to-User()相似。该函数把第二个参数指定的位置上的数据拷贝到第一个参数指定的位置上拷贝的数据长度由第三个参数决定。   如果执行失败这两个函数返回的都是没能完成拷贝的数据的字节数。如果成功返回0。当出现上述错误时系统调用返回标准-EFAULT。   注意copy_to_user()和copy_from_user()都有可能引起阻塞。当包含用户数据的页被换出到硬盘上而不是在物理内存上的时候这种情况就会发生。此时进程就会休眠直到缺页处理程序将该页从硬盘重新换回物理内存。   3.5    系统调用的返回值 系统调用(在Linux中常称作syscalls)通常通过函数进行调用。它们通常都需要定义一个或几个参数(输入)而且可能产生一些副作用例如写某个文件或向给定的指针拷贝数据等等。为防止和正常的返回值混淆系统调用并不直接返回错误码而是将错误码放入一个名为errno的全局变量中。通常用一个负的返回值来表明错误。返回一个0值通常表明成功。如果一个系统调用失败你可以读出errno的值来确定问题所在。通过调用perror()库函数可以把该变量翻译成用户可以理解的错误字符串。   errno不同数值所代表的错误消息定义在errno.h中你也可以通过命令man 3 errno来察看它们。需要注意的是errno的值只在函数发生错误时设置如果函数不发生错误errno的值就无定义并不会被置为0。另外在处理errno前最好先把它的值存入另一个变量因为在错误处理过程中即使像printf()这样的函数出错时也会改变errno的值。   当然系统调用最终具有一种明确的操作。举例来说如getpid()系统调用根据定义它会返回当前进程的PID。内核中它的实现非常简单: asmlinkage long sys_ getpid(void) {     return current- tgid; }   上述的系统调用尽管非常简单但我们还是可以从中发现两个特别之处。首先注意函数声明中的asmlinkage限定词这是一个小戏法用于通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词。其次注意系统调用get_pid()在内核中被定义成sys_ getpid。这是Linux中所有系统调用都应该遵守的命名规则   4       添加新系统调用 给Linux添加一个新的系统调用是件相对容易的工作。怎样设计和实现一个系统调用是难题所在而把它加到内核里却无须太多周折。让我们关注一下实现一个新的Linux系统调用所需的步骤。   实现一个新的系统调用的第一步是决定它的用途。它要做些什么每个系统调用都应该有一个明确的用途。在Linux中不提倡采用多用途的系统调用(一个系统调用通过传递不同的参数值来选择完成不同的工作)。ioctl()就应该被视为一个反例。   新系统调用的参数、返回值和错误码又该是什么呢系统调用的接口应该力求简洁参数尽可能少。设计接口的时候要尽量为将来多做考虑。你是不是对函数做了不必要的限制?系统调用设计得越通用越好。不要假设这个系统调用现在怎么用将来也一定就是这么用。系统调用的目的可能不变但它的用法却可能改变。这个系统调用可移植吗?别对机器的字节长度和字节序做假设。当你写一个系统调用的时候要时刻注意可移植性和健壮性不但要考虑当前还要为将来做打算。   当编写完一个系统调用后把它注册成一个正式的系统调用是件琐碎的工作 在系统调用表的最后加入一个表项。每种支持该系统调用的硬件体系都必须做这样的工作。从0开始算起系统调用在该表中的位置就是它的系统调用号。 对于所支持的各种体系结构系统调用号都必须定义于asm/unistd.h中。 系统调用必须被编译进内核映象(不能被编译成模块)。这只要把它放进kernel/下的一个相关文件中就可以。   让我们通过一个虚构的系统调用f00()来仔细观察一下这些步骤。首先我们要把sys_foo加入到系统调用表中去。对于大多数体系结构来说该表位干entry.s文件中形式如下: ENTRY(sys_ call_ table)       ·long sys_ restart_ syscall/*0*/       .long sys_ exit       ·long sys_ fork       ·long sys_ read       .long sys_write 我们把新的系统调用加到这个表的末尾:      .long sys_foo 虽然没有明确地指定编号但我们加入的这个系统调用被按照次序分配给了283这个系统调用号。对于每种需要支持的体系结构我们都必须将自己的系统调用加人到其系统调用表中去。每种体系结构不需要对应相同的系统调用号。   接下来我们把系统调用号加入到asm/unistd.h中它的格式如下: /*本文件包含系统调用号*/ #define_ NR_ restart_ syscall #define NR exit #define NR fork #define NR read #define NR write #define NR- mq getsetattr 282 然后我们在该列表中加入下面这行: #define_ NR_ foo 283   最后我们来实现f00()系统调用。无论何种配置该系统调用都必须编译到核心的内核映象中去所以我们把它放进kernel/sys.c文件中。你也可以将其放到与其功能联系最紧密的代码中去   asmlinkage long sys-foo(void) { return THREAD SIZE ) 就是这样!严格说来现在就可以在用户空间调用f00()系统调用了。   建立一个新的系统调用非常容易但却绝不提倡这么做。通常模块可以更好的代替新建一个系统调用。   5       访问系统调用 5.1    系统调用上下文 内核在执行系统调用的时候处于进程上下文。current指针指向当前任务即引发系统调用的那个进程。   在进程上下文中内核可以休眠并且可以被抢占。这两点都很重要。首先能够休眠说明系统调用可以使用内核提供的绝大部分功能。休眠的能力会给内核编程带来极大便利。在进程上下文中能够被抢占其实表明像用户空间内的进程一样当前的进程同样可以被其他进程抢占。因为新的进程可以使用相同的系统调用所以必须小心保证该系统调用是可重人的。当然这也是在对称多处理中必须同样关心的问题。   当系统调用返回的时候控制权仍然在system_call()中它最终会负责切换到用户空间并让用户进程继续执行下去。   5.2    系统调用访问示例 操作系统使用系统调用表将系统调用编号翻译为特定的系统调用。系统调用表包含有实现每个系统调用的函数的地址。例如read() 系统调用函数名为 sys_read。read() 系统调用编号是 3所以 sys_read() 位于系统调用表的第四个条目中因为系统调用起始编号为0。从地址 sys_call_table (3 * word_size) 读取数据得到 sys_read() 的地址。   找到正确的系统调用地址后它将控制权转交给那个系统调用。我们来看定义 sys_read() 的位置即 fs/read_write.c 文件。这个函数会找到关联到 fd 编号传递给 read() 函数的的文件结构体。那个结构体包含指向用来读取特定类型文件数据的函数的指针。进行一些检查后它调用与文件相关的 read() 函数来真正从文件中读取数据并返回。与文件相关的函数是在其他地方定义的 —— 比如套接字代码、文件系统代码或者设备驱动程序代码。这是特定内核子系统最终与内核其他部分协作的一个方面。   读取函数结束后从 sys_read() 返回它将控制权切换给 ret_from_sys。它会去检查那些在切换回用户空间之前需要完成的任务。如果没有需要做的事情那么就恢复用户进程的状态并将控制权交还给用户程序。 5.3    从用户空间直接访问系统调用 通常系统调用靠C库支持。用户程序通过包含标准头文件并和C库链接就可以使用系统调用(或者调用库函数再由库函数实际调用)。但如果你仅仅写出系统调用glibc库恐怕并不提供支持。值得庆幸的是Linux本身提供了一组宏用于直接对系统调用进行访问。它会设置好寄存器并调用陷人指令。这些宏是_syscalln()其中n的范围从0到6。代表需要传递给系统调用的参数个数这是由于该宏必须了解到底有多少参数按照什么次序压入寄存器。举个例子open()系统调用的定义是: long open(const char *filename, int flags, int mode) 而不靠库支持直接调用此系统调用的宏的形式为: #define NR_ open 5 syscall3(long, open, const char*filename, int, flags, int, mode) 这样应用程序就可以直接使用open()   对于每个宏来说都有2 n个参数。第一个参数对应着系统调用的返回值类型。第二个参数是系统调用的名称。再以后是按照系统调用参数的顺序排列的每个参数的类型和名称。_NR_ open在asm/unistd.h中定义是系统调用号。该宏会被扩展成为内嵌汇编的C函数。由汇编语言执行前一节所讨论的步骤将系统调用号和参数压入寄存器并触发软中断来陷入内核。调用open()系统调用直接把上面的宏放置在应用程序中就可以了。   让我们写一个宏来使用前面编写的foo()系统调用然后再写出测试代码炫耀一下我们所做的努力。 #define NR foo 283 _sysca110(long, foo) int main() { long stack size; stack_ sizefoo(); printf(The kernel stack size is 81d\nstack_ size); return; } 6 实际使用的注意 1系统调用是需要提前编译固化到内核中的而且需要官方分配一个系统调用号 2需要将系统调用注册到支持的每一种体系结构中 3系统调用一般不能在脚本中直接访问 4尽量避免新建系统调用可用创建设备结点的方法代替。
http://www.pierceye.com/news/756464/

相关文章:

  • 新网 网站备案好的作文网站
  • 网站建设技术外包深圳建设公司网站
  • 做旅游网站的数据怎么来垦利网站设计
  • 附近那里有做网站的微信公众平台注册官网
  • 雏鸟短视频app软件下载网站网站建设心得体会500字
  • 权威发布型舆情回应大连网站优化多少钱
  • 怎么做网站步骤免费的怎么用虚拟主机做网站步骤
  • 网站建设精品课程南昌企业网站建设哪家好
  • 网站空间不够用怎么办电子商务公司名字
  • 策划方案网站wordpress设置视频图片
  • 餐饮设计网站有哪些做副业的网站
  • 如何建设一个电子商务网站四川网站建设电话
  • 网站制作学习学网站开发顺序
  • 外语网站建设怎么知道网站的ftp
  • 苏州专业做网站的公司有哪些网络机柜定制
  • 提供服务的网站免费的进销存软件哪个简单好用
  • 长沙县政务网站网络公司名字大全寓意
  • 网站后台凡科建设有做网站维护的
  • 搭建网站需要什么软件上海在线
  • led灯外贸网站建设网站代码怎么优化
  • 网站建设维护什么意思江苏网络推广专员
  • 潍坊网站开发asp培训珠海市网站建设公司
  • 用什么做响应式网站建行个人余额查询网站
  • 做网站网站代理怎么找客源企业团建公司
  • 电子商务网站开发实战济南兼职做网站
  • 怎样创建网站视频学历提升的重要性
  • 百度搜索引擎录入网站1_ 掌握网站开发的基本流程 要求:熟悉网站开发与设计的基本流程.
  • 广州做网站建设如何在别人网站挂黑链
  • 宁德北京网站建设任丘建设银行网站
  • 积极加强网站建设连锁会员管理系统