电子游戏平台linux系统调用,Linux系统调用过程

Linux内核分析(六)—-字符设备控制方法实现|揭秘系统调用本质,linux系统调用
Linux内核分析(六)
昨天我们对字符设备进行了初步的了解,并且实现了简单的字符设备驱动,今天我们继续对字符设备的某些方法进行完善。
今天我们会分析到以下内容:
1. 字符设备控制方法实现
2. 揭秘系统调用本质
在昨天我们实现的字符设备中有open、read、write等方法,由于这些方法我们在以前编写应用程序的时候,相信大家已经有所涉及所以就没单独列出来分析,今天我们主要来分析一下我们以前接触较少的控制方法。
l 字符设备控制方法实现
1. 设备控制简介
1.
何为设备控制:我们所接触的大部分设备,除了读、写、打开关闭等方法外,还应该具有控制方法,比如:控制电机转速、串口配置波特率等。这就是对设备的控制方法。
2.
用户如何进行设备控制:类似与我们在用户空间使用read、open等函数对设备进行操作,我们在用户空间对设备控制的函数是ioctl其原型为 int ioctl(int fd, int cmd,
…)//fd为要控制的设备文件的描述符,cmd是控制命令,…依据第二个参数类似与我们的printf等多参函数。
3.
Ioctl调用驱动那个函数:在我们的用户层进行ioctl调用的时候驱动会根据内核版本不同调用不同的函数,有以下:
1) 2.6.36以前的内核版本会调用 long
(*ioctl) (struct inode*,struct file *, unsigned int, unsigned
long);
2) 2.6.36以后的内核会调用 long
(*unlocked_ioctl) (struct file *, unsigned int, unsigned
long);
2. Ioctl实现
1.
控制命令解析:我们刚才说到ioctl进行控制的时候有个cmd参数其为int类型的也就是32位,我们的linux为了让这32位更加有意义,所表示的内容更多,所以将其分为了下面几个段
1) Type(类型/幻数8bit):表明这是属于哪个设备的命令
2) Number(序号8bit):用来区分统一设备的不同命令
3)
Direction(2bit):参数传递方向,可能的取值,_IOC_NODE(没有数据传输)、_IOC_READ(从设备读)、_IOC_WRITE(向设备写)
4) Size(13/14bit()):参数长度
2.
定义命令:我们的控制命令如此复杂,为了方便我们的linux系统提供了固定的宏来解决命令的定义,具体如下:
1)
_IO(type,nr); :定义不带参数的命令
2) _IOR(type,nr,datatype); :从设备读参数命令
3) _IOW(type,nr,datatype); :向设备写入参数命令
下面定义一个向设备写入参数的命令例子
#define MEM_CLEAR
_IOW(‘m’,0,int)//通常用一个字母来表示命令的类型
3.
Ioctl实现:下面我们去向我们上次实现的字符设备中添加ioctl方法,并实现设备重启命令(虚拟重启),对于不支持的命令我们返回-EINVAL代码如下,整体工程在
1 long mem_ioctl(struct file *fd, unsigned int cmd, unsigned long arg){
2 switch(cmd){
3 case MEM_RESTART:
4 printk("<0> memdev is restart");
5 break;
6 default:
7 return -EINVAL;
8 }
9 return 0;
10 }
l 揭秘系统调用本质
由于我自己的PC的调用过程不太熟悉,下面以arm的调用过程分析一下我们用户层调用read之后发生了什么,是怎么调用到我们驱动写的read函数的呢,我们下面进行深入剖析。
1. 代码分析
我们首先使用得到arm上可执行的应用程序 arm-linux-gcc -g -static read_mem.c -o
read_mem 然后使用 arm-linux-objdump
-D -S read_mem >dump 得到汇编文件,我们找到main函数的汇编实现
1 int main(void)
2 {
3 8228: e92d4800 push {fp, lr}
4 822c: e28db004 add fp, sp, #4 ; 0x4
5 8230: e24dd008 sub sp, sp, #8 ; 0x8
6 int fd = 0;
7 8234: e3a03000 mov r3, #0 ; 0x0
8 8238: e50b3008 str r3, [fp, #-8]
9 int test = 0;
10 823c: e3a03000 mov r3, #0 ; 0x0
11 8240: e50b300c str r3, [fp, #-12]
12
13 fd = open("/dev/memdev0",O_RDWR);
14 8244: e59f004c ldr r0, [pc, #76] ; 8298 <main+0x70>
15 8248: e3a01002 mov r1, #2 ; 0x2
16 824c: eb0028a3 bl 124e0 <__libc_open>
17 8250: e1a03000 mov r3, r0
18 8254: e50b3008 str r3, [fp, #-8]
19 read(fd,&test,sizeof(int));
20 8258: e24b300c sub r3, fp, #12 ; 0xc
21 825c: e51b0008 ldr r0, [fp, #-8]
22 8260: e1a01003 mov r1, r3
23 8264: e3a02004 mov r2, #4 ; 0x4
24 8268: eb0028e4 bl 12600 <__libc_read>//我们的read函数最终调用了__libc_read
25
26 printf("the test is %dn",test);
27 826c: e51b300c ldr r3, [fp, #-12]
28 8270: e59f0024 ldr r0, [pc, #36] ; 829c <main+0x74>
29 8274: e1a01003 mov r1, r3
30 8278: eb000364 bl 9010 <_IO_printf>
31
32 close(fd);
33 827c: e51b0008 ldr r0, [fp, #-8]
34 8280: eb0028ba bl 12570 <__libc_close>
35 return 0;
36 8284: e3a03000 mov r3, #0 ; 0x0
37 }
上面我们发现read最终调用了__libc_read函数我们继续在汇编代码中找到该函数
1 00012600 <__libc_read>:
2 12600: e51fc028 ldr ip, [pc, #-40] ; 125e0 <__libc_close+0x70>
3 12604: e79fc00c ldr ip, [pc, ip]
4 12608: e33c0000 teq ip, #0 ; 0x0
5 1260c: 1a000006 bne 1262c <__libc_read+0x2c>
6 12610: e1a0c007 mov ip, r7
7 12614: e3a07003 mov r7, #3 ; 0x3
8 12618: ef000000 svc 0x00000000
9 1261c: e1a0700c mov r7, ip
10 12620: e3700a01 cmn r0, #4096 ; 0x1000
11 12624: 312fff1e bxcc lr
12 12628: ea0008b4 b 14900 <__syscall_error>
13 1262c: e92d408f push {r0, r1, r2, r3, r7, lr}
14 12630: eb0003b9 bl 1351c <__libc_enable_asynccancel>
15 12634: e1a0c000 mov ip, r0
16 12638: e8bd000f pop {r0, r1, r2, r3}
17 1263c: e3a07003 mov r7, #3 ; 0x3//系统调用标号,一会解释大家先记主
18 12640: ef000000 svc 0x00000000
19 12644: e1a07000 mov r7, r0
20 12648: e1a0000c mov r0, ip
21 1264c: eb000396 bl 134ac <__libc_disable_asynccancel>
22 12650: e1a00007 mov r0, r7
23 12654: e8bd4080 pop {r7, lr}
24 12658: e3700a01 cmn r0, #4096 ; 0x1000
25 1265c: 312fff1e bxcc lr
26 12660: ea0008a6 b 14900 <__syscall_error>
27 12664: e1a00000 nop (mov r0,r0)
28 12668: e1a00000 nop (mov r0,r0)
29 1266c: e1a00000 nop (mov r0,r0)
在上面代码中大部分汇编指令都知道用法,但是svc调用引起注意,通过查阅资料才发现,我们应用程序通过svc
0x00000000可以产生异常,进入内核空间。
然后呢,系统处理异常,这中间牵扯好多代码还有中断的一些知识,我们找时间在专门分析,总之经过一大堆的处理最后它会跳到entry-common.S中的下面代码
1
.align 5 2 ENTRY(vector_swi) 3 sub sp, sp, #S_FRAME_SIZE 4 stmia sp,
{r0 – r12} @ Calling r0 – r12 5 ARM( add r8, sp, #S_PC ) 6 ARM( stmdb
r8, {sp, lr}^ ) @ Calling sp, lr 7 THUMB( mov r8, sp ) 8 THUMB(
store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr 9 mrs r8, spsr @
called from non-FIQ mode, so ok. 10 str lr, [sp, #S_PC] @ Save
calling PC 11 str r8, [sp, #S_PSR] @ Save CPSR 12 str r0, [sp,
#S_OLD_R0] @ Save OLD_R0 13 zero_fp 14 15 /* 16 * Get the system
call number. 17 */ 18 19 #if defined(CONFIG_OABI_COMPAT) 20 21 /*
22 * If we have CONFIG_OABI_COMPAT then we need to look at the swi 23
* value to determine if it is an EABI or an old ABI call. 24 */ 25
#ifdef CONFIG_ARM_THUMB 26 tst r8, #PSR_T_BIT 27 movne r10, #0 @
no thumb OABI emulation 28 ldreq r10, [lr, #-4] @ get SWI instruction
29 #else 30 ldr r10, [lr, #-4] @ get SWI instruction 31 A710( and
ip, r10, #0x0f000000 @ check for SWI ) 32 A710( teq ip, #0x0f000000 )
33 A710( bne .Larm710bug ) 34 #endif 35 #ifdef
CONFIG_CPU_ENDIAN_BE8 36 rev r10, r10 @ little endian instruction 37
#endif 38 39 #elif defined(CONFIG_AEABI) 40 41 /* 42 * Pure EABI
user space always put syscall number into scno (r7). 43 */ 44 A710( ldr
ip, [lr, #-4] @ get SWI instruction ) 45 A710( and ip, ip,
#0x0f000000 @ check for SWI ) 46 A710( teq ip, #0x0f000000 ) 47 A710(
bne .Larm710bug ) 48 49 #elif defined(CONFIG_ARM_THUMB) 50 51 /*
Legacy ABI only, possibly thumb mode. */ 52 tst r8, #PSR_T_BIT @
this is SPSR from save_user_regs 53 addne scno, r7,
#__NR_SYSCALL_BASE @ put OS number in 54 ldreq scno, [lr, #-4]
55 56 #else 57 58 /* Legacy ABI only. */ 59 ldr scno, [lr, #-4] @
get SWI instruction 60 A710( and ip, scno, #0x0f000000 @ check for SWI
) 61 A710( teq ip, #0x0f000000 ) 62 A710( bne .Larm710bug ) 63 64
#endif 65 66 #ifdef CONFIG_ALIGNMENT_TRAP 67 ldr ip,
__cr_alignment 68 ldr ip, [ip] 69 mcr p15, 0, ip, c1, c0 @ update
control register 70 #endif 71 enable_irq 72 73 get_thread_info tsk
74 adr tbl, sys_call_table @ load syscall table pointer View Code
该段代码中我们先会获取系统调用的标号刚才让大家记住的3,然后呢会去查找sys_call_table我们找到
1 .type sys_call_table, #object
2 ENTRY(sys_call_table)
3 #include "calls.S"
4 #undef ABI
5 #undef OBSOLETE
在calls.S中我们找到了下面东西(列出部分)
1 */
2 /* 0 */ CALL(sys_restart_syscall)
3 CALL(sys_exit)
4 CALL(sys_fork_wrapper)
5 CALL(sys_read)
6 CALL(sys_write)
7 /* 5 */ CALL(sys_open)
8 CALL(sys_close)
9 CALL(sys_ni_syscall) /* was sys_waitpid */
10 CALL(sys_creat)
11 CALL(sys_link)
我们发现我们刚才记住的数字3刚好对应的是sys_read,在read_write.c中我们可以找到sys_read函数
1 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
2 {
3 struct file *file;
4 ssize_t ret = -EBADF;
5 int fput_needed;
6
7 file = fget_light(fd, &fput_needed);
8 if (file) {
9 loff_t pos = file_pos_read(file);
10 ret = vfs_read(file, buf, count, &pos);//调用虚拟文件系统的read
11 file_pos_write(file, pos);
12 fput_light(file, fput_needed);
13 }
14
15 return ret;
16 }
关于SYSCALL_DEFINE3这个宏的解析大家可以去
这篇文章查看,在此我就不分析了,我们继续找到vfs_read代码如下
1 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
2 {
3 ssize_t ret;
4
5 if (!(file->f_mode & FMODE_READ))
6 return -EBADF;
7 if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
8 return -EINVAL;
9 if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
10 return -EFAULT;
11
12 ret = rw_verify_area(READ, file, pos, count);
13 if (ret >= 0) {
14 count = ret;
15 if (file->f_op->read)//我们的文件读函数指针不为空
16 ret = file->f_op->read(file, buf, count, pos);//执行我们驱动中的读函数
17 else
18 ret = do_sync_read(file, buf, count, pos);
19 if (ret > 0) {
20 fsnotify_access(file);
21 add_rchar(current, ret);
22 }
23 inc_syscr(current);
24 }
25
26 return ret;
27 }
2. 过程总结
电子游戏平台,通过上面的分析我们已经了解的read函数的调用基本过程,下面我们将read函数的调用过程在进行总结:
1. 寻找svc异常总体入口,并进入内核空间
2. 取出系统调用的标号
3. 根据系统调用标号,在sys_call_table中找到对应的系统调用函数
4. 根据系统函数比如sys_read找到对应的虚拟文件系统的read
5. 虚拟文件系统在调用驱动的read。
至此我们的分析到此结束,当然整个过程中还有一部分异常处理没有说到,我们在分析中断的时候一块分析。
今天的分析到此结束,感谢大家的关注。
Linux 内核分析(六) 昨天我们对字符设备进行了初步的了…
1 系统调用的作用
系统调用是操作系统提供给用户(应用程序)的一组接口,每个系统调用都有一个对应的系统调用函数来完成相应的工作。用户通过这个接口向操作系统申请服务,如访问硬件,管理进程等等。
应用程序和文件系统的接口是系统调用。
我们经常看到的比如fork、open、write
等等函数实际上并不是真正的系统调用函数,他们都只是c库,在这些函数里将执行一个软中断
swi 指令,产生一个软中断,使CPU 陷入内核态,接着在内核中进行一系列的判断,判断出是哪个系统调用,再转到真正的系统调用函数,完成相应的功能。
2 系统调用过程
http://www.linuxidc.com/Linux/2015-04/116546.htm
系统调用是操作系统提供给用户(应用程序)的一组接口,每个系统调用都有一个对应的系统调用函数来完成相应的工作。用户通过这个接口向操作系统申请服务,如访问硬件,管理进程等等。但是因为用户程序运行在用户空间,而系统调用运行在内核空间,因此用户程序不能直接调用系统调用函数,我们经常看到的比如fork、open、write
等等函数实际上并不是真正的系统调用函数,他们都只是c库,在这些函数里将执行一个软中断
swi 指令,产生一个软中断,使CPU
陷入内核态,接着在内核中进行一系列的判断,判断出是哪个系统调用,再转到真正的系统调用函数,完成相应的功能。下面举一个简单的例子说明从用户态调用一个“系统调用”,到内核处理的整个执行流程。
用户态程序如下:
void pk()
{
__asm__(
”ldr r7 =365 n”
”swi n”
:
:
:
);
}
int main()
{
pk();
retrun 0;
}
上面的代码中,我自己实现了一个新的系统调用,具体怎么做,后面再具体描述。pk()事实上就可以类比于平时我们在用户程序里调用的
open() 等函数,这个函数只做了一件简单的事:将系统调用号传给 r7
,,然后产生一软中断。接着CPU陷入内核
内核态:
CPU相应这个软中断以后,PC指针会到相应的中断向量表中取指,中断向量表在内核代码中:arch/arm/kernel/entry-armv.S
中定义
.LCvswi:
.word vector_swi
.globl __stubs_end
__stubs_end:
.equ stubs_offset, __vectors_start + 0x200 – __stubs_start
.globl __vectors_start
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi +
stubs_offset #响应中断后pc指向这里
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
当pc取到如上的指令后,会跳到 vector_swi
这个标号,这个标号在arch/arm/kernel/entry-commen.S 中定义。
.align 5
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 – r12} @ Calling r0 – r12
ARM( add r8, sp, #S_PC )
ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
THUMB( mov r8, sp )
THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr
mrs r8, spsr @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC] @ Save calling PC
str r8, [sp, #S_PSR] @ Save CPSR
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
zero_fp
/*
* Get the system call
number. #取出系统调用号
*/
#if defined(CONFIG_OABI_COMPAT)
/*
* If we have CONFIG_OABI_COMPAT then we need to look at the swi
* value to determine if it is an EABI or an old ABI call.
*/
#ifdef CONFIG_ARM_THUMB
tst r8, #PSR_T_BIT
movne r10, #0 @ no thumb OABI emulation
ldreq r10, [lr, #-4] @ get SWI instruction
#else
ldr r10, [lr, #-4] @ get SWI instruction
A710( and ip, r10, #0x0f000000 @ check for SWI )
A710( teq ip, #0x0f000000 )
A710( bne .Larm710bug )
#endif
#ifdef CONFIG_CPU_ENDIAN_BE8
rev r10, r10 @ little endian instruction
#endif
#elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
A710( ldr ip, [lr, #-4] @ get SWI instruction )
A710( and ip, ip, #0x0f000000 @ check for SWI )
A710( teq ip, #0x0f000000 )
A710( bne .Larm710bug )
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
ldreq scno, [lr, #-4]
#else
/* Legacy ABI only. */
ldr scno, [lr, #-4] @ get SWI instruction
A710( and ip, scno, #0x0f000000 @ check for SWI )
A710( teq ip, #0x0f000000 )
A710( bne .Larm710bug )
#endif
#ifdef CONFIG_ALIGNMENT_TRAP
ldr ip, __cr_alignment
ldr ip, [ip]
mcr p15, 0, ip, c1, c0 @ update control register
#endif
enable_irq
get_thread_info tsk
adr tbl, sys_call_table @
load syscall table pointer #获取系统调用表的基地址
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
#if defined(CONFIG_OABI_COMPAT)
/*
* If the swi argument is zero, this is an EABI call and we do
nothing.
*
* If this is an old ABI call, get the syscall number into scno and
* get the old ABI syscall table address.
*/
bics r10, r10, #0xff000000
eorne scno, r10, #__NR_OABI_SYSCALL_BASE
ldrne tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)
bic scno, scno, #0xff000000 @ mask off SWI op-code
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#endif
stmdb sp!, {r4, r5} @ push fifth and sixth args
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
bne __sys_trace
cmp scno, #NR_syscalls @ check upper syscall limit
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl
#2] @ call sys_* routine #跳到系统调用函数
add r1, sp, #S_OFF
2: mov why, #0 @ no longer a real syscall
cmp scno, #(__ARM_NR_BASE – __NR_SYSCALL_BASE)
eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back
bcs arm_syscall
b sys_ni_syscall @ not private func
从上面可以看出,当CPU从中断向量表转到vector_swi
之后,完成了几件事情:1.取出系统调用号
2.根据系统调用号取出系统调用函数在系统调用表的基地址,得到一个系统调用函数的函数指针
3.
根据系统调用表的基地址和系统调用号,得到这个系统调用表里的项,每一个表项都是一个函数指针,把这个函数指针赋给PC
, 则实现了跳转到系统调用函数。
系统调用表定义在:arch/arm/kernel/Calls.S
* This program is free software; you can redistribute it and/or
modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This file is included thrice in entry-common.S
*/
/* 0 */ CALL(sys_restart_syscall)
CALL(sys_exit)
CALL(sys_fork_wrapper)
CALL(sys_read)
CALL(sys_write)
/* 5 */ CALL(sys_open)
CALL(sys_close)
CALL(sys_ni_syscall) /* was sys_waitpid */
CALL(sys_creat)
CALL(sys_link)
/* 10 */ CALL(sys_unlink)
CALL(sys_execve_wrapper)
CALL(sys_chdir)
CALL(OBSOLETE(sys_time)) /* used by libc4 */
CALL(sys_mknod)
/* 15 */ CALL(sys_chmod)
CALL(sys_lchown16)
CALL(sys_ni_syscall) /* was sys_break */
CALL(sys_ni_syscall) /* was sys_stat */
CALL(sys_lseek)
/* 20 */ CALL(sys_getpid)
CALL(sys_mount)
CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */
CALL(sys_setuid16)
CALL(sys_getuid16)
/* 25 */ CALL(OBSOLETE(sys_stime))
CALL(sys_ptrace)
CALL(OBSOLETE(sys_alarm)) /* used by libc4 */
CALL(sys_ni_syscall) /* was sys_fstat */
CALL(sys_pause)
/* 30 */ CALL(OBSOLETE(sys_utime)) /* used by libc4 */
CALL(sys_ni_syscall) /* was sys_stty */
CALL(sys_ni_syscall) /* was sys_getty */
CALL(sys_access)
CALL(sys_nice)
/* 35 */ CALL(sys_ni_syscall) /* was sys_ftime */
CALL(sys_sync)
CALL(sys_kill)
CALL(sys_rename)
CALL(sys_mkdir)
/* 40 */ CALL(sys_rmdir)
CALL(sys_dup)
CALL(sys_pipe)
CALL(sys_times)
CALL(sys_ni_syscall) /* was sys_prof */
/* 45 */ CALL(sys_brk)
CALL(sys_setgid16)
CALL(sys_getgid16)
CALL(sys_ni_syscall) /* was sys_signal */
CALL(sys_geteuid16)
/* 50 */ CALL(sys_getegid16)
CALL(sys_acct)
CALL(sys_umount)
CALL(sys_ni_syscall) /* was sys_lock */
CALL(sys_ioctl)
/* 55 */ CALL(sys_fcntl)
…….
CALL(sys_eventfd2)
CALL(sys_epoll_create1)
CALL(sys_dup3)
CALL(sys_pipe2)
/* 360 */ CALL(sys_inotify_init1)
CALL(sys_preadv)
CALL(sys_pwritev)
CALL(sys_rt_tgsigqueueinfo)
CALL(sys_perf_event_open)
CALL(sys_pk)
#我自己加的系统调用
了解了一个系统调用的执行过程就可以试着添加一个自己的系统调用了:
内核:
1. 在内核代码实现一个系统调用函数
即 sys_xxx()函数,如我在 kernel/printk.c 中添加了
void pk()
{
printk(KERN_WARNING”this is my first sys call !n”);
}
2. 添加系统调用号
在
arch/arm/include/asm/Unistd.h
添加 #define __NR_pk (__NR_SYSCALL_BASE+365)
3. 添加调用函数指针列表
在arch/arm/keenel/Calls.S添加
CALL(sys_pk)
4. 声明自己的系统调用函数
在include/linux/syscall.h添加asmlinkage
long sys_pk()
用户空间:
void pk()
{
__asm__(
”ldr r7 =365 n”
”swi n”
:
:
:
);
}
int main()
{
pk();
retrun 0;
}
完成上面的编写以后就可以编译内核和应用程序了。
将生成的文件在arm开发板上运行可以打印出: This is my first sys call!
说明我添加的系统调用可以使用。
至此,描述系统调用的实现机制和添加一个新的系统调用就完成了。
3 添加自己的系统调用
了解了一个系统调用的执行过程就可以试着添加一个自己的系统调用了:
内核:
1. 在内核代码实现一个系统调用函数
即
sys_xxx()函数,如我在 kernel/printk.c 中添加了
void pk()
{
printk(KERN_WARNING”this
is my first sys call !n”);
}
2. 添加系统调用号
在 arch/arm/include/asm/Unistd.h添加 #define __NR_pk
(__NR_SYSCALL_BASE+365)
3. 添加调用函数指针列表
在arch/arm/keenel/Calls.S添加 CALL(sys_pk)
4. 声明自己的系统调用函数
在include/linux/syscall.h添加asmlinkage long sys_pk()
用户空间:
void pk()
{
__asm__(
”ldr r7 =365
n”
”swi n”
:
:
:
);
}
int main()
{
pk();
retrun 0;
}
完成上面的编写以后就可以编译内核和应用程序了。
将生成的文件在arm开发板上运行可以打印出: This
is my first sys call!
说明我添加的系统调用可以使用。
至此,描述系统调用的实现机制和添加一个新的系统调用就完成了。
标签:linux, 电子游戏平台