系统调用是应用程序和操作系统内核之间的功能接口 其主要目的是使得用户可以使用操作系统提供的有关设备管理 输入/输入系统 文件系统和进程控制 通信以及存储管理等方面的功能 而不必了解系统程序的内部结构和有关硬件细节 从而起到减轻用户负担和保护系统以
系统调用是应用程序和操作系统内核之间的功能接口其主要目的是使得用户可以使用操作系统提供的有关设备管理输入/输入系统文件系统和进程控制通信以及存储管理等方面的功能而不必了解系统程序的内部结构和有关硬件细节从而起到减轻用户负担和保护系统以及提高资源利用率的作用
Linux操作系统作为自由软件的代表它优良的性能使得它的应用日益广泛不仅得到专业人士的肯定而且商业化的应用也是如火如荼在Linux中大部分的系统调用包含在Linux的libc库中通过标准的C函数调用方法可以调用这些系统调用那么对Linux的发烧友来说如何在Linux中增加新的系统调用呢?
Linux系统调用机制
在Linux系统中系统调用是作为一种异常类型实现的它将执行相应的机器代码指令来产生异常信号产生中断或异常的重要效果是系统自动将用户态切换为核心态来对它进行处理这就是说执行系统调用异常指令时自动地将系统切换为核心态并安排异常处理程序的执行Linux用来实现系统调用异常的实际指令是
QUOTE:
Int $x
这一指令使用中断/异常向量号(即进制的)将控制权转移给内核为达到在使用系统调用时不必用机器指令编程在标准的C语言库中为每一系统调用提供了一段短的子程序完成机器代码的编程工作事实上机器代码段非常简短它所要做的工作只是将送给系统调用的参数加载到CPU寄存器中接着执行int $x指令然后运行系统调用系统调用的返回值将送入CPU的一个寄存器中标准的库子程序取得这一返回值并将它送回用户程序
为使系统调用的执行成为一项简单的任务Linux提供了一组预处理宏指令它们可以用在程序中这些宏指令取一定的参数然后扩展为调用指定的系统调用的函数
这些宏指令具有类似下面的名称格式
QUOTE: _syscallN(parameters)
其中N是系统调用所需的参数数目而parameters则用一组参数代替这些参数使宏指令完成适合于特定的系统调用的扩展例如为了建立调用setuid()系统调用的函数应该使用
QUOTE: _syscall( int setuid uid_t uid )
syscallN( )宏指令的第个参数int说明产生的函数的返回值的类型是整型第个参数setuid说明产生的函数的名称后面是系统调用所需要的每个参数这一宏指令后面还有两个参数uid_t和uid分别用来指定参数的类型和名称
另外用作系统调用的参数的数据类型有一个限制它们的容量不能超过四个字节这是因为执行int $x指令进行系统调用时所有的参数值都存在位的CPU寄存器中使用CPU寄存器传递参数带来的另一个限制是可以传送给系统调用的参数的数目这个限制是最多可以传递个参数所以Linux一共定义了个不同的_syscallN()宏指令从_syscall()_syscall()直到_syscall()
一旦_syscallN()宏指令用特定系统调用的相应参数进行了扩展得到的结果是一个与系统调用同名的函数它可以在用户程序中执行这一系统调用
添加新的系统调用
如果用户在Linux中添加新的系统调用应该遵循几个步骤才能添加成功下面几个步骤详细说明了添加系统调用的相关内容
() 添加源代码
第一个任务是编写加到内核中的源程序即将要加到一个内核文件中去的一个函数该函数的名称应该是新的系统调用名称前面加上sys_标志假设新加的系统调用为mycall(int number)在/usr/src/linux/kernel/sysc文件中添加源代码如下所示
QUOTE: asmlinkage int sys_mycall(int number)
{
return number;
}
作为一个最简单的例子我们新加的系统调用仅仅返回一个整型值
() 连接新的系统调用
添加新的系统调用后下一个任务是使Linux内核的其余部分知道该程序的存在为了从已有的内核程序中增加到新的函数的连接需要编辑两个文件
在我们所用的Linux内核版本(RedHat 内核为)中第一个要修改的文件是
QUOTE:
/usr/src/linux/include/asmi/unistdh
该文件中包含了系统调用清单用来给每个系统调用分配一个唯一的号码文件中每一行的格式如下
QUOTE:
#define __NR_name NNN
其中name用系统调用名称代替而NNN则是该系统调用对应的号码应该将新的系统调用名称加到清单的最后并给它分配号码序列中下一个可用的系统调用号我们的系统调用如下
QUOTE: #define __NR_mycall
系统调用号为之所以系统调用号是是因为Linux内核自身的系统调用号码已经用到
第二个要修改的文件是
QUOTE: /usr/src/linux/arch/i/kernel/entryS
该文件中有类似如下的清单
QUOTE:
long SYMBOL_NAME()
该清单用来对sys_call_table[]数组进行初始化该数组包含指向内核中每个系统调用的指针这样就在数组中增加了新的内核函数的指针我们在清单最后添加一行
QUOTE:
long SYMBOL_NAME(sys_mycall)
() 重建新的Linux内核
为使新的系统调用生效需要重建Linux的内核这需要以超级用户身份登录
QUOTE: #pwd
/usr/src/linux
#
超级用户在当前工作目录(/usr/src/linux)下才可以重建内核
QUOTE: #make config
#make dep
#make clearn
#make bzImage
编译完毕后系统生成一可用于安装的压缩的内核映象文件
QUOTE:
/usr/src/linux/arch/i/boot/bzImage
() 用新的内核启动系统
要使用新的系统调用需要用重建的新内核重新引导系统为此需要修改/etc/liloconf文件在我们的系统中该文件内容如下
QUOTE:
boot=/dev/hda
map=/boot/map
install=/boot/bootb
prompt
timeout=
image=/boot/vmlinuz
label=linux
root=/dev/hdb
readonly
other=/dev/hda
label=dos
table=/dev/had
首先编辑该文件添加新的引导内核
QUOTE:
image=/boot/bzImagenew
label=linuxnew
root=/dev/hdb
readonly 添加完毕该文件内容如下所示
QUOTE: boot=/dev/hda
map=/boot/map
install=/boot/bootb
prompt
timeout=
image=/boot/bzImagenew
label=linuxnew
root=/dev/hdb
readonly
image=/boot/vmlinuz
label=linux
root=/dev/hdb
readonly
other=/dev/hda
label=dos
table=/dev/hda
这样新的内核映象bzImagenew成为缺省的引导内核为了使用新的liloconf配置文件还应执行下面的命令
QUOTE:
#cp /usr/src/linux/arch/i/boot/zImage /boot/bzImagenew
其次配置lilo:
QUOTE:
# /sbin/lilo
现在当重新引导系统时在boot:提示符后面有三种选择linuxnew linuxdos新内核成为缺省的引导内核
至此新的Linux内核已经建立新添加的系统调用已成为操作系统的一部分重新启动Linux用户就可以在应用程序中使用该系统调用了
()使用新的系统调用
在应用程序中使用新添加的系统调用mycall同样为实验目的我们写了一个简单的例子xtdyc
QUOTE:
/* xtdyc */
#include
_syscall(intmycallintret)
main()
{
printf(%d \n