linux内核3.16.0怎么linux添加系统调用 调用

博客访问: 47076
博文数量: 53
博客积分: 125
博客等级: 入伍新兵
技术积分: 255
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
  系统调用是内核和应用程序间的接口,应用程序要访问硬件设备和其他操作系统资源,可以通过系统调用来完成。
  在linux中,系统调用是用户空间访问内核的一种手段,除异常和中断外,他们是进入内核的合法入口。系统调用的数量很少,在i386上只有大概300个左右。
  应用程序员通过C库中的应用程序接口(API)而不是直接通过系统调用来编程。
  C库中的函数可以不调用系统调用,也可以只是简单封装一个系统调用,还可以通过调用多个系统调用来实现一个功能。
  linux>本身提供的一组宏来对系统调用直接进行访问。man 2 syscall。
  从程序员的角度来看,系统调用无关紧要,他们只需要跟API打交道就可以了;
  从内核的角度来看,内核只跟系统调用打交道,库函数及应用程序怎么使用系统调用不是内核所关心的。
  linux内核中所有的系统调用函数都用sys_开头。
  函数定义中的asmlinkage宏,用于通知编译器,使用局部堆栈来传递参数;
  函数定义中的FASTCALL宏,用于通知编译器,使用寄存器来传递参数。
  如果上面两个宏都没有,则使用默认传参规则,前4个参数通过R0~R3寄存器传递,其余更多的参数通过栈传递。
  调用链:APP --> LIB --> kernel (syscall)&&--> module --> hardware
  因为系统调用要从用户空间进入内核空间,所以不可能通过简单的函数调用完成,必须通过一些处理器支持的特殊机制(所谓的软中断)。
  在x86上,这一特殊机制就是汇编指令int $0x80, 而在arm上,就是汇编指令SWI。
  这条指令被封装到C库中的函数里,当程序执行到这一条指令后,cpu会进入一个特殊的异常模式(或软中断模式),并将程序指针跳转到特点的位置(如arm为中断向量表的0x8处)。
  内核中实现了很多的系统调用,这些系统调用的地址被按顺序放在一个系统调用表中,这个表是一个名为sys_call_table的数组,共有NR_syscalls个表项。
  通过这个表,就可以调用到内核定义的所有sys_函数。
  调用汇编指令int $0x80 或SWI 时,要同时传递一个系统调用号,这个系统调用号将作为索引,从sys_call_table中选择对应的系统调用。
  int80将系统调用号保存在eax寄存器中,而SWI将其直接集成在指令中(如SWI 0x124)。
  过程:swi 系统调用号 --> 系统调用表 --> 系统调用
  内核中处理系统调用的函数定义在arch/x86/kernel/entry.s中的system_call,而arm系统在arch/arm/kernel/entry-common.s中的vector_swi。
  x86系统的系统调用表定义在arch/x86/kernel/syscall_table.s(或直接定义在entry.s)中,而arm定义在arch/arm/kernel/calls.s中。
  x86系统调用号定义在arch/x86/include/asm/unistd.h中,arm系统调用号定义在arch/arm/include/asm/unistd.h中。
  系统调用必须仔细检查传入参数的有效性,尤其是用户提供的指针,必须确保:    *指针指向的内存区域属于用户空间,进程不能哄骗内核去读内核空间的数据。    *指针指向的内存区域属于进程的地址空间,不能哄骗内核去读其他进程的数据。    *进程不能绕过内存访问权限。
  内核在执行系统调用的时候处于进程上下文,可以休眠,也可以被抢占,所以必须保证系统调用是可重入的。
跟踪linux内核中arm架构的sys_getpid()系统调用
系统调用号在文件arch/arm/include/asm/unistd.h中,如下:
  说明:可见sys_getpid()的系统调用号是20,此调用号其实就是系统调用表(数组)的下标。所以系统调用表中sys_getpid()肯定在第20项。
    & 特别注意:系统调用号17之类,此系统调用已经弃用,但为了兼容性及不至于日后混乱,所以调用号不能重用,只能空着(跳过).
