1.将已经下载好的内核解压,我的目录是
/home/shangsongchao/LinuxKernel/testlinux-3.18.6/linux-3.18.6
2.添加系统调用表:
在/home/shangsongchao/LinuxKernel/testlinux-3.18.6/linux-3.18.6/arch/x86/syscalls目录下,打开syscall_32.tbl
添加三个系统调用,如图358,359,360
不需要像Linux 2.6的内核一样,在<asm/unistd.h>中添加类似于#define __NR_foo 358之类的宏定义了,3.x的内核会自动根据系统调用表的定义生成。
3.添加系统调用的服务例程
在/home/shangsongchao/LinuxKernel/testlinux-3.18.6/linux-3.18.6/arch/x86/kernel目录下,打开process_32.c
添加下面函数
/*
* sys_foo
* return each process kernel stack sizes
* */
asmlinkage long sys_foo(void)
{
return 5;
}
/*
* sys_my_oper
* compute numbers + - * /
*/
asmlinkage long sys_my_oper(int * result , int num1,int num2, char *op)
{
if(*op=='+')
{
*result=num1+num2;
}
else if(*op=='-')
{
*result=num1-num2;
}
else if(*op=='*')
{
*result=num1*num2;
}
else if(*op=='/')
{
*result=num1/num2;
}
else
return 0;
return 0;
}
/*
* sys_my_compute
* compute numbers + - * /
*/
asmlinkage long sys_my_compute(int num1,int num2, char *op)
{
long result=0;
if(*op=='+')
{
result=num1+num2;
}
else if(*op=='-')
{
result=num1-num2;
}
else if(*op=='*')
{
result=num1*num2;
}
else if(*op=='/')
{
result=num1/num2;
}
else
result=0;
return result;
}
如图:
4.编译内核
在/home/shangsongchao/LinuxKernel/testlinux-3.18.6/linux-3.18.6目录下,执行一下命令:
make i386_defconfig
make # 一般要编译很长时间,少则20分钟多则数小时
5.安装内核
sudo make modules //编译模块
sudo make modules_install //安装模块
sudo make install //安装内核
6.重启查看内核版本
uname -r
7.编写C测试程序
#include<linux/unistd.h>
#include<sys/syscall.h>
#include<stdio.h>
#include <time.h>
#define _NR_my_oper 359
#define CALL_TIME 1000*1000*100
int main(){
int num1 = 10;
int num2 = 5;
int result;
long i;
time_t *t;
long end;
long begin = (long)(time(t));
for(i =0; i<CALL_TIME ;i++ ){
syscall(_NR_my_oper, &result, num1, num2, "+");
}
printf("%d\n",result);
end = (long)(time(t));
printf("syscall:%ld\n", end-begin);
begin = (long)(time(t));
for(i =0; i<CALL_TIME ;i++ ){
result = num1+num2;
}
printf("%d\n",result);
end = (long)(time(t));
printf("non-syscall:%ld\n", end-begin);
return 0;
}
测试图
收获:
1. 系统调用表的产生过程。
内核开发者是在syscall_64.tbl中声明系统调用号与服务例程的对应关系,以及其ABI,但系统调用表的真正定义是在arch/x86 /kernel/syscall_64.c中。
① arch/x86/kernel/syscall_64.c, arch/x86/kernel /syscall_32.c文件中存放了实际的系统调用表定义,以64位系统为例,其中有如下内容:
#include <asm/asm-offsets.h>
#define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
#ifdef CONFIG_X86_X32_ABI
# define __SYSCALL_X32(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
#else
# define __SYSCALL_X32(nr, sym, compat) /* nothing */
#endif
#define __SYSCALL_64(nr, sym, compat) extern asmlinkage void sym(void) ; // 注意,是分号结尾
#include <asm/syscalls_64.h> // 引入系统调用服务例程的声明
#undef __SYSCALL_64
#define __SYSCALL_64(nr, sym, compat) [nr] = sym, //注意,是逗号结尾
typedef void (*sys_call_ptr_t)(void);
extern void sys_ni_syscall(void);
const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { //系统调用表定义
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h> // 系统调用服务例程地址,对应arch/x86/include/generated/asm/syscalls_64.h文件
};
② arch/x86/syscalls目录中的syscall_64.tbl、syscall_32.tbl文件是系统调用表声明。syscalltbl.sh脚本负责生产syscalls_XX.h文件,由Makefile负责驱动
② arch/x86/include/generated目录,其中存放根据arch/x86/syscalls目录生成的文件。主要有 generated/asm/syscalls_64.h、generated/asm/syscalls_32.h文件,用于生成系统调用表数组。生成 的syscalls_32.h内容部分如下:
2. 系统调用号声明头文件的生成(#define __NR_xxx之类的信息)。
类似于系统调用表的产生,arch/x86/syscalls/syscallhdr.sh脚本负责generated/uapi/asm /unistd_32.h, generated/uapi/asm/unistd_64.h文件,unistd_XX.h文件又被间接include到 asm/unistd.h中,后者最终被include用户空间使用的<sys/syscall.h>文件中(安装之后)。生成的generated/uapi/asm/unistd_32.h部分内容如下
注意,这里的unistd.h文件与用户空间使用的<unistd.h>文件没有任何关系,后者声明了系统调用包装函数,包括syscall函数等
参考网址: