linux内核剖析(六)Linux系统调用详解(实现机制分析)

本文介绍了系统调用的一些实现细节。首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系。而后,咱们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递系统调用号和参数,执行正确的系统调用函数,并把返回值带回用户空间。最后讨论了如何增长系统调用,并提供了从用户空间访问系统调用的简单例子。
参考 《Linux内核设计与实现》读书笔记(五)- 系统调用html

系统调用概述


计算机系统的各类硬件资源是有限的,在现代多任务操做系统上同时运行的多个进程都须要访问这些资源,为了更好的管理这些资源进程是不容许直接操做的,全部对这些资源的访问都必须有操做系统控制。也就是说操做系统是使用这些资源的惟一入口,而这个入口就是操做系统提供的系统调用(System Call)。在linux中系统调用是用户空间访问内核的惟一手段,除异常和陷入外,他们是内核惟一的合法入口。linux

通常状况下应用程序经过应用编程接口API,而不是直接经过系统调用来编程。在Unix世界,最流行的API是基于POSIX标准的。程序员

操做系统通常是经过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工做,去处理更重要的事情。好比,在x86机器上能够经过int指令进行软件中断,而在磁盘完成读写操做后会向CPU发起硬件中断。算法

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

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

为何须要系统调用


linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用很是类似,只是系统调用由操做系统核心提供,运行于内核态,而普通的函数调用由函数库或用户本身提供,运行于用户态api

通常的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为何它被称做“保护模式”(详细参见深刻理解计算机系统-之-内存寻址(二)–存储保护机制(CPU实模式与保护模式)))。数组

为了和用户空间上运行的进程进行交互,内核提供了一组接口。透过该接口,应用程序能够访问硬件设备和其余操做系统资源。这组接口在应用程序和内核之间扮演了使者的角色,应用程序发送各类请求,而内核负责知足这些请求(或者让应用程序暂时搁置)。实际上提供这组接口主要是为了保证系统稳定可靠,避免应用程序肆意妄行,惹出大麻烦。安全

系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层主要做用有三个:网络

  • 它为用户空间提供了一种统一的硬件的抽象接口。好比当须要读些文件的时候,应用程序就能够不去管磁盘类型和介质,甚至不用去管文件所在的文件系统究竟是哪一种类型。

  • 系统调用保证了系统的稳定和安全。做为硬件设备和应用程序之间的中间人,内核能够基于权限和其余一些规则对须要进行的访问进行裁决。举例来讲,这样能够避免应用程序不正确地使用硬件设备,窃取其余进程的资源,或作出其余什么危害系统的事情。

  • 每一个进程都运行在虚拟系统中,而在用户空间和系统的其他部分提供这样一层公共接口,也是出于这种考虑。若是应用程序能够随意访问硬件而内核又对此一无所知的话,几乎就无法实现多任务和虚拟内存,固然也不可能实现良好的稳定性和安全性。在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函数不须要调用相应的系统调用(所以它所完成的不是内核提供的服务)

系统调用的实现原理


基本机制


前文已经提到了Linux下的系统调用是经过0x80实现的,可是咱们知道操做系统会有多个系统调用(Linux下有319个系统调用),而对于同一个中断号是如何处理多个不一样的系统调用的?最简单的方式是对于不一样的系统调用采用不一样的中断号,可是中断号明显是一种稀缺资源,Linux显然不会这么作;还有一个问题就是系统调用是须要提供参数,而且具备返回值的,这些参数又是怎么传递的?也就是说,对于系统调用咱们要搞清楚两点:

  1. 系统调用的函数名称转换。
  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查找)
而内核中的头文件路径不一样的内核版本以及不一样的发行版,文件的存储结构可能有所区别

linux-3.0
这里写图片描述

linux-2.6

这里写图片描述

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

系统调用表sys_call_table记录了各sys_name函数在表中的位置,共190项。有了这张表,就很容易根据特定系统调用

sys_call_table

在表中的偏移量,找到对应的系统调用响应函数的入口地址。系统调用表共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)

 

因为系统调用表中的表项是以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, esiedi按照顺序存放前五个参数。须要六个或六个以上参数的状况很少见,此时,应该用一个单独的寄存器存放指向全部这些参数在用户空间地址的指针。