系统调用表在文件arch/arm/kernel/calls.s,如下:
  说明:可见sys_getpid()在系统调用表中第20项,和其系统调用号一致。
     特别注意:系统调用号17对应的表项,对于已经弃用的系统调用,linux系统统一赋予sys_ni_syscall()系统调用。
系统调用的声明在文件&include/linux/syscalls.h,如下:
系统调用的实现在文件kernel/timer.c,如下:
  说明:源代码中不能直接找到sys_getpid()的实现代码,因为64为系统的BUG,所以源代码中的系统调用sys_ABC,都用SYSCALL_DEFINEx(ABC)封装了一层,以解决BUG。
SYSCALL_DEFINE在文件include/linux/syscalls.h,如下:
手工添加一个自己实现的系统调用:
首先,模仿原来代码,在文件arch/arm/include/asm/unistd.h添加一个系统调用号,如下:
特别说明:自己新加的系统调用号只能在原来最大值得基础上加1,所以我的系统调用号是361,对应系统调用是sys_mysyscall()
然后,模仿原来代码,在文件arch/arm/kernel/calls.S添加一个系统调用表项,如下:
最后:编写系统调用的实现代码,此代码必须能保证编译进内核,如下:
因为我们知道文件init/main.c一定编译进内核,所以我们的实现代码在此文件中添加。
特别注意:系统调用必须返回long型值。
要使用此系统调用,必须重新编译内核,并且开发板必须使用新内核,如下:
<SPAN style="COLOR: # 在主机端,linux源码跟目录,输入如下: <SPAN style="COLOR: # # make <SPAN style="COLOR: # # cp -f arch/arm/boot/zImage /tftpboot/ <SPAN style="COLOR: # 在开发板端,输入如下: <SPAN style="COLOR: # $ reboot
程序:测试系统调用的实现效果
创建目录/nfsroot/kern//02/。
创建文件/nfsroot/kern//02/test.c,内容如下:
创建文件/nfsroot/kern//02/Makefile,内容如下:
在主机端编译程序,过程如下:
在开发板端运行测试程序,过程如下:
  说明:可见getpid的两种结果一直,我们自己的系统调用361也正确运行。
