您的位置:首页 > 移动开发 > Android开发

android基于Socket的系统调用实现

2011-10-17 10:44 253 查看
android基于Socket的系统调用实现

声明:该文件为本人原创,如转载修改及使用其中图片,请注明出处及原作者。

Author:lanbo(高兆成)

E-mail:lanbo_g@126.com

如有任何疑问可留言或E-mail

系统调用就是用户空间应用程序和内核提供的服务之间的接口。服务是由linux内核提供的,无法直接调用。因此必须使用一个进程来跨越用户空间和内核之间的界限。

今天我们就将从用户层通过socket来分析linux下的系统调用的实现过程。

通过该文章读者可熟悉系统调用的实现



以wpa_supplicant中driver_wext中的socket为例来分析:

要想通过wext与kernel沟通wpa_supplicant中是在wpa_driver_wext_init函数中通过创建socket来实现

Socket定义在bionic/libc/arch-arm/syscalls/socket.S中如下:

#include <sys/linux-syscalls.h>

.text

.type socket, #function

.globl socket

.align 4

.fnstart

socket:

.save {r4, r7}

stmfd sp!, {r4, r7}

ldr r7, =__NR_socket//此处将__NR_socket放入到ARM R7中

swi #0//调用系统中断

ldmfd sp!, {r4, r7}

movs r0, r0

bxpl lr

b __set_syscall_errno

.fnend

__NR_socket 在几个文件中都有定义,我就不确认是调用的kernel/arch/arm/include/asm/Unistd.h还是
ndk/build/platforms/android-8/arch-arm/usr/include/sys/Linux-syscalls.h
#if !defined__ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H

#if defined__arm__ && !defined __ARM_EABI__ && !defined __thumb__

# define __NR_SYSCALL_BASE 0x900000

#else

# define __NR_SYSCALL_BASE 0

#endif

………………………省略号……………….

#define __NR_socket (__NR_SYSCALL_BASE +281)

如上调用了swi(软中断) ,接下来我们看看中断向量实现。

在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的对应关系如下:

V=0:0x00000000~0x0000001C

V=1:0xffff0000~0xffff001C

arch/arm/mm/proc-arm920.S中

.section".text.init", #alloc, #execinstr

__arm920_setup:

……orr r0, r0,#0x2100 @ ..1. ...1 ..11 ...1

//bit13=1 中断向量表基址为0xFFFF0000。R0的值将被付给CP15的C1.

中断向量在early_trap_init中定义,调用顺序如下:

start_kernel(kernel/init/main.c)==>
setup_arch(kernel/arch/arm/kernel/Setup.c)==>early_trap_init(kernel/arch/arm/kernel/Traps.c)

early_trap_init部分代码如下:

unsigned longvectors = CONFIG_VECTORS_BASE;//定义中断向量起始地址

//#defineCONFIG_VECTORS_BASE 0xffff0000定义在kernel/include/linux/Autoconf.h

extern char__stubs_start[], __stubs_end[];

extern char__vectors_start[], __vectors_end[];

//如下做中断向量的搬移动作,为保护模式准备,如上调用swi后PC指针会指向vectors +address 0x00000008。

memcpy((void*)vectors, __vectors_start, __vectors_end -__vectors_start);

memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

memcpy((void *)vectors + 0x1000 - kuser_sz,__kuser_helper_start, kuser_sz);

其中__stubs_start[], __stubs_end[],__vectors_start[], __vectors_end[]在kernel/arch/arm/kernel/dntry-armv.S中定义,我们来分析看看如何实现。

.macro vector_stub,name, mode, correction=0//此处定义了一个vector_stub宏定义
.align 5

vector_\name:
.if \correction
sub lr,lr, #\correction
.endif

