---
title: 'Linux 系統 - 模式 & System call'
disqus: kyleAlien
---
Linux 系統 - 模式 & System call
===
## OverView of Content
使用到指令 cc(編譯 c)、strace(System call 追蹤)、sar(系訊訊息)、**ldd(查看分享庫依賴)**、kill ...
[TOC]
## 電腦系統概念
電腦是不斷處理以下幾個步驟
1. 透過輸入裝置 or 網路命令,**委託電腦進行處理**,並將這些指令存在 **記憶體中**
2. 要求 CPU 運算記憶體中的委託命令
3. 將 CPU 計算的結果寫入記憶體
4. 將記憶體上的資料寫入硬碟中 or 透過網路傳至別的電腦
> 
### 程式分類
1. 分類
| 種類 | 說明 | 舉例 |
| -------- | -------- | -------- |
| 應用程式 | 直接協助使用者 | Office、APP |
| 中介軟體 | 多數應用共通處理,可以算是協助開發的 Library | Web 伺服器、資料庫 |
| OS 系統 | 直接操作硬體 (韌體的部分) | Linux、Mac、Windows |
> 
2. 多行程協作
> 
### 程式組成 - Process
* 程式的運行最小單位為 Process 行程 (也就是進程),^1^ 程式使用的進程可以是複數 or 單數,^2^ **不同進程之間也可以通訊**
> 
### Linux 驅動裝置 - 概述
* 上面有說到 Linux 會透過驅動裝置進行存取,而 Linux 有特定的規定,必須透過 **==核心模式==才能存取裝置**
> 
## 操作系統 os
操作系統簡單來說就是介於硬體 & 應用之間的東西,**向下負責硬體,向上負責提供高效的 API,可說是 ==硬件管家==**
> 
### OS 作用
* 以 Linux 來說,Linux 的特色之一是外部裝置驅動
1. 如果沒有 OS 的話,每個應用就必須針對各個裝置寫必要的驅動程式
> 
2. 有 OS 的話,以 Linux 來說就有共用的裝置驅動程式來做處理 (相當於一個共用的 Lib)
> 
### OS 分類
| 分類角度 | Example |
| -------- | -------- |
| 應用領域 | 嵌入式系統、桌面操作、服務器類型 |
| 用戶數量 | 單用戶:Dos; 多用戶:window, mac, unix |
| 實時性(多線程) | 實時操作系統:RTOS、VRTX; 分時操作系統:window, mac, unix|
| 指令長度 | 8、16、32、64 bit |
| 分佈性 | 分佈式、網路式 |
| open source | 開源:Android, Unix; 閉源:Mac、Window |
| Unix 系列 | Unix-like:Linux, Mac, Xinu; Non-Unix-like:Windows |
| POSIX 兼容性 | Linux 兼容 |
### Android 系統
* **Android os 是基於 Linux Kernel 的**,會使用的原因如下:
1. 進程、記憶體管理
2. 權限安全機制
3. 經過驗證的驅動程序模型
4. 提供多種共享厙
5. Open Source~
### 資料 & 裝置 - 關係
* 簡單來說資料與裝置的關係如下
1. 資料透過讀取硬體
2. 再將資料儲存到記憶體
3. 最後到 CPU 做運算
4. 將結果存到記憶體
5. 最後存到裝置 or 網路上
> 
### 記憶體速度 & 價格 & 大小
| 記憶體分類 | 速度 | 價格 | 大小 |
| -------- | -------- | -------- | -------- |
| Register | 最快 | 最貴 | 最小 |
| SDR (靜態、快速記憶體) | 中 | 中 | 中 |
| DDR (動態記憶體) | 最慢 | 最便宜 | 最大 |
* 雖然 DDR 是記憶體中速度最慢的,但仍比硬碟快得多 ~
> 
## 核心模式 & 使用者模式
* Linux 透過 **++硬體++ 的協助,讓行程無法對裝置逕行存取**,**CPU 具備 `核心模式` & `使用者模式` 兩種**,除了 `Linux 驅動裝置` 需透過核心存取外,其他還有 `Process 管理系統`、`Process 排程器`、`Memory 管理系統`
* **核心模式**:以核心模式來運作、OS 核心處理的稱為核心 (kernel),它主要管理系統 CPU、Memory (核心有核心專用的 CPU、Memory 區塊,這裡先不說明)
> 
### System Call - 核心模式
* System Call 是一種統稱 (許多行為都可以稱為 System Call),以下舉例幾個
:::success
* 當使用到 System call 時就一定會切換到核心模式
:::
1. 建立、刪除 Process
2. 確保 (申請)、釋放 Memory
3. 不同行程之間的通訊 (mmap)
4. 網路
5. 檔案系統操作
6. 檔案操作 (裝置 IO 存取)
### CPU 中斷 - 模式轉換
* 當使用者有 **使用到 System call 的動作時,便會觸發 CPU 產生 ==中斷事件==,產生中斷事件時 CPU 就換轉換到 ++核心模式++**,並檢查 System call 要求是否正常
> CPU 收動 System call 中斷事件後會先檢查,eg. 檢查記憶體段是否要求正常 (是否超出 or 不合法)
> 
### 追蹤 System Call - strace 命令
* 前面有說到 IO 存取會呼叫到 system call,以下就寫一個簡單的 c 語言程式進行 IO 存取
1. **前置作業**: 安裝必要套件
```shell=
# 先安裝 gcc 套件
sudo apt-get install -y gcc
# 創建文件
touch hello.c
# 編輯文件
vim hello.c
```
2. 使用 puts 終端機上輸出 `Hello world!`
```c=
#include <stdio.h>
int main(void) {
puts("Hello world!");
return 0;
}
```
3. 使用 gcc 編譯 hello.c 檔案
```shell=
# 編譯 & 輸出可執行檔
cc -o hello hello.c
```
4. 運行編譯結果
```shell=
# 運行
./hello
```
> 
* **使用 `strace` 命令** 追蹤 system call 相關資訊
> 
```shell=
# -o 另外輸出一個檔案
strace -o hello.log ./hello
# 查看 system call 數量
wc -l hello.log
# -n 輸出行數
cat -n hello.log
```
重點是第 28 行的 write ~
> 
* 同樣使用 strace 命令,追蹤 Python 程式試試看,可以看到 python 使用到了 400 多行 system call (因為 Python 包裝較多層)
```shell=
# echo 輸出,並重新導向,最終輸出 hello.py
echo 'print("Hello world, by py")' > hello.py | cat hello.py
# 執行 python3
python3 hello.py
# 同上
strace -o hello.py.log python3 hello.py
# 同上
cat -n hello.py.log
```
雖然使用到 400 多次 system call,但最終仍都會用到 write ~
> 
### 測試 System call & CPU 模式 - sar 收集系統資訊
* sar 命令是用來收集系統資訊
1. sar 使用格式
```shell=
# -P 是行程
# ALL 是全部 CPU
# 1 是一秒一次
# 10 是監看次數
sar -P ALL 10
```
2. 使用者、核心模式 看法
| 模式 | 關注欄位 |
| -------- | -------- |
| 使用者 | `%user` + `%nice` |
| 核心 | `%system` |
> 
* 以下做兩個 loop 實驗,使用無限迴圈執行 ^1^ 沒有任何動作的執行檔案,^2^ 執行不斷 system call,並觀察 sar 數據
1. 沒有任何動作的執行檔案 (檔案創建、編輯指令都先略過,需要可以參考上面的)
```c=
// loop.c
int main(void) {
for(;;)
;
}
```
* 背景執行 + 監聽 CPU,最終會發現 **loop.c 都是 (觀察 user + nice) 運行在使用者模式**
```shell=
# 編譯檔案
cc -o loop loop.c
# 背景執行 loop (使用 & 就是背景執行,並且它會返回 PID,方便我們 Kill)
./loop &
# 監聽 CPU
sar -P ALL 1
```
> 
* **CPU 概念**:單執行 for 迴圈時 (基本上都消耗在 `%user` 欄位)
> 
2. 執行不斷 system call,`getppid` 函數 (取的當前行程的 pid)
```c=
// ppid_loop.c
#include <sys/types.h>
#include <unistd.h>
int main(void) {
for(;;)
getppid();
}
```
* 背景執行應用 (`ppid_loop.c`) + 監聽 CPU,最終會發現 **ppid_loop.c 分別有使用者 + 核心模式**
```shell=
# 編譯檔案
cc -o ppid_loop ppid_loop.c
# 背景執行 loop (使用 & 就是背景執行,並且它會返回 PID,方便我們 Kill)
./ppid_loop &
# 監聽 CPU
sar -P ALL 1
```
> 
* **CPU 概念**:因為有使用 SystemCall 函數,所以 CPU 消耗會分配部分到 `%system`
> 
:::success
* 最後移除背景運行行程
```shell=
# pid 在 & 之後返回
kill <pid>
```
:::
### System call 耗時
* strace -T 用來採集各種 System call 的耗時,以 **微秒 (µs) 為單位**,以下運行 ./hello 執行檔案,並監測它輸出 write 的耗時
```shell=
strace -T ./hello
```
可以看到該裝置運行時耗費了 120 µs 來執行 write 的 System call
> 
## System call 包裝函數
Linux 由提供許多 CPU 架構的 library,**System call 無法由 C 語言這種高等語言來呼叫,必須要使用到與 CPU 架構對應的 ==組合語言== 來進行呼叫**
### OS 包裝 System call
* 像是 Linux OS 會提供不同 CPU 架構使用的組合語言 (eg. `x86`、`x86_64`、`arm7`... 等等),如果沒有 OS 的話每個行程就要撰寫對應的 CPU 組語
```java=
// 以 x86_64 為例的組語範例
mov $0x6e,%eax // 將 0x6ax 寫入 eax 暫存器
syscall // 對 cpu 發出 system call,cpu 收到後產生中斷,並進入 ++核心模式++
```
> 
### 標準 C Library - glibc
* C 語言有依照 ISO 訂製的標準函數 Library,Linux 也有提供 C 的標準,通常依照 GUN 專案所以提供的 **glibc** 作為標準 C 函數 Library,幾乎所以有的 C 語言程式都會連接到 glibc
* 該程式與何種 library 相連可以使用 `ldd` 命令查看
:::info
* ldd : print shared object dependencies
ldd 後面是添加 **執行檔** 的絕對路徑
:::
* 以下幾個範例都連接到 `libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6` 也就是 glic
1. 查看 echo
```shell=
# 查看 echo 命令位置
which echo
# ldd 後面要添加絕對路徑
ldd /usr/bin/echo
```
> 
2. 查看自己寫的 hello 執行檔
```shell=
# ldd 後面要添加絕對路徑
ldd hello
```
> 
3. 查看 python3
```shell=
# 查看 python3 命令位置
which python3
# ldd 後面要添加絕對路徑
ldd /usr/bin/python3
```
> 
### OS 提供程式
* 以下提幾個常見到的程式
| OS 程式 | 關鍵字 |
| -------- | -------- |
| 系統初始化 | init |
| 變更 OS 行動 | sysctl、nice、sync |
| 檔案操作 | touch、mkdir |
| 文字加工 | grep、sort、uniq |
| 效能測量 | sar、iostat |
| 編譯器 | gcc |
| 腳本語言執行環境 | perl、python、ruby |
| shell | bash |
| 視窗系統 | X |
## Appendix & FAQ
:::info
:::
###### tags: `Linux 系統核心`