特别说明:
系统调用号是linux内核维护人员确定的,自己添加的系统调用,其他人开发的应用程序不会使用到,因为只有自己知道有这个系统调用。
这种系统调用需要直接修改内核源代码,而且必须编译进内核才能使用,而且系统调用号是自己设定的,所以一般不会这样手动添加系统调用。
若自己sys_open,sys_read等系统调用,可以通过模块来实现,从而实现系统调用的复用。
阅读(3379) | 评论(0) | 转发(3) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。选题要求:在Linux内核中增加一个系统调用,并;目录;一.程序的主要设计思路,实现方式........;1.1添加系统调用的两种方法..........;1.1.1编译内核法..............;1.1.2内核模块法..............;1.2程序的主要设计思路............;1.3环境...................;二.
选题要求:在Linux内核中增加一个系统调用,并编写对应的linux应用程序。利用该系统调用能够遍历系统当前所有进程的任务描述符,并按进程父子关系将这些描述符所对应的进程id(PID)组织成树形结构显示。
一.程序的主要设计思路,实现方式 .......................... 1
1.1 添加系统调用的两种方法....................................................................... 1
1.1.1编译内核法 ...................................................................................................... 1
1.1.2内核模块法 ...................................................................................................... 1
1.2 程序的主要设计思路............................................................................... 1
1.3 环境........................................................................................................... 2
二.程序的模块划分,及对每个模块的说明 .................... 2
2.1 通过内核模块实现添加系统调用........................................................... 2
2.1.1修改系统调用的模块 ...................................................................................... 2
2.1.2获取sys_call_table的地址 ............................................................................. 2
2.1.3清除内存区域的写保护 .................................................................................. 3
2.2 编写系统调用指定自己的系统调用....................................................... 4
2.2.1内核的初始化函数 .......................................................................................... 4
2.2.2自己的系统调用服务例程 .............................................................................. 4
2.2.3移除内核模块时,将原有的系统调用进行还原 .......................................... 6
2.2.4模块注册相关 .................................................................................................. 6
2.3 编写用户态的测试程序........................................................................... 6
2.4 编写Makefile文件 .................................................................................. 7
三.所遇到的问题及解决的方法 .............................. 8
3.1 进程个数确定........................................................................................... 8
3.2 被更改的系统调用号的选择................................................................... 8
3.3 获取系统调用表的地址........................................................................... 8
3.4 内核和用户态数据交换........................................................................... 8
四.程序运行结果及使用说明 ................................ 8
4.1 将编译出来的内核模块hello.ko加载到内核中.................................... 8
4.2通过dmesg查看输出信息是否正确 ....................................................... 9
4.3运行测试程序,输出树状打印结果(部分结果截图)........................ 9
4.4卸载自定义模块...................................................................................... 10
五.附录 ................................................. 11
5.1 内核模块程序hello.c ............................................................................. 11
5.2 测试程序hello_test.c ............................................................................. 14
5.3Makefile文件 ........................................................................................... 14
一.程序的主要设计思路,实现方式
1.1添加系统调用的两种方法
1.1.1编译内核法
编写好源码之后
以上准备工作做完之后,然后就要进行编译内核了,以下是编译内核的一个过程
1.1.2内核模块法
内核模块可以作为独立程序来编译的函数和数据类型的集合。之所以提供模块机制,是因为Linux本身是一个单内核。单内核由于所有内容都集成在一起,效率很高,但可扩展性和可维护性相对较差,模块机制可以弥补这一缺陷。
Linux模块可以通过静态或动态的方法加载到内核空间,静态加载是指在内核启动过程中加载;动态加载是指在内核运行的过程中随时加载。
一个模块被加载到内核中时,就成为内核代码的一部分。模块加载入系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号添加到内核符号表中,以便模块间通信。
这种方法是采用系统调用拦截的一种方式,改变某一个系统调用号对应的服务程序为我们自己的编写的程序,从而相当于添加了我们自己的系统调用。
下面的内容,会详述用内核模块法实现目标的过程。
1.2程序的主要设计思路
程序分三部分,一部分是通过内核模块实现添加系统调用,二是编写系统调用指定自己的系统调用,最后是编写用户态的测试程序。
Ubuntu14.04 + 3.13.0内核版本
内核版本:
二.程序的模块划分,及对每个模块的说明
2.1 通过内核模块实现添加系统调用
这种方法其实是系统调用拦截的实现。系统调用服务程序的地址是放在sys_call_table中通过系统调用号定位到具体的系统调用地址,那么我们通过编写内核模块来修改sys_call_table中的系统调用的地址为我们自己定义的函数的地址,就可以实现系统调用的拦截。
通过模块加载时,将系统调用表里面的那个系统调用号的那个系统调用号对应的系统调用服务例程改为我们自己实现的系统历程函数地址。
2.1.1修改系统调用的模块
在/usr/include/i386-linux-gnu/asm/unistd_32.h文件中查看系统调用序号:
找到结果(部分截图):
可以看到,222号和223号系统调用是空的,因此选取223作为新的系统调用号。
2.1.2获取sys_call_table的地址
在/boot/System.map-3.16.0-30-generic查看系统调用表的内存地址:
找到结果:
三亿文库包含各类专业文献、中学教育、专业论文、各类资格考试、应用写作文书、幼儿教育、小学教育、高等教育、外语学习资料、15Linux内核中增加一个系统调用_图文等内容。 
 Linux 内核修改与编译图文教程 1 1、 实验目的针对 Ubuntu10.04 中,通过下载新的内核版本, 并且修改新版本内核中的系 统调用看,然后,在其系统中编译,加载新...  linux-2.6.32.63内核版本增加系统调用过程_计算机软件及应用_IT/计算机_专业资料。linux-2.6.32.63内核版本增加系统调用过程实验1 要求 1、不能只是简单的实验...  ②系统初始化后运行的第一个内核程序 asmlinkage void __initstart_kernel(void)定义在 /usr/src/linux/init/main.c 中,它通过调用 usr/src/linux/arch/i386/...  操作系统实验一:向 Linux 内核增加一个系统调用一、实验目的通过实验, 熟悉 Linux 操作系统的使用,掌握构建与启动 Linux 内核的方 法; 掌握用户程序如何利用系统...  ……….…...7 操作系统课程设计 1、 概述这次设计主要完成的工作是编写一个简单的程序,然后加载到内核 中并在 Linux 系统内核中添加系统调用。 2、 课程设计...  (2) 连接新的系统调用 添加新的系统调用后,下一个任务是使 Linux 内核的其余部分知道该程序的存 在.为了从已有的内核程序中增加到新的函数的连接,需要编辑两个...  Linux 内核提供了大量的系统调用,现在从系统 调用的基本原理出发探究 Linux 系统调用的方法。 这是在一个用户进程中通过 GNU C 库进行的系统调用示意图,系统调用...  4、线程的实现 Linux 内核中没有专门的实现线程的机制,而是通过用户级程序库来...Linux 的每个系统调用都是通过一些宏、一张系统调用表、一个系统调用入口来完成...  linux实验_添加系统调用-完整版_理学_高等教育_教育专区。完整介绍了linux添加系统...中,每一个 title 标签表示一个启动项,仿照第 一段 title 的写法,编写新内核...linux采用编译内核的方法增加系统功能调用
