向linux-2.6.34内核中添加系统调用


(2010-06-20 17:51:37)
转载
标签:
系统调用
linux-2.6.34
it
    分类: 嵌入式Linux学习历程
      系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。它并非直接和程序员或系统管理员打交道,它仅仅是一个通过软中断机制(我们后面讲述)向内核提交请求,获取内核服务的接口。而在实际使用中程序员调用的多是用户编程接口——API,而管理员使用的则多是系统命令。
     系统调用是用户空间和内核空间交互的一种有效手段,但是这并不是说要完成交互功能就非要添加新系统调用不可。除了系统本身提供的系统调用外,用户还可以添加自己的系统调用。下面我们就以一个简单的例子来说明系统调用添加的具体过程。
首先,进入到内核源码目录/usr/src/linux-2.6.34中,添加自己的系统调用号。
sally@sally-desktop:~$ cd /usr/src/linux-2.6.34/
系统调用号在unistd_32.h文件中定义。内核中每个系统调用号都是以“__NR_"开头的,在该文件中添加自己的系统调用号
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo vim arch/x86/include/asm/unistd_32.h
#define __NR_pwritev            334
#define __NR_rt_tgsigqueueinfo  335
#define __NR_perf_event_open    336
#define __NR_recvmmsg           337
#define __NR_mycall             338        

#ifdef __KERNEL__

#define NR_syscalls 339
 在内核源文件中该行为#define NR_syscalls 338,在系统调用执行的过程               中,system_call()函数会根据该值来对用户态进程的有效性进行检查。如果这个号大于或等于NR_syscalls,系统调用处理程序终止。所以应该将原来的#define NR_syscalls 338修改为#define NR_syscalls 339
其次,在系统调用表中添加相应的表项
(1)sally@sally-desktop:/usr/src/linux-2.6.34$ sudo vim arch/x86/kernel/syscall_table_32.S
ENTRY(sys_call_table)
        .long sys_restart_syscall      
        .long sys_exit
         ………………(这里省略了部分)
        .long sys_rt_tgsigqueueinfo    
        .long sys_perf_event_open
        .long sys_recvmmsg
        .long sys_mycall
(2)sally@sally-desktop:/usr/src/linux-2.6.34$ sudo vim arch/h8300/kernel/syscalls.S

#include <linux/sys.h>
#include <asm/linkage.h>
#include <asm/unistd.h>
……………………(这里省略了部分)
        .long SYMBOL_NAME(sys_vmsplice)
        .long SYMBOL_NAME(sys_ni_syscall)      
        .long SYMBOL_NAME(sys_getcpu)
        .long SYMBOL_NAME(sys_ni_syscall)      
        .long SYMBOL_NAME(sys_mycall)
最后,实现系统调用服务例程。
系统调用服务例程函数名为"sys_xxx”
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo vim arch/x86/kernel/sys_i386_32.c
asmlinkage long sys_mycall(void)
{
        printk("mycall worked!!\n");
        printk("current pid is:%d\n",current->pid);
        return current->pid;
}
其中,asmlinkage修饰符是gcc中一个特殊的标志,加了该修饰符的函数必须从堆栈中获取参数。内核中所有系统调用的实现都使用这个修饰符。该服务例程的作用是输出当前进程的pid。
接下来的任务就是重新编译内核了!通过上述几步,添加新的系统调用的所有工作已经完成。但是要使这个系统调用真正的运行起来,还需要重新编译内核。这里内核编译相对之前内核编译及安装而言非常简单,依次输入如下命令即可:
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo make bzImage
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo make modules
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo make install
sally@sally-desktop:/usr/src/linux-2.6.34$ sudo make modules_install
这样内核便编译好了。
注意:内核编译完了之后必须重启,否则已经添加的用户系统调用不能使用。

sally@sally-desktop:~$ sudo  /usr/include/bits/syscall.h

#ifndef _SYSCALL_H
# error "Never use <bits/syscall.h> directly; include <sys/syscall.h> instead."
#endif
#include <bits/wordsize.h>
#define SYS__sysctl __NR__sysctl
#define SYS_access __NR_access
………………(这里省略了部分)
#define SYS_vm86old __NR_vm86old
#define SYS_waitpid __NR_waitpid
#define SYS_mycall __NR_mycall
#endif
为了检验添加的系统调用是否成功,特编写下面的测试程序。
sally@sally-desktop:~/linux课程实验/系统调用$ vim test.c
#include <stdio.h>
#include <linux/unistd.h>
#include <stdio.h>
#include <syscall.h>
#include <sys/types.h>
#define mycall() syscall(SYS_mycall)
int main()
{
        int x = 0;
        x = mycall();
        printf("current pid : %d\n", x);
        return 0;
}
sally@sally-desktop:~/linux课程实验/系统调用$ gcc test.c -o test
sally@sally-desktop:~/linux课程实验/系统调用$ ./test
current pid : 5570
使用dmesg命令可以看到系统信息如下:
[ 3051.573873] mycall worked!!
[ 3051.573879] current pid is:5570
这样一个简单的系统调用便添加成功了!


你可能感兴趣的:(gcc,table,嵌入式,vim,Access,include)