linux系统调用

标签: 系统调用

  1. 概述

本文学习基于armv7和armv8体系的linux系统调用机制,linux内核版本为3.10.79。通过分析系统调用机制和源代码来展示系统调用过程。

  1. 什么是系统调用

linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用很相似,仅仅是系统调用由操作系统核心提供,执行于核心态。而普通的函数调用由函数库或用户自己提供。执行于用户态。

通常系统调用过程如下:

用户程序------>C库(即API):软中断 ----->system_call------->系统调用服务函数------->内核程序。比如,  用户程序打开一个文件------>C库open:中断----->system_call------->sys_open------>内核程序------>返回到用户程序。

http://hi.csdn.net/attachment/201112/2/0_1322832443IQlL.gif

注:图片来自网络

应用编程接口(API)与系统调用的不同在于,前者只是一个函数定义,说明了如何获得一个给定的服务,而后者是通过软件中断向内核发出的一个明确的请求。POSIX标准针对API,而不针对系统调用。Unix系统给程序员提供了很多API库函数。libc的标准c库所定义的一些API引用了封装例程(wrapper routine)(其唯一目的就是发布系统调用)。通常情况下,每个系统调用对应一个封装例程,而封装例程定义了应用程序使用的API。反之则不然,一个API没必要对应一个特定的系统调用。从编程者的观点看,API和系统调用之间的差别是没有关系的:唯一相关的事情就是函数名、参数类型及返回代码的含义。然而,从内核设计者的观点看,这种差别确实有关系,因为系统调用属于内核,而用户态的库函数不属于内核。

  1. 系统调用意义

用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以直接在内核的地址空间上读写的话,系统安全就会失去控制。所以,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序来执行该系统调用了。

系统调用在用户空间进程和硬件设备之间加入了一个中间层。该层主要作用有三个:
(1) 它为用户空间提供了一种统一的硬件的抽象接口。

比方当须要读些文件的时候,应用程序就能够不去管磁盘类型和介质,甚至不用去管文件所在的文件系统究竟是哪种类型。
(2)系统调用保证了系统的稳定和安全。作为硬件设备和应用程序之间的中间人,内核能够基于权限和其它一些规则对须要进行的访问进行裁决。

举例来说,这样能够避免应用程序不对地使用硬件设备,窃取其它进程的资源,或做出其它什么危害系统的事情。
(3) 每一个进程都执行在虚拟系统中,而在用户空间和系统的其余部分提供这样一层公共接口,也是出于这样的考虑。假设应用程序能够任意访问硬件而内核又对此一无所知的话,就没法实现多任务和虚拟内存,当然也不可能实现良好的稳定性和安全性。在Linux中。系统调用是用户空间访问内核的惟一手段。除异常和中断外,它们是内核惟一的合法入口。

 

  1. 系统调用的实现

应用编程接口(API)内部实现向内核切换的机制。首先,API为系统调用设置参数,其中一个参数是系统调用编号,其它参数为API接口参数。参数设置完成后,程序执行“系统调用”指令。这个指令会导致一个异常使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序,它与硬件体系结构紧密相关,异常处理程序会根据系统调用编号跳转到系统调用函数地址,执行系统调用函数。

    1. 系统调用号

系统调用号来标识系统调用函数,区分不同调用函数,系统调用号是唯一的,独一无二的。用户态向内核态切换方式都是一样的,所以必须通过这个系统调用号作为参数,使内核能够识别用户态想调用哪个系统调用函数。

      1. ArmV7

文件/arch/arm/kernel/calls.S中定义了系统调用表,文件\arch\arm\include\uapi\asm\unistd.h中定义了系统调用号,编号从0开始,arm 32位系统中一个表项占用4个字节,地址为基地址+编号*4(编号左移2位),另外定义了编号的上限,当编号不在范围内时,调用,直接返回错误号。

系统调用号:

\arch\arm\include\uapi\asm\unistd.h


#if defined(__thumb__) || defined(__ARM_EABI__)

#define __NR_SYSCALL_BASE 0

#else

#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE

#endif


/*

 * This file contains the system call numbers.

 */


#define __NR_restart_syscall         (__NR_SYSCALL_BASE+  0)

#define __NR_exit                  (__NR_SYSCALL_BASE+  1)

#define __NR_fork                 (__NR_SYSCALL_BASE+  2)

#define __NR_read                 (__NR_SYSCALL_BASE+  3)

#define __NR_write                        (__NR_SYSCALL_BASE+  4)

#define __NR_open                        (__NR_SYSCALL_BASE+  5)

#define __NR_close                        (__NR_SYSCALL_BASE+  6)