给用户空间的返回值也经过寄存器传递。在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_readread()系统调用编号是 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 world\n"); 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_modulecleanup_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 world\n"); return 0; } int init_module() { sys_call_table[_NR_tsetsyscall]=testsyscall; printf("system call testsyscall() loaded success\n"); return 0; } void cleanup_module() { }

 

使用新的系统调用

#define_NR_testsyscall 191 _syscall0(int,testsyscall) main() { testsyscall(); }

 

内核Linux系统调用的列表


如下是Linux系统调用的一个列表,包含了大部分经常使用系统调用和由系统调用派生出的的函数。

进程控制


系统调用 描述
fork 建立一个新进程
clone 按指定条件建立子进程
execve 运行可执行文件
exit 停止进程
_exit 当即停止当前进程
getdtablesize 进程所能打开的最大文件数
getpgid 获取指定进程组标识号
setpgid 设置指定进程组标志号
getpgrp 获取当前进程组标识号
setpgrp 设置当前进程组标志号
getpid 获取进程标识号
getppid 获取父进程标识号
getpriority 获取调度优先级
setpriority 设置调度优先级
modify_ldt 读写进程的本地描述表
nanosleep 使进程睡眠指定的时间
nice 改变分时进程的优先级
pause 挂起进程,等待信号
personality 设置进程运行域
prctl 对进程进行特定操做
ptrace 进程跟踪
sched_get_priority_max 取得静态优先级的上限
sched_get_priority_min 取得静态优先级的下限
sched_getparam 取得进程的调度参数
sched_getscheduler 取得指定进程的调度策略
sched_rr_get_interval 取得按RR算法调度的实时进程的时间片长度
sched_setparam 设置进程的调度参数
sched_setscheduler 设置指定进程的调度策略和参数
sched_yield 进程主动让出处理器,并将本身等候调度队列队尾
vfork 建立一个子进程,以供执行新程序,常与execve等同时使用
wait 等待子进程终止
wait3 参见wait
waitpid 等待指定子进程终止
wait4 参见waitpid
capget 获取进程权限
capset 设置进程权限
getsid 获取会晤标识号
setsid 设置会晤标识号

文件系统控制


文件读写操做


系统调用 描述
fcntl 文件控制
open 打开文件
creat 建立新文件
close 关闭文件描述字
read 读文件
write 写文件
readv 从文件读入数据到缓冲数组中
writev 将缓冲数组里的数据写入文件
pread 对文件随机读
pwrite 对文件随机写
lseek 移动文件指针
_llseek 在64位地址空间里移动文件指针
dup 复制已打开的文件描述字
dup2 按指定条件复制文件描述字
flock 文件加/解锁
poll I/O多路转换
truncat e 截断文件
ftruncate 参见truncate
vumask 设置文件权限掩码
fsync 把文件在内存中的部分写回磁盘

文件系统操做


系统调用 描述
access 肯定文件的可存取性
chdir 改变当前工做目录
fchdir 参见chdir
chmod 改变文件方式
fchmod 参见chmod
chown 改变文件的属主或用户组
fchown 参见chown
lchown 参见chown
chroot 改变根目录
stat 取文件状态信息
lstat 参见stat
fstat 参见stat
statfs 取文件系统信息
fstatfs 参见statfs
readdir 读取目录项
getdents 读取目录项
mkdir 建立目录
mknod 建立索引节点
rmdir 删除目录
rename 文件更名
link 建立连接
symlink 建立符号连接
unlink 删除连接
readlink 读符号连接的值
mount 安装文件系统
umount 卸下文件系统
ustat 取文件系统信息
utime 改变文件的访问修改时间
utimes 参见utime
quotactl 控制磁盘配额

系统控制


系统调用 描述
ioctl I/O总控制函数
_sysctl 读/写系统参数
acct 启用或禁止进程记帐
getrlimit 获取系统资源上限
setrlimit 设置系统资源上限
getrusage 获取系统资源使用状况
uselib 选择要使用的二进制函数库
ioperm 设置端口I/O权限
iopl 改变进程I/O权限级别
outb 低级端口操做
reboot 从新启动
swapon 打开交换文件和设备
swapoff 关闭交换文件和设备
bdflush 控制bdflush守护进程
sysfs 取核心支持的文件系统类型
sysinfo 取得系统信息
adjtimex 调整系统时钟
alarm 设置进程的闹钟
getitimer 获取计时器值
setitimer 设置计时器值
gettimeofday 取时间和时区
settimeofday 设置时间和时区
stime 设置系统日期和时间
time 取得系统时间
times 取进程运行时间
uname 获取当前UNIX系统的名称、版本和主机等信息
vhangup 挂起当前终端
nfsservctl 对NFS守护进程进行控制
vm86 进入模拟8086模式
create_module 建立可装载的模块项
delete_module 删除可装载的模块项
init_module 初始化模块
query_module 查询模块信息
*get_kernel_syms 取得核心符号,已被query_module代替