我的图书馆
linux采用编译内核的方法增加系统功能调用
最近做了我们院的操作系统课程设计,本来是想把我的报告拿出来和大家分享的,可寝室的朋友说我这么做容易使后来的童鞋不经过自己的思考就全盘copy,所以我决定还是分类讲讲我做的过程吧!首先完成的是一个linux系统功能调用的增加。我们都知道系统功能调用是Unix/Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服务后,将控制和结果返回给用户程序。系统调用的主要目的是使得用户可以使用操作系统提供的有关设备管理、输入/输出系统、文件系统和进程控制、通信以及存储管理等方面的功能,而不必了解系统程序的内部结构和有关硬件细节,从而起到减轻用户负担和保护系统以及提高资源利用率的作用。在进行系统功能调用时会由用户态(也称目态)转到核态(也称管态)。昨天我的一信安的朋友还问过我管态和目态的区别,我想来想去也就是这里有大的区别了,在管态下能使用系统的所有资源,调用系统的特权函数,而目态是不行的。在执行系统功能调用时就必须是在管态下执行。而如果我需要给我的linux系统增加一个系统功能调用的话,就必须弄清楚系统是如何调用那些功能函数的,又是如何由目态变为管态的。在linux下可以通过中断来进入管态,这类中断称为访管中断。在Linux系统中,系统调用是作为一种异常类型实现的,它将执行相应的机器代码指令来产生异常信号。产生中断或异常的重要效果是系统自动将用户态切换为核心态来对它进行处理。用户态的程序只有通过门(gate)陷入(trap)到系统内核中去(执行int指令),才能执行一些具有特权的内核函数。系统调用完成后,系统执行另一组特征指令(iret指令)将系统返回到用户态,控制权返回给进程。Linux用来实现系统调用异常的实际指令是:int $0x80这一指令使用中断/异常向量号128(即16进制的80)将控制权转移给内核(进行模式切换)。为达到在使用系统调用时不必用机器指令编程,在标准的C语言库中为每一系统调用提供了一段短的子程序,完成机器代码的编程工作。事实上,机器代码段非常简短。它所要做的工作只是将送给系统调用的参数加载到CPU寄存器中,接着执行int $0x80指令。然后运行系统调用。系统调用的返回值将送入CPU的一个寄存器中,标准的库子程序取得这一返回值,并将它送回用户程序。下面以getuid()系统调用为例来看调用过程:我们可以看到其中有一些宏定义,我们可以看看这些宏的定义(arch/i386/kernel/ entry.S).………#define SAVE_ALL \ \pushl % \pushl % \pushl % \pushl % \pushl % \pushl % \pushl % \pushl % \pushl % \movl $(__USER_DS),%&\movl %edx,% \movl %edx,%我们可以看到SAVE_ALL主要是保存寄存器信息,即现场保留。其中, movl $(__USER_DS), %edx;从这一句开始是重新填充DS,ES段。#define RESTORE_INT_REGS \popl % \popl % \popl % \popl % \popl % \popl % \popl %eax#define RESTORE_REGS \RESTORE_INT_REGS; \1: popl % \2: popl % \.section .fixup,"ax"; \3: movl $0,(%esp); \jmp 1b; \4: movl $0,(%esp); \jmp 2b; \. \.section __ex_table,"a";\.align 4; \.long 1b,3b; \.long 2b,4b; \.previous&ENTRY(ret_from_fork)pushl %eaxcall schedule_tailGET_THREAD_INFO(%ebp)popl %eaxjmp syscall_exit这里主要完成现场恢复并返回。ENTRY(system_call)pushl %eax # save orig_eaxSAVE_ALLGET_THREAD_INFO(%ebp)# system call tracing in operation/* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */testw $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),TI_flags(%ebp)jnz syscall_trace_entrycmpl $(nr_syscalls), %eaxjae syscall_badsyssyscall_call:call *sys_call_table(,%eax,4)movl %eax,EAX(%esp) # store the return valuesyscall_exit:cli # make sure we don't miss an interrupt# setting need_resched or sigpending# between sampling and the iretmovl TI_flags(%ebp), %ecxtestw $_TIF_ALLWORK_MASK, %cx # current-&workjne syscall_exit_workrestore_all:movl EFLAGS(%esp), %eax # mix EFLAGS, SS and CS# Warning: OLDSS(%esp) contains the wrong/random values if we# are returning to the kernel.# See comments in process.c:copy_thread() for details.movb OLDSS(%esp), %ahmovb CS(%esp), %alandl $(VM_MASK | (4 && 8) | 3), %eaxcmpl $((4 && 8) | 3), %eaxje ldt_ss # returning to user-space with LDT SSrestore_nocheck:RESTORE_REGSaddl $4, %esp1: iret这一段中,主要是完成调用。eax放置的是系统调用号,因为eax有可能被使用,所以先保存其值。call *sys_call_table(,%eax,4)这一句是计算调用的入口。其中,sys_call_table是LINUX的系统调用表,它存在目录arch/i386/kernel/ sys_call_table.S 下。.dataENTRY(sys_call_table).long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */.long sys_exit.long sys_fork.long sys_read.long sys_write.long sys_open /* 5 */………….long sys_mq_timedreceive /* 280 */.long sys_mq_notify.long sys_mq_getsetattr.long sys_ni_syscall /* reserved for kexec */.long sys_waitid.long sys_ni_syscall /* 285 */ /* available */.long sys_add_key.long sys_request_key.long sys_keyctl.代表当前地址,sys_call_table代表数组首地址。这个表依次保存所有系统调用的函数指针,以方便总的系统调用处理函数(system_call)进行索引。调用具体的实现在kernel/sys.c中。asmlinkage longsys_getuid16(void){return hig2lowuid(current_uid);}刚才我们提到,这一指令使用中断/异常向量号128(即16进制的80)将控制权转移给内核,那么中断向量是怎么形成的。它的定义在(arch/i386/kernel/traps.c)中。void __init trap_init(void){……set_trap_gate(0,&divide_error);set_trap_gate(1,&debug);set_intr_gate(2,&nmi);set_system_gate(3,&int3); /* int3-5 can be called from all */set_system_gate(4,&overflow);set_system_gate(5,&bounds);set_trap_gate(6,&invalid_op);set_trap_gate(7,&device_not_available);set_trap_gate(8,&double_fault);set_trap_gate(9,&coprocessor_segment_overrun);set_trap_gate(10,&invalid_TSS);set_trap_gate(11,&segment_not_present);set_trap_gate(12,&stack_segment);set_trap_gate(13,&general_protection);set_intr_gate(14,&page_fault);set_trap_gate(15,&spurious_interrupt_bug);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);set_trap_gate(18,&machine_check);set_trap_gate(19,&simd_coprocessor_error);set_system_gate(,&system_call);……}上一句就是设置system_call的值。SYSCALL_VECTOR的值就是0X80.&那么概括起来,系统调用的过程大致如下:(1)&系统调用初始化在traps.c中,系统在初始化程序trap_init()中,通过调用set_system_gate(0x80,*system_call)完成中断描述表的填充。这样当每次用户执行指令int 0x80时,系统能把控制转移到entry.S中的函数中去。(2)&系统调用执行system_call会根据用户传进来系统调用号,在系统调用表 system_call中寻找到相应偏移地址的内核处理函数,进行相应的处理。当然在这个过程之前,要保存环境(SAVE_ALL)。(3)&系统调用的返回系统调用处理完毕后,通过sys_call_exit返回。返回之前,程序会检查一些变量,相应地返回。不一定是返回到用户进程。真正返回到用户空间时,要恢复环境(restore_all)。&在前面提到system_call会根据用户传进来系统调用号,在系统调用表 system_call中寻找到相应偏移地址的内核处理函数,进行相应的处理。那么系统调用号怎么产生,在include/asm-i386/unistd.h中可以看到系统调用号的定义。#define __NR_restart_syscall 0#define __NR_exit 1#define __NR_fork 2#define __NR_read 3#define __NR_write 4#define __NR_open 5#define __NR_close 6#define __NR_waitpid 7#define __NR_creat 8#define __NR_link 9……#define __NR_mq_open 277#define __NR_mq_unlink (__NR_mq_open+1)#define __NR_mq_timedsend (__NR_mq_open+2)#define __NR_mq_timedreceive (__NR_mq_open+3)#define __NR_mq_notify (__NR_mq_open+4)#define __NR_mq_getsetattr (__NR_mq_open+5)#define __NR_sys_kexec_load 283#define __NR_waitid 284/* #define __NR_sys_setaltroot 285 */#define __NR_add_key 286#define __NR_request_key 287#define __NR_keyctl 288#define NR_syscalls 289此处的代码是从2.6.11中的代码,其中系统调用号已到了288,并且与前面system_call中的相对应。每一个系统调用号前都是相应函数名加了__NR_。内核跟用户程序的交互,其实有标准C库作为它们之间的桥梁。标准C库把用户希望传递的参数装载到CPU的寄存器中,然后触发0X80中断。当从系统调用返回的时候(sys_call_exit),标准C库又接过控制权,处理返回值。对于__NR_,标准C库会作相应处理。转换成相应函数。&对于系统函数的调用,有几个通用的宏在include/asm-i386/unistd.h中定义。#define __syscall_return(type, res) \do { \if ((unsigned long)(res) &= (unsigned long)(-(128 + 1))) { \errno = -(res); \res = -1; \} \return (type) (res); \} while (0)#else# define __syscall_return(type, res) return (type) (res)#endif#define _syscall0(type,name) \type name(void) \{ \long __ \__asm__ volatile ("int $0x80" \: "=a" (__res) \: "0" (__NR_##name)); \__syscall_return(type,__res); \}这是无参函数调用的形式。#define _syscall1(type,name,type1,arg1) \type name(type1 arg1) \{ \long __ \__asm__ volatile ("int $0x80" \: "=a" (__res) \: "0" (__NR_##name),"b" ((long)(arg1))); \__syscall_return(type,__res); \}这是含一个参数的调用形式,……标准C库会把我们的调用如pause()转换成相应的形式。pause()int pause(void){long ____asm__ volatile(“int $0x80”:”=a”(__res):””(__NR_pause));__syscall_return(int,__res);}进入内核调用过程。&基础知识介绍完了,下面来进行我们的实验:准备如果你安装的系统包含内核源文件,一般在/usr/src路径下可以看到,那么可以直接跳到步骤3进行内核修改。首先下载最新的linux2.6.37内核,先修改/usr/src/linux下的Makefile文件,将内核版本修改成自己的。把2.6.37中Makefile文件头几行为:VERSION = 2PATCHLEVEL = 6SUBLEVEL = 37EXTRAVERSION = .1我们可以修改成自己版本(2.6.37.rangercyh):VERSION = 2PATCHLEVEL = 6SUBLEVEL = 37EXTRAVERSION = rangercyh下载源代码如果系统不包含源文件,则需要在网站上下载系统源代码。网址:在官方网站上下到类似 linux-2.6.37.1.tar.gz的代码后(大概有70兆左右),放在/usr/src/ 的目录下,然后解压(如何解压请看我的博文:),解压后会出现文件夹& linux-2.6.37.1。不过貌似最新的代码已经出到2.6.38了。修改相应内核文件(1)&修改(添加)源代码第一个任务是编写加到内核中的源程序,即将要加到一个内核文件中去的一个函数,该函数的名称应该是新的系统调用名称前面加上sys_标志。假设新加的系统调用为mycall(int number),在/usr/src/linux—2.6.37.1/kernel/sys.c文件中添加源代码,如下所示:作为一个最简单的例子,我新加的系统调用是实现一个文件复制功能。代码如下:说明:在以后的文档中,我们所指的/usr/src/linux—2.6.37.1/都是指内核路径,要根据自己的内核存放位置进行相应的改变。&(2)&连接新的系统调用添加新的系统调用后,下一个任务是使Linux内核的其余部分知道该程序的存在。为了从已有的内核程序中增加到新的函数的连接,需要编辑两个文件。在我们所用的Linux内核版本(2.6.37.1)中,首先要修改的文件是unistd.h:/usr/src/linux—2.6.37.1/arch/x86/include/asm/unistd_32.h该文件中包含了系统调用清单,用来给每个系统调用分配一个唯一的号码。文件中每一行的格式如下:#define __NR_name NNN其中,name用系统调用名称代替,而NNN则是该系统调用对应的号码应该将新的系统调用名称加到清单的最后,并给它分配号码序列中下一个可用的系统调用号。我们的系统调用如下:#define __NR_sync_file_vange 337#define __NR_tee 338#define __NR_vmsplice 339#define __NR_syscalls 340最后一行NR_syscalls 说明内核自身的系统调用号已经使用到339,共有340个(从0开始)。我们新添加的系统调用应该加到最后,并修改系统调用总数。修改如下:#define __NR_sync_file_vange 337#define __NR_tee 338#define __NR_vmsplice 339#define __NR_mysyscall 340&/*这是我们自己添加的系统调用*/#define __NR_syscalls 341第二个要修改的文件是:/usr/src/linux—2.6.37.1/arch/x86/kernel/syscall_table_32.s&在2.6版本以前,需要修改的是/usr/src/linux/arch/i386/kernel/entry.s,在该文件下有sys_call_table,直接进行修改。在2.6版本后,entry.s文件中的sys_call_table独立出来,我们可以在该文件下看到类似于#include "syscall_table.s"字样,说明我们正真需要修改syscall_table.s。 在syscall_table.s中有类似如下的清单:.long sys_name该清单用来对sys_call_table[]数组进行初始化。该数组包含指向内核中每个系统调用的指针。这样就在数组中增加了新的内核函数的指针。我们在清单上与系统调用号相对应的位置添加一行:.long sys_mysyscall必须注意添加的行的位置,否则容易造成内核编译的失败。开始对新的内核进行编译首先需要清空以前的编译信息,make mrproper命令清除旧的配置等文件,避免编译内核时生成的文件不一致。make menuconfig命令生成配置清单文件。make -j5命令编译新内核,这个编译过程非常长,需要1~2个小时。j5参数代表使用5个线程同时编译,这样速度会点。make bzImage命令生成系统视图。make modules命令生成模块。make modules_install命令安装模块。mkinitramfs -o /boot/initrd.img-mycall命令生成系统镜像文件。make install命令安装新的系统。update-grub命令更新启动程序grub,使启动界面上出现新安装的系统。&进入自己编译的系统后就可以编写测试程序。测试代码如下,使用syscall函数调用自己编写的系统功能函数:注意:syscall()中的第一个参数341是系统调用号,第二个参数是源文件,第三个参数是目标文件。至此这个系统功能调用就完成了,这次实验有个小插曲,就是我同时在帮一位信安的朋友一起做这个课设,所以文档很多地方都是直接copy的,结果我是先把他的文档完成了再做我自己的文档,结果没有留意到一个地方写了他的名字,结果文档被老师收走了,现在我只能听天由命了,希望老师不要发现这个名字的问题,要不然我就会悲剧的被指名抄袭~~~我冤不冤~~有木有~~有木有~~&本文出自 “” 博客,请务必保留此出处
TA的最新馆藏[转]&[转]&[转]&

我要回帖

更多关于 linux4.4添加系统调用 的文章

 

随机推荐