/arch/arm/kernel/calls.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)

 

      1. ArmV8

文件:arch\arm64\kernel\syc.c, arch\arm64\include\asm\ unistd.h,

arch\arm64\include\upai\asm\ unistd.h, include\asm-generic\ unistd.h,

include\upai\\asm-generic\ unistd.h

 

syc.c中:定义了sys_call_table,包含了几个头文件unistd.h,最终\upai\\asm-generic\ unistd.h添加了其它系统调用。

#define __SYSCALL(nr, sym)       [nr] = sym,

/*

 * The sys_call_table array must be 4K aligned to be accessible from

 * kernel/entry.S.

 */

注意这里数组初始化用法,第一次见。

void *sys_call_table[__NR_syscalls] __aligned(4096) = {

        [0 ... __NR_syscalls - 1] = sys_ni_syscall,

#include <asm/unistd.h>

};


include\upai\\asm-generic\ unistd.h中:

/* fs/read_write.c */

#define __NR3264_lseek 62

__SC_3264(__NR3264_lseek, sys_llseek, sys_lseek)

#define __NR_read 63

__SYSCALL(__NR_read, sys_read) //相当于[nr] = sym, [__NR_read] = sys_read

#define __NR_write 64

__SYSCALL(__NR_write, sys_write)

#define __NR_readv 65

__SC_COMP(__NR_readv, sys_readv, compat_sys_readv)

#define __NR_writev 66

__SC_COMP(__NR_writev, sys_writev, compat_sys_writev)

#define __NR_pread64 67

__SC_COMP(__NR_pread64, sys_pread64, compat_sys_pread64)

#define __NR_pwrite64 68

__SC_COMP(__NR_pwrite64, sys_pwrite64, compat_sys_pwrite64)

#define __NR_preadv 69

__SC_COMP(__NR_preadv, sys_preadv, compat_sys_preadv)

#define __NR_pwritev 70

__SC_COMP(__NR_pwritev, sys_pwritev, compat_sys_pwritev)

 

    1. 系统调用参数传递

API接口中有参数,如ssize_t write (int fd, const void * buf, size_t count); 参数是通过寄存器传递到内核的。

armv7:通过寄存器r1-r5传递函数参数, r0存放函数返回值,r7存放系统调用号

armv8 aarch64:通过寄存器x1-x6传递函数参数,x0存放函数返回值,x8存放系统调用号。

文件:glibc中\sysdeps\unix\sysv\linux\arm\sysdep.h, \sysdeps\unix\ sysdep.h。

后面系统调用举例章节会详细描述。

    1. 中断指令

用户态通过执行中断指令切换到内核态,

armv7:swi 0

armv8 aarch64:svc 0

文件:glibc中\sysdeps\unix\sysv\linux\arm\sysdep.h, \sysdeps\unix\ sysdep.h

后面系统调用举例章节会详细描述。

    1. 系统调用中断处理程序
      1. Armv7

文件:linux/arch/arm/kernel/ entry-common.S

分析主要代码,vector_swi为swi中断处理函数,swi指令后,cpu会自动跳转到中断向量进行处理。关于中断,后续中断学习中再解析。

  1. 中断向量入口函数
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//这里^表示被备份的sp lr 是usr_mode的寄存器不是目前所在svc_mode的寄存器

 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


//把系统调用表指针存在tbl中

        adr   tbl, sys_call_table           @ load syscall table pointer


