乐趣区

关于linux:Linux系统调用详解实现机制分析

零碎调用概述


计算机系统的各种硬件资源是无限的,在古代多任务操作系统上同时运行的多个过程都须要拜访这些资源,为了更好的治理这些资源过程是不容许间接操作的,所有对这些资源的拜访都必须有操作系统管制。也就是说操作系统是应用这些资源的惟一入口,而这个入口就是操作系统提供的 零碎调用(System Call)。在 linux 中零碎调用是用户空间拜访内核的惟一伎俩,除异样和陷入外,他们是内核惟一的非法入口。

个别状况下应用程序通过利用编程接口 API,而不是间接通过零碎调用来编程。在 Unix 世界,最风行的 API 是基于 POSIX 规范的。

操作系统个别是通过中断从用户态切换到内核态。中断就是一个硬件或软件申请,要求 CPU 暂停以后的工作,去解决更重要的事件。比方,在 x86 机器上能够通过 int 指令进行软件中断,而在磁盘实现读写操作后会向 CPU 发动硬件中断。

中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具备不同的中断处理程序。在操作系统内核中保护着一个中断向量表(Interrupt Vector Table),这个数组存储了所有中断处理程序的地址,而中断号就是相应中断在中断向量表中的偏移量。

个别地,零碎调用都是通过软件中断实现的,x86 零碎上的软件中断由 int $0x80 指令产生,而 128 号异样处理程序就是零碎调用处理程序 system_call(),它与硬件体系无关,在 entry.S 中用汇编写。接下来就来看一下 Linux 下零碎调用具体的实现过程。

为什么须要零碎调用


linux 内核中设置了一组用于实现零碎性能的子程序,称为零碎调用。零碎调用和一般库函数调用十分类似,只是零碎调用由操作系统外围提供,运行于 内核态 ,而一般的函数调用由函数库或用户本人提供,运行于 用户态

个别的,过程是不能拜访内核的。它不能拜访内核所占内存空间也不能调用内核函数。CPU 硬件决定了这些(这就是为什么它被称作“保护模式”)。

为了和用户空间上运行的过程进行交互,内核提供了一组接口。透过该接口,应用程序能够拜访硬件设施和其余操作系统资源。这组接口在应用程序和内核之间表演了使者的角色,应用程序发送各种申请,而内核负责满足这些申请(或者让应用程序临时搁置)。实际上提供这组接口次要是为了保证系统稳固牢靠,防止应用程序肆意妄行,惹出大麻烦。

零碎调用在用户空间过程和硬件设施之间增加了一个中间层。该层次要作用有三个:

1、它为用户空间提供了一种对立的硬件的形象接口。比方当须要读些文件的时候,应用程序就能够不去管磁盘类型和介质,甚至不必去管文件所在的文件系统到底是哪种类型。

2、零碎调用保障了零碎的稳固和平安。作为硬件设施和应用程序之间的中间人,内核能够基于权限和其余一些规定对须要进行的拜访进行裁决。举例来说,这样能够防止应用程序不正确地应用硬件设施,窃取其余过程的资源,或做出其余什么危害零碎的事件。

3、每个过程都运行在虚构零碎中,而在用户空间和零碎的其余部分提供这样一层公共接口,也是出于这种思考。如果应用程序能够随便拜访硬件而内核又对此无所不知的话,简直就没法实现多任务和虚拟内存,当然也不可能实现良好的稳定性和安全性。在 Linux 中,零碎调用是用户空间拜访内核的惟一伎俩;除异样和中断外,它们是内核惟一的非法入口。

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 设计中的一大亮点。大部分的编程问题都能够被切割成两个局部:“须要提供什么性能”(机制)和“怎么实现这些性能”(策略)。

区别


api 是函数的定义,规定了这个函数的性能,跟内核无间接关系。而零碎调用是通过中断向内核发申请,实现内核提供的某些服务。

分割


