现在的位置: 首页 > 综合 > 正文

android 下 socket 系统调用

2013年10月11日 ⁄ 综合 ⁄ 共 11576字 ⁄ 字号 评论关闭

    系统调用就是用户空间应用程序和内核提供的服务之间的接口。服务是由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)。


转自:http://blog.csdn.net/lanbo_g/article/details/6880448

抱歉!评论已关闭.