#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 //把r7中的中断号存储到scno变量中

 USER(   ldreq        scno, [lr, #-4]           )


local_restart:

        ldr    r10, [tsk, #TI_FLAGS]           @ check for syscall tracing

        stmdb      sp!, {r4, r5}                      @ push fifth and sixth args //把参数所在寄存器r4,r5入栈,因为ATPCS,参数传递只能使用r0-r3,所以r4,r5通过栈传递


        cmp scno, #NR_syscalls         @ check upper syscall limit //检查是否超过了最大调用号

        adr   lr, BSYM(ret_fast_syscall)     @ return address //设置返回地址到lr

        ldrcc pc, [tbl, scno, lsl #2]                @ call sys_* routine // scno小于NR_syscalls执行此指令,跳转到系统调用函数地址,tbl+scno*4,系统调用表首地址+调用号*4,每个指针占4字节


   // scno大于等于NR_syscalls执行如下指令

        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 //系统调用号错误处理函数


ENDPROC(vector_swi)

 

      1. ArmV8

只解析64位运行模式。

文件arch\arm64\kernel\ entry.S:

中断向量表:

ENTRY(vectors)

        ventry      el1_sync_invalid             // Synchronous EL1t

        ventry      el1_irq_invalid                        // IRQ EL1t

        ventry      el1_fiq_invalid                        // FIQ EL1t

        ventry      el1_error_invalid            // Error EL1t


        ventry      el1_sync                  // Synchronous EL1h

        ventry      el1_irq                              // IRQ EL1h

        ventry      el1_fiq                              // FIQ EL1h

        ventry      el1_error_invalid            // Error EL1h


        ventry      el0_sync                  // Synchronous 64-bit EL0  //svc为同步异常,中断向量处理,来自el0

        ventry      el0_irq                              // IRQ 64-bit EL0

        ventry      el0_fiq                              // FIQ 64-bit EL0

        ventry      el0_error_invalid            // Error 64-bit EL0

…

END(vectors)

 

//同步异常处理函数,svc指令,data abort, instruct abort等都是同步异常,这里需要判断哪种异常,关于中断,详细分析看中断学习笔记。

el0_sync:

        kernel_entry 0 //保存寄存器,状态

        msr daifclr, #1                          //enable fiq

        mrs  x25, esr_el1                     // read the syndrome register

        lsr    x24, x25, #ESR_EL1_EC_SHIFT // exception class

        cmp x24, #ESR_EL1_EC_SVC64         // SVC in 64-bit state

        b.eq el0_svc


/*

 * SVC handler.

 */

        .align       6

el0_svc:

        adrp stbl, sys_call_table          // load syscall table pointer 系统调用表

        uxtw        scno, w8                   // syscall number in w8 系统调用号X8寄存器

        mov sc_nr, #__NR_syscalls

el0_svc_naked:                                         // compat entry point

        stp   x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number

        disable_step x16

        isb

        enable_dbg

        enable_irq


        get_thread_info tsk

        ldr    x16, [tsk, #TI_FLAGS]          // check for syscall hooks

        tst    x16, #_TIF_SYSCALL_WORK

        b.ne __sys_trace

        adr   lr, ret_fast_syscall           // return address 返回地址

        cmp     scno, sc_nr                     // check upper syscall limit

        b.hs ni_sys

        ldr    x16, [stbl, scno, lsl #3]   // address in the syscall table 计算系统调用函数地址,基地址+系统调用号*8, 64位是8字节存放指针长度

        br     x16                           // call sys_* routine //跳转到系统调用

ni_sys:

        mov x0, sp

        b      do_ni_syscall  //系统调用号错误处理函数

ENDPROC(el0_svc)

 

 

 

 

 

 

    1. 系统调用函数

Armv7系统函数调用表在calls.S中,以sys_开头,具体定义方式在SYSCALL_DEFINE进行定义,举例如下,通过SYSCALL_DEFINE3定义,这里不在介绍具体定义方式。Armv8参考章节5.1。

函数执行完返回执行结果。

SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)

{

        struct fd f = fdget(fd);

        ssize_t ret = -EBADF;


        if (f.file) {

                 loff_t pos = file_pos_read(f.file);

                 ret = vfs_read(f.file, buf, count, &pos);

                 file_pos_write(f.file, pos);

                 fdput(f);

        }

        return ret;

}

 

 

    1. 系统调用返回

系统调用函数返回结果一般成功是0,失败返回一个负值,并且把错误码放到全局变量errno中。返回值放在r0寄存器中。

      1. Armv7

文件:linux/arch/arm/kernel/ entry-common.S

返回时执行ret_fast_syscall,

ret_fast_syscall:

 UNWIND(.fnstart )

 UNWIND(.cantunwind )

        disable_irq                               @ disable interrupts

        ldr    r1, [tsk, #TI_FLAGS]

        tst    r1, #_TIF_WORK_MASK

        bne  fast_work_pending

        asm_trace_hardirqs_on


        /* perform architecture specific actions before user return */

        arch_ret_to_user r1, lr

        ct_user_enter


        restore_user_regs fast = 1, offset = S_OFF  //回复各寄存器后返回到用户空间

 UNWIND(.fnend          )


        .macro     restore_user_regs, fast = 0, offset = 0

        ldr    r1, [sp, #\offset + S_PSR]       @ get calling cpsr

        ldr    lr, [sp, #\offset + S_PC]! @ get pc

        msr  spsr_cxsf, r1                    @ save in spsr_svc

#if defined(CONFIG_CPU_V6)

        strex r1, r2, [sp]                        @ clear the exclusive monitor

#elif defined(CONFIG_CPU_32v6K)

        clrex                                  @ clear the exclusive monitor

#endif

        .if     \fast

        ldmdb      sp, {r1 - lr}^                     @ get calling r1 - lr

        .else

        ldmdb      sp, {r0 - lr}^                     @ get calling r0 - lr

        .endif

        mov r0, r0                                 @ ARMv5T and earlier require a nop

                                                  @ after ldm {}^

        add  sp, sp, #S_FRAME_SIZE - S_PC

        movs        pc, lr                                 @ return & move spsr_svc into cpsr

        .endm

      1. Armv8

文件arch\arm64\kernel\ entry.S:系统调用返回时执行ret_fast_syscall。

ret_fast_syscall:

        disable_irq                               // disable interrupts

        ldr    x1, [tsk, #TI_FLAGS]

        and  x2, x1, #_TIF_WORK_MASK

        cbnz x2, fast_work_pending

        tbz   x1, #TIF_SINGLESTEP, fast_exit

        disable_dbg

        enable_step x2

fast_exit:

        kernel_exit 0, ret = 1



.macro     kernel_exit, el, ret = 0

        ldp   x21, x22, [sp, #S_PC]             // load ELR, SPSR // ELR为返回用户空间,执行的地址

        .if     \el == 0

        ldr    x23, [sp, #S_SP]             // load return stack pointer

#ifdef CONFIG_ARM64_ERRATUM_845719

        tbz   x22, #4, 1f

#ifdef CONFIG_PID_IN_CONTEXTIDR

        mrs         x29, contextidr_el1

        msr         contextidr_el1, x29

1:

#else

        msr         contextidr_el1, xzr

1:

#endif

#endif

        .endif

        .if     \ret

        ldr    x1, [sp, #S_X1]                       // preserve x0 (syscall return)

        add  sp, sp, S_X2

        .else

        pop  x0, x1

        .endif

        pop  x2, x3                               // load the rest of the registers

        pop  x4, x5

        pop  x6, x7

        pop  x8, x9

        msr  elr_el1, x21                      // set up the return data

        msr  spsr_el1, x22

        .if     \el == 0

        msr  sp_el0, x23

        .endif

        pop  x10, x11

        pop  x12, x13

        pop  x14, x15

        pop  x16, x17

        pop  x18, x19

        pop  x20, x21

        pop  x22, x23

        pop  x24, x25

        pop  x26, x27

        pop  x28, x29

        ldr    lr, [sp], #S_FRAME_SIZE - S_LR // load LR and restore SP //LR为在用户空间函数跳转时保存的地址。而ELR是异常发生切换时,保存的地址。

        eret                                    // return to kernel

        .endm

 

  1. 系统调用举例

write为例,分析调用过程。

    1. 用户空间
      1. Armv7

用户程序调用write();

ssize_t write (int fd, const void * buf, size_t count); 

文件:glibc中\sysdeps\unix\sysv\linux\arm\sysdep.h, \sysdeps\unix\ sysdep.h

\sysdeps\unix\sysv\linux\arm\sysdep.h,write.c

  1. Write->__write(__libc_write)-> SYSCALL_CANCEL

ssize_t

__libc_write (int fd, const void *buf, size_t nbytes)

{

  return SYSCALL_CANCEL (write, fd, buf, nbytes); //3个参数转化为4个,添加了函数名write

}

libc_hidden_def (__libc_write)

weak_alias (__libc_write, __write)

 

  1. SYSCALL_CANCEL-> INLINE_SYSCALL_CALL->__INLINE_SYSCALL4-> INLINE_SYSCALL

INLINE_SYSCALL_CALL宏定义会根据参数个数转换为_INLINE_SYSCALL4

#define SYSCALL_CANCEL(...) \

  ({                                                                         \

    long int sc_ret;                                                       \

    if (SINGLE_THREAD_P)                                                   \

      sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__);                       \

    else                                                                  \

      {                                                                          \

        int sc_cancel_oldtype = LIBC_CANCEL_ASYNC ();                       \

        sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__);                            \

        LIBC_CANCEL_RESET (sc_cancel_oldtype);                                  \

      }                                                                          \

    sc_ret;                                                                     \

  })

 

#define __INLINE_SYSCALL4(name, a1, a2, a3, a4) \

  INLINE_SYSCALL (name, 4, a1, a2, a3, a4)

 

#define __INLINE_SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n

#define __INLINE_SYSCALL_NARGS(...) \

  __INLINE_SYSCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)

#define __INLINE_SYSCALL_DISP(b,...) \

  __SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) //转换,如果有4个参数,那么__VA_ARGS__占用a,b,c  那n的位置正好是3,所以__INLINE_SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n 计算后为(write, fd, buf, nbytes,7,6,5,4, 3,2,1,0,...)  4

 

#define INLINE_SYSCALL_CALL(...) \

  __INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__)

 

 

  1. INLINE_SYSCALL-> INTERNAL_SYSCALL-> INTERNAL_SYSCALL_RAW

#define INLINE_SYSCALL(name, nr, args...)                               \

  ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args);        \

 

#define INTERNAL_SYSCALL(name, err, nr, args...)         \

        INTERNAL_SYSCALL_RAW(SYS_ify(name), err, nr, args) // SYS_ify计算系统调用号

 

 

  1. # define INTERNAL_SYSCALL_RAW(name, err, nr, args...)             \

  ({                                                            \

       register int _a1 asm ("r0"), _nr asm ("r7");               \//定义变量_a1,_nr存放在寄存器r0r7中,

       LOAD_ARGS_##nr (args)                                        \  //加载参数args寄存器中,nr为个数

       _nr = name;                                                 \ //存放系统号

       asm volatile ("swi     0x0  @ syscall " #name  \//swi指令,切换到内核

                      : "=r" (_a1)                             \ //输出,返回值放在r0

                      : "r" (_nr) ASM_ARGS_##nr                        \ //变量参数

                      : "memory");                          \  //告诉编译器,这段内嵌式汇编代码,所有变量通过内存获取或存储,不使用缓存

       _a1; })

 

      1. Armv8

接口和调用过程一样,只是最后中断指令和使用寄存器不同,armv8如下,

 

# define INTERNAL_SYSCALL_RAW(name, err, nr, args...)             \

  ({ long _sys_result;                                              \

     {                                                               \

       LOAD_ARGS_##nr (args)                                        \//参数存储在x1-x5

       register long _x8 asm ("x8") = (name);                     \//x8存储系统调用号

       asm volatile ("svc     0      // syscall " # name     \//中断指令svc,切换到内核

                      : "=r" (_x0) : "r"(_x8) ASM_ARGS_##nr : "memory");     \

       _sys_result = _x0;                                      \

     }                                                               \

     _sys_result; })

 

    1. 内核空间

内核空间执行,前面已经分析了,

中断后,cpu跳转到中断向量处理函数vector_swi,之后查找系统调用表,跳转到asmlinkage long sys_write(unsigned int fd, const char __user *buf,

                           size_t count);

执行完,返回用户空间。

Armv8类似,前面章节已分析。

 

 

  1. 如何添加系统调用

系统调用设计原则保持函数通用性,简洁性,可移植性,健壮性。

    1. Armv7
      1. 定义系统调用函数,

放在相关功能的.c文件中,如文件相关的在fs/read_write.c等

SYSCALL_DEFINE3(write_example, unsigned int, fd, const char __user *, buf,

                 size_t, count)

{

……

…….

        return ret;

}

SYSCALL_DEFINE 使用与参数个数有关,n个参数就使用SYSCALL_DEFINEn,

SYSCALL_DEFINEn(xxx)中第一个参数为函数名,后面是函数参数内容,这里参数类型和变量用逗号隔开。

      1. 添加系统调用声明

在相关.h文件添加声明,通用函数一般在kernel\linux-3.10.y\include\linux\syscalls.h中添加声明,与体系相关的对应体系目录下syscalls.h文件中添加声明

asmlinkage long sys_ write_example (unsigned int fd, const char __user *buf,

                           size_t count);

 

      1. 添加系统调用号

在体系相关头文件unistd.h添加系统调用号

如armv7:\arch\arm\include\uapi\asm\unistd.h 最后添加系统调用号

#define __NR_finit_module           (__NR_SYSCALL_BASE+379)

#define __NR_seccomp                  (__NR_SYSCALL_BASE+383)

#define __NR_ write_example                        (__NR_SYSCALL_BASE+384)

 

      1. 添加系统调用表项

在体系相关文件中添加系统调用表

如armv7:/arch/arm/kernel/calls.S

/* 380 */  CALL(sys_ni_syscall)            /* reserved sys_sched_setattr */

                 CALL(sys_ni_syscall)            /* reserved sys_sched_getattr */

                 CALL(sys_ni_syscall)            /* reserved sys_renameat2     */

                 CALL(sys_seccomp)

CALL(sys_ write_example)

至此系统调用添加完成;

      1. 系统调用访问

一种方法是添加到glibc库中,作为一个库函数,添加方式参考章节6.另一种方式使用库函数syscall函数直接调用,如ret = syscall(系统调用号,参数….)

版权声明:本文为qq_45683435原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_45683435/article/details/101387586