一个 api 可能会须要一个或多个零碎调用来实现特定性能。艰深点说就是如果这个 api 须要跟内核打交道就须要零碎调用,否则不须要。
程序员调用的是 API(API 函数),而后通过与零碎调用共同完成函数的性能。
因而,API 是一个提供给应用程序的接口,一组函数,是与程序员进行间接交互的。
零碎调用则不与程序员进行交互的,它依据 API 函数,通过一个软中断机制向内核提交申请,以获取内核服务的接口。
并不是所有的 API 函数都一一对应一个零碎调用,有时,一个 API 函数会须要几个零碎调用来共同完成函数的性能,甚至还有一些 API 函数不须要调用相应的零碎调用(因而它所实现的不是内核提供的服务)

须要 C /C++ Linux 高级服务器架构师学习材料后盾加群 812855908(包含 C /C++,Linux,golang 技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等)

零碎调用的实现原理


根本机制


前文曾经提到了 Linux 下的零碎调用是通过 0x80 实现的,然而咱们晓得操作系统会有多个零碎调用(Linux 下有 319 个零碎调用),而对于同一个中断号是如何解决多个不同的零碎调用的?最简略的形式是对于不同的零碎调用采纳不同的中断号,然而中断号显著是一种稀缺资源,Linux 显然不会这么做;还有一个问题就是零碎调用是须要提供参数,并且具备返回值的,这些参数又是怎么传递的?也就是说,对于零碎调用咱们要搞清楚两点:

  1. 1. 零碎调用的函数名称转换。
  2. 2. 零碎调用的参数传递。

首先看第一个问题。实际上,Linux 中每个零碎调用都有相应的零碎调用号作为惟一的标识,内核保护一张零碎调用表,sys_call_table,表中的元素是零碎调用函数的起始地址,而零碎调用号就是零碎调用在调用表的偏移量。在 x86 上,零碎调用号是通过 eax 寄存器传递给内核的。比方 fork()的实现:

用户空间的程序无奈间接执行内核代码。它们不能间接调用内核空间中的函数,因为内核驻留在受爱护的地址空间上。如果过程能够间接在内核的地址空间上读写的话,系统安全就会失去管制。所以,应用程序应该以某种形式告诉零碎,通知内核本人须要执行一个零碎调用,心愿零碎切换到内核态,这样内核就能够代表应用程序来执行该零碎调用了。

告诉内核的机制是靠软件中断实现的。首先,用户程序为零碎调用设置参数。其中一个参数是零碎调用编号。参数设置实现后,程序执行“零碎调用”指令。x86 零碎上的软中断由 int 产生。这个指令会导致一个异样:产生一个事件,这个事件会以致处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异样处理程序。此时的异样处理程序实际上就是零碎调用处理程序。它与硬件体系结构严密相干。

新地址的指令会保留程序的状态,计算出应该调用哪个零碎调用,调用内核中实现那个零碎调用的函数,复原用户程序状态,而后将控制权返还给用户程序。零碎调用是设施驱动程序中定义的函数最终被调用的一种形式。

从系统分析的角度,linux 的零碎调用波及 4 个方面的问题。

响应函数 sys_xxx


响应函数名以“sys_”结尾,后跟该零碎调用的名字。

例如

零碎调用 fork()的响应函数是 sys_fork()(见 Kernel/fork.c),

exit()的响应函数是 sys_exit()(见 kernel/fork.)。

零碎调用表与零碎调用号 -=> 数组与下标


文件 include/asm/unisted.h 为每个零碎调用规定了惟一的编号。

在咱们零碎中 /usr/include/asm/unistd_32.h,能够通过 find / -name unistd_32.h -print 查找)
而内核中的头文件门路不同的内核版本以及不同的发行版,文件的存储构造可能有所区别

假如用 name 示意零碎调用的名称,那么零碎调用号与零碎调用响应函数的关系是:以零碎调用号_NR_name 作为下标,可找出零碎调用表 sys_call_table(见 arch/i386/kernel/entry.S)中对应表项的内容,它正好是该零碎调用的响应函数 sys_name 的入口地址。

零碎调用表 sys_call_table 记录了各 sys_name 函数在表中的地位,共 190 项。有了这张表,就很容易依据特定零碎调用

在表中的偏移量,找到对应的零碎调用响应函数的入口地址。零碎调用表共 256 项,余下的项是可供用户本人增加的零碎调用空间。

