天天看點

Linux系統調用

特此說明: 劉超的趣談linux作業系統是比較重要的參考資料,本文大部分内容和所有圖檔來源于這個專欄。

相關概念

程式vs程序vs指令: Linux系統上所有的操作由

程序

完成,

程序

的運作是動态的,在此之前是一個靜态的

程式

。使用者用一個程式來啟動一個程序,這個程式可以是别人寫好的(最終被編譯成可執行檔案),比如

ls

pwd

cat

,也可以是我們自己寫的。

系統調用: 無論如何,程式最後運作起來都是程序,并且一個程式想要在系統上跑,要用到

系統調用

,這是系統給使用者提供的API接口。

trace指令: Linux有個指令strace,常用來跟蹤程序執行時系統調用和所接收的信号。通過

manstrace

檢視具體描述。

Glibc: 作為一個開發者,也許平時并沒有直接使用系統調用,而是Glibc庫。Glibc是Linux下使用的開源的标準C庫它是GNU釋出的libc庫。

Glibc

即系統調用的封裝。

介紹系統調用

然後本文開始介紹這些系統調用,先上圖

Linux系統調用
程序管理

linux作業系統使用

fork

從父程序中建立子程序,子程序

execve

運作程式(二進制檔案),父程序

waitpid

等待子程序結束。

所有程序都是父程序

fork

出來的,對于作業系統而言第一個鼻祖程序是哪來的呢? 系統啟動的時候先建立一個所有使用者程序的“祖宗程序”。

記憶體管理

在作業系統中,每個程序都有自己的

程序記憶體空間

。其中布局就有

代碼段

資料段

一個程序的記憶體空間是很大的,32位的是4G,64位的就更大了。實體空間是有限的,是以程序的空間不能事先配置設定好的,一定是需要的時候再配置設定。

brk

mmap

是官員堆配置設定記憶體的系統調用,配置設定記憶體數量比較小的時候,使用

brk

會和原來的堆的資料連在一起。當配置設定的記憶體數量比較大的時候,使用mmap,會重新劃分一塊區域。

檔案管理

檔案系統相當于公司的資料庫,用于儲存一些永久性質的資料。能做到長期儲存,檔案之是以能做到這一點,一方面是因為

媒體

,另一方面是因為

格式

對于檔案的操作,無非是

建立creat

打開open

讀read

寫write

等等

Linux中一切皆檔案

就包括

二進制檔案

文本檔案

stdout檔案

Socket檔案

裝置檔案

目錄檔案

,包括程序運作起來在

/proc

下生成的程序号也是檔案。對于每一個檔案,Linux配置設定了

檔案描述符

,這是一個整數。

信号處理

信号是異步處理機制,用于緊急突發情況。每一種信号都有預設動作,當然使用者也能編寫信号處理函數,通過

sigaction

系統調用進行處理。

程序間通信

本地程序之間實作資料的互通,比較常見的處理機制有

消息隊列

共享記憶體

通過

msgget

建立一個新的隊列,

msgsnd

将消息發送到消息隊列,而消息接收方可以使用

msgrcv

從隊列中取消息; 我們可以通過

shmget

建立一個共享記憶體塊,通過

shmat

将共享記憶體映射到自己的記憶體空間,然後就可以讀寫了。

網絡通信

核心中有TCP/IP網絡協定棧的實作,可以通過socket來實作跨系統的程序間通信。

檢視源碼

下載下傳核心源碼,找到

./include/asm-x86_64/unistd.h

檔案,裡面對于系統調用的定義

hinzer@ubuntu:linux-2.6.11$ head ./include/asm-x86_64/unistd.h
#ifndef _ASM_X86_64_UNISTD_H_
#define _ASM_X86_64_UNISTD_H_

#ifndef __SYSCALL
#define __SYSCALL(a,b) 
#endif

/*
 * This file contains the system call numbers.
 *
           

檢視過程

程序通過系統調用從使用者态到核心态,

使用者态 - 系統調用 - 儲存寄存器 - 核心态執行系統調用 - 恢複寄存器 - 傳回使用者态

。對于應用開發,上層還通過

glibc

庫(對系統調用的一個封裝庫)。

從glibc提供的open函數出發,剖析如何從glibc的

open

調用到核心的

sys_open

!!!
glibc封裝
# 以下相關檔案
./sysdeps/unix/syscalls.list   # 列出所有glibc的函數對應的系統調用
./sysdeps/unix/make-syscalls.sh # 根據上面的配置檔案,對于每一個封裝好的系統調用,生成一個檔案
./sysdeps/unix/syscall-template.S  # 定義了這個系統調用的調用方式
./sysdeps/hppa/sysdep.h   # 通過 `vim -t PSEUDO` 找到 PSEUDO 這個宏的定義。
           

經過分析: open函數的代碼邏輯,得出結論: 對于任何的系統調用,會調用

DO_CALL

。這也是一個宏,這個宏 32 位和 64 位的定義是不一樣的。

32位系統調用過程

繼續分析glibc源碼,發現宏

DO_CALL

定義處

unix/sysv/linux/i386/sysdep.h

了解: 使用者調用通過軟中斷進入核心态,在系統調用表中找到對應的核心系統調用,執行核心調用之前儲存使用者态寄存器,執行核心調用後傳回并恢複使用者态。

Linux系統調用
64位系統調用過程

DO_CALL定義在源碼位置

unix/sysv/linux/x86_64/sysdep.h

,還是将系統調用名稱轉換為系統調用号,放到寄存器 rax。和32位不同的是,這裡是真正進行調用,不是用中斷了,而是改用 syscall 指令了,而且傳遞參數的寄存器也變了。

了解: 使用者調用通過syscall指令進入核心态,在系統調用表中找到對應的核心系統調用,執行核心調用之前儲存使用者态寄存器,執行核心調用後傳回并恢複使用者态。

Linux系統調用
整個流程
Linux系統調用

補充

系統調用表

資料結構定義在

arch/x86/entry/syscall_64.c

,系統調用清單輸出在

arch/x86/entry/syscalls/syscall_64.tbl

#系統調用号 abi類型 函數名                      系統調用名
2       common  open                    sys_open
           
系統調用函數聲明
系統調用函數實作
編譯規則

參考資料

  • 趣談Linux作業系統 - 05系統調用
  • 趣談Linux系統專欄 - 09系統調用
  • 如何下載下傳檢視glibc源代碼
  • glibc源碼分析(一)系統調用
  • linux下實作一個系統調用

繼續閱讀