Linux系統調用過程

来源:http://www.cnblogs.com/linhaostudy/archive/2017/04/21/6742839.html
-Advertisement-
Play Games

1 系統調用的作用 系統調用是操作系統提供給用戶(應用程式)的一組介面,每個系統調用都有一個對應的系統調用函數來完成相應的工作。用戶通過這個介面向操作系統申請服務,如訪問硬體,管理進程等等。 應用程式和文件系統的介面是系統調用。 我們經常看到的比如fork、open、write 等等函數實際上並不是 ...


 

 

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!

說明我添加的系統調用可以使用。

至此,描述系統調用的實現機制和添加一個新的系統調用就完成了。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、Yum簡介 1、Yum(全稱為 Yellow dog Updater, Modified)是一個在Fedora和RedHat以及CentOS中的Shell前端軟體包管理器。 2、基於RPM包管理,能夠從指定的伺服器自動下載RPM包並且安裝,可以自動處理依賴性關係,並且一次安裝所有依賴的軟體包,無 ...
  • 在之前寫了Linux密碼破解的方法,雖然這樣對於忘記密碼時很方便,但同時別人也可以很輕易的破解你的Liunx虛擬機,安全問題存在隱患。 下麵給出一些Liunx密碼的安全防護操作: 1、防止破解root密碼(設置boot密碼) 之前已經瞭解到了破解root密碼的方法,其實就是進入boot的單用戶模式對 ...
  • 電鍍鎳金 其實電鍍金本身就可以分為硬金及軟金。因為電鍍硬金實際上就是合金,所以硬度會比較硬,適合用在需要受力摩擦的地方,在電子業,一般用來作為店路板的板邊接觸點(俗稱「金手指」);而軟金一般則用於COB(Chip On Board)上面打鋁線,或是手機按鍵的接觸面,近來則被大量運用在BGA載板的正反 ...
  • Linux web開發環境搭建完整過程。折騰了好幾天,無奈網速太差一直沒弄好,這次乾脆在宿舍搭建。 虛擬機上安裝CentOS 6 虛擬機使用的是VMware Workstation 12 Player(非商業用途免費),CentOS版本為6.8(推薦6.5+)。 分配的記憶體最好大於2G,不過不用擔心 ...
  • 信號量: 是用來解決進程/線程之間的同步和互斥問題的一種通信機制,是用來保證兩個或多個關鍵代碼不被併發調用。 信號量(Saphore)由一個值和一個指針組成,指針指向等待該信號量的進程。信號量的值表示相應資源的使用情況。信號量S>=0時,S表示可用資源的數量。執行一次P操作意味著請求分配一個資源,因 ...
  • RedHat靜態Ip地址配置 依次修改以下三個文件: /etc/sysconfig/network /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/resolv.conf 1./etc/sysconfig/network NETWORKING=yes N ...
  • samba服務的安裝及配置:可以在Linux中建議共用目錄給網路中的其他主機 一、安裝: sudo apt-get install samba 二、配置: 1、創建一個需要共用的目錄,並修改許可權: xx@ubuntu:~$ mkdir linux-tq2440 xx@ubuntu:~$ sudo c ...
  • 在這裡對nginx的安裝簡單的做個記錄,後續有時間的話在詳細補充。 1.yum安裝gcc gcc-c++: 2.下載必需的依賴庫:zlib 、openssl 、pcre 3.安裝依賴庫:zlib 、openssl 、pcre 4.修改配置: 添加以下內容,註意位置: 5.測試配置問是否正確: 出現以 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...