在 Linux 中,每个零碎调用被赋予一个零碎调用号。这样,通过这个举世无双的号就能够关联系统调用。当用户空间的过程执行一个零碎调用的时候,这个零碎调用号就被用来指明到底是要执行哪个零碎调用。过程不会提及零碎调用的名称。

零碎调用号相当要害,一旦调配就不能再有任何变更,否则编译好的应用程序就会解体。Linux 有一个“未实现”零碎调用 sys_ni_syscall(),它除了返回一 ENOSYS 外不做任何其余工作,这个谬误号就是专门针对有效的零碎调用而设的。

因为所有的零碎调用陷入内核的形式都一样,所以仅仅是陷入内核空间是不够的。因而必须把零碎调用号一并传给内核。在 x86 上,零碎调用号是通过 eax 寄存器传递给内核的。在陷人内核之前,用户空间就把相应零碎调用所对应的号放入 eax 中了。这样零碎调用处理程序一旦运行,就能够从 eax 中失去数据。其余体系结构上的实现也都相似。

内核记录了零碎调用表中的所有已注册过的零碎调用的列表,存储在 sys_call_table 中。它与体系结构无关,个别在 entry.s 中定义。这个表中为每一个无效的零碎调用指定了惟一的零碎调用号。sys_call_table 是一张由指向实现各种零碎调用的内核函数的函数指针组成的表:
system_call()函数通过将给定的零碎调用号与 NR_syscalls 做比拟来查看其有效性。如果它大于或者等于 NR syscalls, 该函数就返回一 ENOSYS。否则,就执行相应的零碎调用。

 call *sys_ call-table(,%eax, 4)1

因为零碎调用表中的表项是以 32 位 (4 字节) 类型寄存的,所以内核须要将给定的零碎调用号乘以 4,而后用所得的后果在该表中查问其地位

过程的零碎调用命令转换为 INT 0x80 中断的过程


宏定义_syscallN()见 include/asm/unisted.h)用于零碎调用的格局转换和参数的传递。N 取 0~5 之间的整数。

参数个数为 N 的零碎调用由_syscallN()负责格局转换和参数传递。零碎调用号放入 EAX 寄存器,启动 INT 0x80 后,规定返回值送 EAX 寄存器。

零碎调用功能模块的初始化


对系统调用的初始化也就是对 INT 0x80 的初始化。

系统启动时,汇编子程序 setup_idt(见 arch/i386/kernel/head.S)筹备了 1 张 256 项的 idt 表,由 start_kernel()(见 init/main.c),trap_init()(见 arch/i386/kernel/traps.c)调用的 C 语言宏定义 set_system_gate(0x80,&system_call)(见 include/asm/system.h)设置 0x80 号软中断的服务程序为 system_call(见 arch/i386/kernel/entry.S), system.call 就是所有零碎调用的总入口。

内核如何为各种零碎调用服务


当过程须要进行零碎调用时,必须以 C 语言函数的模式写一句零碎调用命令。该命令如果已在某个头文件中由相应的_syscallN()开展,则用户程序必须蕴含该文件。当过程执行到用户程序的零碎调用命令时,实际上执行了由宏命令_syscallN()开展的函数。零碎调用的参数 由各通用寄存器传递,而后执行 INT 0x80,以内核态进入入口地址 system_call。

ret_from_sys_call


以 ret_from_sys_call 入口的汇编程序段在 linux 过程治理中起到了非常重要的作用。

所有零碎调用完结前以及大部分中断服务返回前,都会跳转至此处入口地址。该段程序不仅仅为零碎调用服务,它还解决中断嵌套、CPU 调度、信号等事务。

内核如何为零碎调用的参数传递参数


参数传递


除了零碎调用号以外,大部分零碎调用都还须要一些内部的参数输人。所以,在产生异样的时候,应该把这些参数从用户空间传给内核。最简略的方法就是像传递零碎调用号一样把这些参数也寄存在寄存器里。在 x86 零碎上,ebx, ecx, edx, esi 和 edi 依照程序寄存前五个参数。须要六个或六个以上参数的状况不多见,此时,应该用一个独自的寄存器寄存指向所有这些参数在用户空间地址的指针。

