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系统调用过程的更多相关文章

  1. arm Linux 系统调用过程

    系统调用是操作系统提供给用户(应用程序)的一组接口,每个系统调用都有一个对应的系统调用函数来完成相应的工作.用户通过这个接口向操作系统申请服务,如访问硬件,管理进程等等.但是因为用户程序运行在用户空间 ...

  2. Linux 系统调用过程详细分析

    内核版本:Linux-4.19 操作系统通过系统调用为运行于其上的进程提供服务. 那么,在应用程序内,调用一个系统调用的流程是怎样的呢? 我们以一个假设的系统调用 xyz() 为例,介绍一次系统调用的 ...

  3. [Linux]系统调用理解(1)

    本文是Linux系统调用专栏系列文章的第一篇,对Linux系统调用的定义.基本原理.使用方法和注意事项大概作了一个介绍,以便读者对Linux系统调用建立一个大致的印象. 什么是系统调用? Linux内 ...

  4. 别出心裁的Linux系统调用学习法

    别出心裁的Linux系统调用学习法 操作系统与系统调用 操作系统(Operating System,简称OS)是计算机中最重要的系统软件,是这样的一组系统程序的集成:这些系统程序在用户对计算机的使用中 ...

  5. 关于Linux系统调用,内核函数【转】

    转自:http://blog.csdn.net/ubuntulover/article/details/5988220 早上听人说到某个程序的一部分是内核态,另一部分是用户态,需要怎么怎么.当时突然想 ...

  6. Linux系统调用(转载)

    目录: 1. Linux系统调用原理 2. 系统调用的实现 3. Linux系统调用分类及列表 4.系统调用.用户编程接口(API).系统命令和内核函数的关系 5. Linux系统调用实例 6. Li ...

  7. linux内核剖析(六)Linux系统调用详解(实现机制分析)

    本文介绍了系统调用的一些实现细节.首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系.然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递 ...

  8. 使用 Linux 系统调用的内核命令【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-system-calls/ 探究 SCI 并添加自己的调用 Linux® 系统调用 —— 我们每天都在使 ...

  9. [svc]linux启动过程及级别

    Unix目录结构的来历 Linux 的启动流程 Linux 引导过程内幕 嵌入式系统 Boot Loader 技术内幕 centos6使用chkconfig治理服务和其原理 centos7的服务治理- ...

随机推荐

  1. MyBatis_延迟加载01

    一.延迟加载 MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时, 按照设置延迟规则推迟对关联对象的select查询.延迟加载可以有效的减少数据库压力. MyBatis的延迟加载只是对关联 ...

  2. 《跟我学IDEA》三、实用配置(行号、提示、代码等)

    上一篇博文我们介绍了idea如何配置一个maven,git,tomcat等,这一篇我们来进行一些常用设置,这些也正是idea可爱之处,大大提高了开发的效率. 第一节:idea常用配置显示行数.显示方法 ...

  3. final关键字细节

    final关键字在java中是一个很重要的关键字,其实按照其字面意思理解,就可以一窥这个关键字端倪,final的本意是最终的.所谓最终的,其最重要的特征就是不能修改,由此衍生出的许多细节均应以这个特征 ...

  4. Apache反向代理的配置

    Apache反向代理的配置 一: Mac系统自带apache服务器 1. 查看apache版本命令如下:   sudo apachectl -v 2. 启动apache   sudo apachect ...

  5. 苹果审核返回崩溃日志 iOS .crash文件处理 symbolicatecrash.  困扰我多年的牛皮癣根治了 看到这篇文章拿过来用下

    AppStore审核被拒,返回crashLog.txt文件,可是打开后都是十六进制的地址,我们可以使用Xcode自带的 symbolicatecrash 解析得到我们需要的详细崩溃信息crashLog ...

  6. 随笔:JavaScript函数中的对象----arguments

    关于arguments 调用函数时,如果需要传参,其实参数就是一个数组,在函数体的内置对象arguments可以访问这个数组,如: arguments[0]:第一个参数 arguments[1]:第二 ...

  7. JavaScript--AJAX页面传值

    1.首先 闲话不说 直接代码走起,都是我工作闲事的积累干货 //重要 js 运行 $(function (){ 代码 }); 2.ajax 传值 //第一种 输入框 <input type=&q ...

  8. 安装卸载selenium

    安装: C:\Users\xiongjiawei>pip install selenium==2.48.0 Collecting selenium==2.48.0 Retrying (Retry ...

  9. iOS 科学计数法保留N位有效数字

    iOS开发 项目中用到了将一个很大的数值转换成科学计数法的需求,转换成科学计数法的方式在iOS中其实是很好做的,使用NSNumber 的 kCFNumberFormatterScientificSty ...

  10. OC学习15——文件I/O体系

    OC提供了丰富的I/O相关API,如果只是管理文件和目录,程序可以使用NSFileManager进行管理,包括创建.删除.移动和复制文件等:如果程序需要读取文件内容,则可通过NSFileHandle进 ...