内存管理


系统调用 描述
brk 改变数据段空间的分配
sbrk 参见brk
mlock 内存页面加锁
munlock 内存页面解锁
mlockall 调用进程全部内存页面加锁
munlockall 调用进程全部内存页面解锁
mmap 映射虚拟内存页
munmap 去除内存页映射
mremap 从新映射虚拟内存地址
msync 将映射内存中的数据写回磁盘
mprotect 设置内存映像保护
getpagesize 获取页面大小
sync 将内存缓冲区数据写回硬盘
cacheflush 将指定缓冲区中的内容写回磁盘

网络管理


系统调用 描述
getdomainname 取域名
setdomainname 设置域名
gethostid 获取主机标识号
sethostid 设置主机标识号
gethostname 获取本主机名称
sethostname 设置主机名称

socket控制


系统调用 描述
socketcall socket系统调用
socket 创建socket
bind 绑定socket到端口
connect 链接远程主机
accept 响应socket链接请求
send 经过socket发送信息
sendto 发送UDP信息
sendmsg 参见send
recv 经过socket接收信息
recvfrom 接收UDP信息
recvmsg 参见recv
listen 监听socket端口
select 对多路同步I/O进行轮询
shutdown 关闭socket上的链接
getsockname 取得本地socket名字
getpeername 获取通讯对方的socket名字
getsockopt 取端口设置
setsockopt 设置端口参数
sendfile 在文件或端口间传输数据
socketpair 建立一对已联接的无名socket

用户管理


系统调用 描述
getuid 获取用户标识号
setuid 设置用户标志号
getgid 获取组标识号
setgid 设置组标志号
getegid 获取有效组标识号
setegid 设置有效组标识号
geteuid 获取有效用户标识号
seteuid 设置有效用户标识号
setregid 分别设置真实和有效的的组标识号
setreuid 分别设置真实和有效的用户标识号
getresgid 分别获取真实的,有效的和保存过的组标识号
setresgid 分别设置真实的,有效的和保存过的组标识号
getresuid 分别获取真实的,有效的和保存过的用户标识号
setresuid 分别设置真实的,有效的和保存过的用户标识号
setfsgid 设置文件系统检查时使用的组标识号
setfsuid 设置文件系统检查时使用的用户标识号
getgroups 获取后补组标志清单
setgroups 设置后补组标志清单

进程间通讯


系统调用 描述
ipc 进程间通讯总控制调用

信号


系统调用 描述
sigaction 设置对指定信号的处理方法
sigprocmask 根据参数对信号集中的信号执行阻塞/解除阻塞等操做
sigpending 为指定的被阻塞信号设置队列
sigsuspend 挂起进程等待特定信号
signal 参见signal
kill 向进程或进程组发信号
*sigblock 向被阻塞信号掩码中添加信号,已被sigprocmask代替
*siggetmask 取得现有阻塞信号掩码,已被sigprocmask代替
*sigsetmask 用给定信号掩码替换现有阻塞信号掩码,已被sigprocmask代替
*sigmask 将给定的信号转化为掩码,已被sigprocmask代替
*sigpause 做用同sigsuspend,已被sigsuspend代替
sigvec 为兼容BSD而设的信号处理函数,做用相似sigaction
ssetmask ANSI C的信号处理函数,做用相似sigaction

消息


系统调用 描述
msgctl 消息控制操做
msgget 获取消息队列
msgsnd 发消息
msgrcv 取消息

管道


系统调用 描述
pipe 建立管道

信号量


系统调用 描述
semctl 信号量控制
semget 获取一组信号量
semop 信号量操做

共享内存


系统调用 描述
shmctl 控制共享内存
shmget 获取共享内存
shmat 链接共享内存
shmdt 拆卸共享内存
相关文章
相关标签/搜索