给用户空间的返回值也通过寄存器传递。在 x86 零碎上,它寄存在 eax 寄存器中。接下来许多对于零碎调用处理程序的形容都是针对 x86 版本的。但不必放心,所有体系结构的实现都很相似。

参数验证


零碎调用必须仔细检查它们所有的参数是否非法无效。举例来说,与文件 I / O 相干的零碎调用必须查看文件描述符是否无效。与过程相干的函数必须查看提供的 PID 是否无效。必须查看每个参数,保障它们岂但非法无效,而且正确。

最重要的一种查看就是检查用户提供的指针是否无效。试想,如果一个过程能够给内核传递指针而又毋庸被查看,那么它就能够给出一个它基本就没有拜访权限的指针,哄骗内核去为它拷贝本不容许它拜访的数据,如本来属于其余过程的数据。在接管一个用户空间的指针之前,内核必须保障:

  • 指针指向的内存区域属于用户空间。过程决不能哄骗内核去读内核空间的数据。
  • 指针指向的内存区域在过程的地址空间里。过程决不能哄骗内核去读其余过程的数据。
  • 如果是读,该内存应被标记为可读。如果是写,该内存应被标记为可写。过程决不能绕过内存拜访限度。

内核提供了两个办法来实现必须的检查和内核空间与用户空间之间数据的来回拷贝。留神,内核无论何时都不能轻率地承受来自用户空间的指针! 这两个办法中必须有一个被调用。为了向用户空间写入数据,内核提供了 copy_to_user(),它须要三个参数。第一个参数是过程空间中的目标内存地址。第二个是内核空间内的源地址。最初一个参数是须要拷贝的数据长度(字节数)。

为了从用户空间读取数据,内核提供了 copy_from_ user(),它和 copy-to-User()类似。该函数把第二个参数指定的地位上的数据拷贝到第一个参数指定的地位上,拷贝的数据长度由第三个参数决定。

如果执行失败,这两个函数返回的都是没能实现拷贝的数据的字节数。如果胜利,返回 0。当呈现上述谬误时,零碎调用返回规范 -EFAULT。

留神 copy_to_user()和 copy_from_user()都有可能引起阻塞。当蕴含用户数据的页被换出到硬盘上而不是在物理内存上的时候,这种状况就会产生。此时,过程就会休眠,直到缺页处理程序将该页从硬盘从新换回物理内存。

零碎调用的返回值


零碎调用 (在 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 中所有零碎调用都应该恪守的命名规定。

拜访零碎调用


零碎调用上下文


内核在执行零碎调用的时候处于过程上下文。current 指针指向当前任务,即引发零碎调用的那个过程。

在过程上下文中,内核能够休眠并且能够被抢占。这两点都很重要。首先,可能休眠阐明零碎调用能够应用内核提供的绝大部分性能。休眠的能力会给内核编程带来极大便当。在过程上下文中可能被抢占,其实表明,像用户空间内的过程一样,以后的过程同样能够被其余过程抢占。因为新的过程能够应用雷同的零碎调用,所以必须小心,保障该零碎调用是可重人的。当然,这也是在对称多解决中必须同样关怀的问题。

当零碎调用返回的时候,控制权依然在 system_call()中,它最终会负责切换到用户空间并让用户过程继续执行上来。

零碎调用拜访示例


操作系统应用零碎调用表将零碎调用编号翻译为特定的零碎调用。零碎调用表蕴含有实现每个零碎调用的函数的地址。例如,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。它会去查看那些在切换回用户空间之前须要实现的工作。如果没有须要做的事件,那么就复原用户过程的状态,并将控制权交还给用户程序。

从用户空间间接拜访零碎调用


通常,零碎调用靠 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_ size=foo();
printf("The kernel stack
size is 81d/n",stack_ size);
return;
}

增加零碎调用


通过批改内核源代码增加零碎调用


linux-2.6.*


通过以上剖析 linux 零碎调用的过程,

将本人的零碎调用加到内核中就是一件容易的事件。上面介绍一个理论的零碎调用,

并把它加到内核中去。要减少的零碎调用是:inttestsyscall(),其性能是在管制终端屏幕上显示 hello world,

执行胜利后返回 0。

编写 int testsyscall()零碎调用–响应函数


编写一个零碎调用意味着要给内核减少 1 个函数,将新函数放入文件 kernel/sys.c 中。新函数代码如下:

asmlingkage sys_testsyscall()
{print("hello worldn");    
    return 0;
 }

增加零碎调用号


编写了新的零碎调用过程后,下一项工作是使内核的其余部分晓得这一程序的存在,而后重建蕴含新的零碎调用的内核。为了把新的函数连贯到已有的内核中去,须要编辑 2 个文件:

1).inculde/asm/unistd.h 在这个文件中退出