@
@ Save r0, lr_<exception> (parentPC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp,{r0, lr} @ save r0, lr
mrs lr,spsr
str lr,[sp, #8] @ save spsr

@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0,cpsr
eor r0,r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf,r0

@
@ the branch table must immediatelyfollow this code
@
and lr,lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0,sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc,lr @ branch tohandler in SVC mode
ENDPROC(vector_\name)

………………………………省略号………………………………………………….

.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//未定义异常时,CPU将执行这条指令

W(ldr) pc, .LCvswi + stubs_offset//swi异常

W(b) vector_pabt + stubs_offset//指令预取中止

W(b) vector_dabt + stubs_offset//数据访问中止

W(b) vector_addrexcptn + stubs_offset//没有用到

W(b) vector_irq + stubs_offset//irq中断异常

W(b) vector_fiq + stubs_offset//fiq 快速中断异常

.globl __vectors_end

__vectors_end:

至于为何要加stubs_offset,请参考如下解释,我也没分析(引用于网上)

__stubs_end 至 __stubs_start之间是异常处理的位置。也位于文件arch/arm/kernel/entry-armv.S中。vector_und、vector_pabt、vector_irq、vector_fiq都在它们中间。

stubs_offset值如下:

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

stubs_offset是如何确定的呢?

当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量(±32M)写入指令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。我们把搬移前的中断向量表中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start,
vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0000处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断 向量中的中断入口地址的偏移量就是,200+vector_irq在stubs中的偏移量再减去中断入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC)
+vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇编器完成的,而后面的 vectors_start+200-stubs_start就应该是stubs_offset,实际上在entry-armv.S中也是这样定义的。

如上我们有看到有“vector_swi”(系统调用向量),其定义处为kernel/arch/arm/kernel/entry-common.S
其部分内容为:

.equ NR_syscalls,0
#defineCALL(x) .equ NR_syscalls,NR_syscalls+1
#include"calls.S"
#undefCALL
#defineCALL(x) .long x
………………………省略号………………………………………………
/*=============================================================================
* SWI handler
*-----------------------------------------------------------------------------
*/

/* If we're optimising for StrongARMthe resulting code won't
run on an ARM7 and we can save a couple of instructions.
--pb*/
#ifdefCONFIG_CPU_ARM710
#defineA710(code...) code
.Larm710bug:
ldmia sp,{r0 - lr}^ @ Getcalling r0 - lr
mov r0,r0
add sp,sp, #S_FRAME_SIZE
subs pc,lr, #4
#else
#defineA710(code...)
#endif

.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_lrr8, r10, S_SP ) @ calling sp, lr
mrs r8,spsr @ called fromnon-FIQ mode, so ok.
str lr,[sp, #S_PC] @ Savecalling PC
str r8,[sp, #S_PSR] @ Save CPSR
str r0,[sp, #S_OLD_R0] @ SaveOLD_R0
zero_fp

/*
* Get the system call number.
*/

#ifdefined(CONFIG_OABI_COMPAT)

/*
* If we have CONFIG_OABI_COMPAT then we needto look at the swi
* value to determine if it is an EABI or anold ABI call.
*/
#ifdefCONFIG_ARM_THUMB
tst r8,#PSR_T_BIT
movne r10,#0 @ nothumb OABI emulation
ldreq r10,[lr, #-4] @ getSWI instruction
#else
ldr r10,[lr, #-4] @ getSWI instruction
A710( and ip, r10, #0x0f000000 @ check for SWI )
A710( teq ip, #0x0f000000 )
A710( bne .Larm710bug )
#endif
#ifdefCONFIG_CPU_ENDIAN_BE8
rev r10,r10 @ little endianinstruction
#endif

#elifdefined(CONFIG_AEABI)

/*
* Pure EABI user space always put syscallnumber 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 )

#elifdefined(CONFIG_ARM_THUMB)

/* Legacy ABI only, possibly thumbmode. */
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 SWIinstruction
A710( and ip, scno, #0x0f000000 @ check for SWI )
A710( teq ip, #0x0f000000 )
A710( bne .Larm710bug )

#endif

#ifdefCONFIG_ALIGNMENT_TRAP
ldr ip,__cr_alignment
ldr ip,[ip]
mcr p15,0, ip, c1, c0 @ update controlregister
#endif
enable_irq

get_thread_info tsk
adr tbl, sys_call_table @load syscall table pointer
ldr ip,[tsk, #TI_FLAGS] @ check forsyscall tracing

#ifdefined(CONFIG_OABI_COMPAT)
/*
* If the swi argument is zero, this is an EABIcall and we do nothing.
*
* If this is an old ABI call, get the syscallnumber 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 SWIop-code
eor scno,scno, #__NR_SYSCALL_BASE @ check OS number
#endif

stmdb sp!,{r4, r5} @ pushfifth and sixth args
tst ip,#_TIF_SYSCALL_TRACE @ arewe tracing syscalls?
bne __sys_trace

cmp scno,#NR_syscalls @ check uppersyscall limit
adr lr,BSYM(ret_fast_syscall) @ returnaddress
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine

add r1,sp, #S_OFF
2: mov why,#0 @ nolonger a real syscall
cmp scno,#(__ARM_NR_BASE - __NR_SYSCALL_BASE)
eor r0,scno, #__NR_SYSCALL_BASE @ put OSnumber back
bcs arm_syscall
b sys_ni_syscall @ not private func
ENDPROC(vector_swi)
……………………………省略号……………………………………………………….

.type sys_call_table, #object

ENTRY(sys_call_table)

#include "calls.S"

#undef ABI

#undef OBSOLETE

还记得之前分析时系统调用入栈的__NR_socket (__NR_SYSCALL_BASE + 281)吗?在如上代码中再将其转换成对应的偏移值再通过调用sys_call_table+偏移调用指定系统调用。而在sys_call_table中先调将calls.S给include进来,这样我们通过偏移可得出我们系统调用对应的function 为CALL(sys_socket),其中CALL(X)如上也有定义。

sys_socket系统调用在kernel/include/linux/Syscalls.h中,

asmlinkage longsys_socket(int, int, int);

sys_socket原型如下:

kernel/net/Socket.c

SYSCALL_DEFINE3(socket,int, family, int, type, int, protocol)

{

retval = sock_create(family, type,protocol, &sock);//主要作用是创建socket,暂且先不深入了。

if (retval < 0)

goto out;

retval = sock_map_fd(sock, flags &(O_CLOEXEC | O_NONBLOCK));//该函数创建文件描述符并与socket关联。

if (retval < 0)

goto out_release;

out:

/* It may be already another descriptor8) Not kernel problem. */

return retval;//返回文件描述符

int sock_map_fd(struct socket *sock, int flags)

{

structfile *newfile;

intfd = sock_alloc_fd(&newfile, flags);// 结构分配一个空闲的文件描述符

if(likely(fd >= 0)) {

interr = sock_attach_fd(sock, newfile, flags);//关联文件描述符与socket

if(unlikely(err < 0)) {

put_filp(newfile);

put_unused_fd(fd);

returnerr;

}

fd_install(fd,newfile);

}

returnfd;

}

static int sock_attach_fd(struct socket*sock, struct file *file, int flags)

sock->file = file;//socket的file指针指向文件描述符

init_file(file, sock_mnt, dentry,FMODE_READ | FMODE_WRITE,

&socket_file_ops);//提供文件描述符接口,在文章“WEXTdriver的执行过程实现”会用到。

file->private_data =sock;//文件描述符的private_data指向socket

int init_file(struct file *file, struct vfsmount *mnt, struct dentry *dentry,

fmode_t mode, const struct file_operations *fop)

{

int error = 0;

file->f_path.dentry = dentry;

file->f_path.mnt = mntget(mnt);

file->f_mapping = dentry->d_inode->i_mapping;

file->f_mode = mode;

file->f_op = fop;以后上层调用ioctl等都会用到该接口。

至此终于能够执行到socket的系统调用了,以后我们再分析上层是如何调用到网络驱动的(wpa_supplicant及iwpriv)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: