原始连接:
http://www.physics.sjtu.edu.cn/~nbh/bbs/viewtopic.php?p=216
发表于: 星期六 二月 26, 2005 11:47 pm 发表主题: 为FreeBSD增加系统调用
--------------------------------------------------------------------------------
发信人: bsder (不鸣), 信区: FreeBSD
标 题: 为FreeBSD增加系统调用
发信站: BBS 水木清华站 (Thu Jan 27 14:35:08 2005), 转信
为FreeBSD增加系统调用
本文举例讲述如何为FreeBSD添加一个系统调用。以下的例子是在装有FreeBSD5.3-RELEASE
的i386架构工作站下测试通过的。
什么是系统调用?
系统调用是用户程序为完成某种工作向系统内核提出的一种请求。每种操作系统都会提供有
限个数的系统调用,作为应用程序请求系统内核服务的桥梁。
在FreeBSD操作系统中定义了用C语言表示的系统调用。每一个系统调用在C库中有一个同名
函数,应用程序使用C语言可以调用这些函数,这些函数向系统要求相应的内核服务。
例如,read是一个系统调用,他请求操作系统将储存在磁盘上的数据读入缓冲区。
为FreeBSD增加新的系统调用:
首先介绍几个FreeBSD源代码中关于系统调用的文件:
l /usr/src/sys/kern/syscalls.c, 保存系统调用名称的字符型指针数组,每个元素储存
一个系统调用名称。
l /usr/src/sys/kern/init_sysent.c, 结构体sysent数组,每一元素包含两个成员,其
中一个成员用来储存系统调用对应函数的参数个数,另一个成员是指向系统调用对应函数的
指针。
l /usr/src/sys/sys/sysproto.h, 包含系统调用对应函数参数的原型。
l /usr/src/sys/sys/syscall.h,包含系统调用对应的数字编号,例如:
#define SYS_read 3
表明read对应的系统调用数字编号为3。
l /usr/src/sys/sys/syscall.mk, Makefile要用到的文件。
下面我们看看如何为FreeBSD增加一个系统调用。我们还是从打印Hello World开始。我们将
Hello World输出到FreeBSD默认的控制台。我们将这个系统调用命名为hello。
首先编辑/usr/src/sys/kern/syscalls.master增加一条数据hello。
系统调用有多种类型:
l STD 一直包含在系统
l COMPAT 兼容4.3考虑,要在内核配置文件定义options COMPAT_43才生效
l COMPAT4 兼容FreeBSD4.x,要在内核配置文件定义options COMPAT_FREEBSD4才生效
l LIBCOMPAT也是为兼容考虑,要在syscall.h定义才生效
l OBSOL 已经废除的系统调用,已经不包含在系统。只保留个名字
l UNIMPL 没有实现,只占个位置
l NOSTD 如果你定义成NOSTD它是通过lkm实现的,sysent entry将被lkmsys填充
,使得SYSCALL_MODULE宏工作。
文件/usr/src/sys/kern/syscalls.master格式:
系统调用对应的数字编号 系统调用类型 { 系统调用对应函数的伪原型 } [别名]
添加一条新数据在/usr/src/sys/kern/syscalls.master文件最后。
445 STD { int hello( void ); }
保存/usr/src/sys/kern/syscalls.master并在/usr/src/sys/kern目录下执行命令make ini
t_sysent.c这个命令通过分析/usr/src/sys/kern/syscalls.master文件重新生成前面介绍
的/usr/src/sys/kern/syscalls.c, /usr/src/sys/kern/init_sysent.c, /usr/src/sys/sy
s/sysproto.h, /usr/src/sys/sys/syscall.h 以及 /usr/src/sys/sys/syscall.mk, 这五
个文件。
下面我们来编写我们新增加的系统调用hello在系统内核中对应的文件/usr/src/sys/kern/h
ello.c (hello.c文件放在/usr/src/sys/kern目录不是必需的):
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/proc.h>
#include <sys/syscall.h>
int hello( td, uap )
struct thread *td;
struct hello_args *uap;
{
printf("Hello World\n");
return (0);
}
接下来编辑/usr/src/sys/conf/files文件在文件底部加入一行kern/hello.c standard保
存文件并从新编译内核。
# cd /usr/src/sys/i386/conf/
#config GENERIC
#cd ../compile/GENERIC/
#make depend
#make
#make install
#reboot
系统重起后新的系统内核生效了,我们为FreeBSD新增加的系统调用也跟着生效了,下面我
们编写一个简单的测试程序test.c用来测试我们新增加的系统调用:
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
int main( int argc, char **argv)
{
syscall(445);
}
编译test.c
#gcc –o test test.c
执行test如果默认控制台输出Hello World证明我们编写的系统调用生效了。但目前我们还
只能通过系统调用对应的数字编号调用我们所添加的系统调用。后面的例子我们将解决Free
BSD新增的系统调用如何使用同名函数方式调用的问题。
接下来的例子mysyscall.c主要说明如何通过系统调用传递一个字符串到内核缓冲并打印。
参数len只是用来检测参数str是否超过内核缓冲buffer的长度:
添加一条新数据在/usr/src/sys/kern/syscalls.master文件最后。
446 STD { int mysyscall( char *str , int len ); }
在/usr/src/sys/kern目录下执行命令make init_sysent.c。
需要说明的是系统调用同名函数返回值问题,struct thread这个结构题是在/usr/src/sys/
sys/proc.h中定义的, 系统调用同名函数返回值储存在结构体struct thread中的register_
t td_retval[2]成员。(为什么td_retval是一个双元素数组,因为在系统调用fork中td_
retval[0]为父进程返回值而td_retval[1]为子进程返回值。通常我们不需要关心td_retval
[1]),在我们创建的系统调用同名函数mysyscall中如果执行成功返回0失败返回-1。
创建文件/usr/src/sys/kern/mysyscall.c:
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/proc.h>
#include <sys/syscall.h>
int mysyscall ( td, uap )
struct thread *td;
struct mysyscall_args *uap; /*这个结构在/usr/src/sys/sys/sysproto.h 定义
make init_sysent.c的时候会自动生成*/
{
char buffer[1024];
/*如果len大于1024就返回一个EINVAL错误*/
if( uap->len > 1024 )
{
td->td_retval[0] = -1;
return( EINVAL );
}
/*拷贝str到缓冲buffer*/
bcopy( uap->str , buffer , uap->len );
printf( "%s\n" , buffer );
td->td_retval[0] = 0;
return (0);
}
编辑文件/usr/src/sys/conf/files在文件底部加入一行kern/mysyscall.c standard,重
新编译内核并重启计算机。为了让用户程序可以使用系统调用同名函数来调用新创建的系统
调用,我们拷贝文件/usr/src/sys/sys/syscall.h至/usr/include/sys/syscall.h接着我们
要重新编译/usr/src/lib/libc/并重起计算机:
#cd /usr/src/lib/libc/
#make
#make install
#reboot
我们编写一个测试程序test2.c:
int mysyscall( char* , int ); /*声明一个原型*/
main()
{
char str[]="hello kernel";
mysyscall( str, strlen(str)+1 );
}
编译test2.c
#gcc –o test2 test2.c
#./test2
如果看到默认控制台输出hello kernel 那证明用户程序成功利用系统调用同名函数方式调
用了新创建的系统调用。
FreeBSD4.x问题:
另外考虑到有些用户还固守着FreeBSD4.x平台,在FreeBSD4.x新增系统调用的方法基本与Fr
eeBSD5.x相同。只是FreeBSD5.x中struct thread取代了FreeBSD4.x中的struct proc。在Fr
eeBSD4.x新增系统调用只需要将上例中的struct thread替换为 struct proc将结构体成员t
d_retval替换为p_retval。这与FreeBSD5.x中不再使用进程控制块PCB来执行任务调度,
而是使用线程控制块TCB来作为最基本的调度实体有关。
需要注意的问题:
自己增加的系统调用,很可能给系统带来种种安全隐患。解决办法是不要自己添加系统调用
,除非你必须这样做才能达到你的目的。
参考资料:
1. Kevin Lo 2003年 Adding System Calls (an OpenBSD Example) :
http://www.onlamp.com/pub/a/bsd/ ... g_system_calls.html
2. FreeBSD5.3原代码 以及FreeBSD4.10源代码
http://www.freebsd.org/
关于作者:
张帆,承天网络科技有限公司。Email:
bsder@msn.com 目前从事网络安全产品的开发。
--