#define_NR_testsyscall 191

零碎调用表中增加对应项


2).are/i386/kernel/entry.s 这个文件用来对指针数组初始化,在这个文件中减少一行:

 .long SYMBOL_NAME(_sys_tsetsycall)

将.rept NR_syscalls-190 改为 NR_SYSCALLS-191, 而后从新编译和运行新内核。

应用新的零碎调用


在保障的 C 语言库中没有新的零碎调用的程序段,必须本人建设其代码如下

#inculde

_syscall0(int,testsyscall)

main()
{tsetsyscall();
}

在这里应用了_syscall0 宏指令,宏指令自身在程序中将扩大成名为 syscall()的函数,它在 main()函数外部加以调用。

在 testsyscall()函数中,预处理程序产生所有必要的机器指令代码,包含用零碎调用参数值加载相应的 cpu 寄存器,而后执行 int 0x80 中断指令。

linux-3.*


在 linux-3.8.4/kernel/sys.c 文件开端增加新的零碎调用函数如:

asmlinkage int sys_mycall(int number) {printk("这是我增加的第一个零碎调用");    
    return number; }

在 arch/x86/syscall_32.tbl 下找到 unused 223 号调用而后替换如:

223 i386 mycall sys_mycall

如果是 64 位零碎,在 arch/x86/syscalls/syscall_64.tbl 下找到 313 号零碎调用,而后在其上面加上 314 号本人的中断如:
`314 common mycall sys_mycall

利用内核模块增加零碎调用


模块是内核的一部分,然而并没有被编译到内核外面去。它们被别离编译并连接成一组指标文件,这些文件能被插入到正在运行的内核,或者从正在运行的内核中移走。内核模块至多必须有 2 个函数:init_module 和 cleanup_module。

第一个函数是在把模块插入内核时调用的;

第二个函数则在删除该模块时调用。因为内核模块是内核的一部分,所以能拜访所有内核资源。依据对 linux 零碎调用机制的剖析,

如果要减少零碎调用,能够编写本人的函数来实现,而后在 sys_call_table 表中减少一项,使该项中的指针指向本人编写的函数,

就能够实现零碎调用。上面用该办法实现在管制终端上打印“hello world”的零碎调用 testsyscall()。

编写零碎调用内核模块


#inculde(linux/kernel.h)

#inculde(linux/module.h)

#inculde(linux/modversions.h)

#inculde(linux/sched.h)

 #inculde(asm/uaccess.h)

#define_NR_testsyscall 191

extern viod *sys_call+table[];

asmlinkage int testsyscall() {printf("hello worldn");

    return 0;

}

int init_module() {sys_call_table[_NR_tsetsyscall]=testsyscall;
    printf("system call testsyscall() loaded successn");

    return 0;
}

void cleanup_module() {}

应用新的零碎调用

#define_NR_testsyscall 191

_syscall0(int,testsyscall)

main()
{testsyscall();
}

内核 Linux 零碎调用的列表


以下是 Linux 零碎调用的一个列表,蕴含了大部分罕用零碎调用和由零碎调用派生出的的函数。

过程管制

文件系统管制


文件读写操作

文件系统操作

系统控制

内存治理

网络管理

socket 管制

用户治理

过程间通信

信号

音讯

管道

信号量

共享内存

退出移动版