--- title: 'shell 命令 - 裝置資訊、處理數據' disqus: kyleAlien --- shell 命令 - 裝置資訊、處理數據 === ## OverView of Content 這章演示如何透過指令來探查 Linux 裝置訊息 [TOC] ## 追蹤程序 追蹤程序通常有兩個命令 `stace`(系統呼叫追蹤)、`ltrace`(系統函式庫追蹤) ### 追蹤系統呼叫 - strace * **`strace` 命令**:可以 **追蹤程序 `fork` 後的所有涉及系統呼叫的行為**;範例如下 ```shell= strace cat /dev/null ``` 簡單分析以下系統呼叫 1. 使用 `execve` 命令去覆蓋 fork 命令後的記憶體區塊,去執行新的命令… 並使用 `brk` 來初始化記憶體 ```shell= execve("/usr/bin/cat", ["cat", "/dev/null"], 0x7fc339ef08 /* 50 vars */) = 0 brk(NULL) ``` 2. 共享函式庫的載入,載入多個 so 庫(`ld.so.preload`、`ld.so.cache`、`libc.so.6`、``) ```shell= faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=79836, ...}) = 0 mmap(NULL, 79836, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f81b1e000 close(3) = 0 openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0`\17\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1455120, ...}) = 0 ``` 3. 開啟 `/dev/null` 裝置,如果核心開啟檔案成功,則會返回 3 ```shell= openat(AT_FDCWD, "/dev/null", O_RDONLY) = 3 ``` :::warning * 如果核心開啟檔案失敗,則會返回 -1 ```shell= openat(AT_FDCWD, "/dev/hello", O_RDONLY) = -1 ENOENT (No such file or directory) ``` ::: 4. 退出 ```shell= close(3) = 0 close(1) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++ ``` > ![](https://hackmd.io/_uploads/S1-CxXg52.png) ### 追蹤系統函式庫 - ltrace * **`ltrace` 命令**:它不追蹤核心即別的內容,只追蹤共享函式庫 ```shell= ltrace cat /dev/null ``` ## 監看 Process、Thread Process 就是一個應用的最小單位,每個應用都會有自己專屬的 Process ID ### 查看 Process 訊息 - ps * 基礎使用如下;而 `ps` 這個命令有許多的參數,這邊這會列出幾個常用的參數,其餘的參數可以使用 `man ps` 去查詢 ```shell= ## 查看當前用戶所使用的進程資訊 ps ``` > ![](https://i.imgur.com/201Sq31.png) Linux 系統中的 GNU **ps 命令支持 3 種不同類型參數** 1. **Unix 風格**:單破折線 (`-<options>`),以下是常用參數 | 參數 | 功能 | | - | - | | **-A / -e** | 顯示所有的進程(不限用戶) | | **-f** | 顯示完整格式輸出 | | **-l** | 長列表 | | -N | 顯示與指定參數不同的進程 | | -a | 顯示除了 控制進程 (session leader) 和 無終端進程之外的所有進程 | | -d | 顯示除了 控制進程 (session leader) 之外的所有進程 | | -C <cmdlist\> | 顯示指定列表中的進程 | | -G <grplist\> | 顯示列表中指定的 GID 進程 | | -U <userlist\> | 顯示列表中指定的 UID 進程 | ```shell= ## Unix 風格常用組合 ps -ef ## 下表說明 -efl 輸出的欄位說明 ps -efl ``` > ![](https://i.imgur.com/M81GS0p.png) | 輸出欄位(前到後) | 說明 | 補充 | | - | - | - | | F | 內核分配給系統的標記 | | | S | 進程當前狀態 | O:運行、S:睡眠、Z:僵化、R:等待中彈可運行、T:停止 | | UID | 啟動該進程的的用戶 | | | PID | 進程編號 | | | PPID | 父進程編號 | | | C | 進程的 CPU 使用率 | | | PRI | 優先級 | 數字越大越優先 | | NI | 謙讓度 | 與 PRI 共同決定最終的優先級 | | ADDR | 進程的內存地址 | | | SZ | 若進程被換出,所需要的交換空間大小 | Swap size | | WCHAN | 進程休眠時的內存地址 | | | TTY | 進程啟動的終端設備 | | | TIME | 進程使用 CPU 的累積時間 | 格式:`hh:mm:ss` | | CMD | 啟動的程序名稱 | 可能會更改 | 2. **BSD 風格**:無破折線(`<options>`);以下說明幾個常見的 options :::info BSD 風格的 ps 命令相對簡單 ::: | 參數 | 功能 | | - | - | | T | 跟蹤與當前終端有關的進程 | | a | 所有終端的進程 | | g | 顯示所有終端的進程(包括控制進程) | | r | 僅顯示運行中的進程 | | **x** | 顯示所有進程,包括未分配終端的進程 | | l | 長模式 | | s | 信號格式顯示 | | **u** | 基於用戶的格式顯示 | | v | 採用虛擬內存的方式顯示 | | **w** | 顯示命令全名,而非簡易名稱 | ```shell= ## BSD 風格常用組合 ps s ps xl ps aux ``` :::info 加州伯克利分校開發的 Unix 版本,大部分與 Unix 風格可獲得的數據相差不大,它的特點是,它可以 **獲得更詳細的進程資訊** (如下表) ::: | BSD 特色欄位 | 說明 | 補充 | | - | - | - | | VSZ | 進程在內存中的大小 | 大小為 KB | | RSS | 物理內存的大小 | | | STAT | 當前進程的狀態 | `<`:進程運行在高優先級上、`N`:進程運行在低優先級、`L`:進程有頁面鎖定在內存、`s`:控制進程、`l`:多線程進程、`+`:運行在前台、`S`:可休眠的進程 | > ![](https://i.imgur.com/B7bBmoe.png) :::success * ps + `$$` 參數配合:可以獲得屬於當前程序的運行程序 ```shell= ps u$$ ``` > ![](https://hackmd.io/_uploads/HkHDRd_Ln.png) ::: 3. **GNU 風格**:雙破折線(`--<options>`);改進 ps 命令中加入了另外一些參數; ```shell= ## 查看進程之間的關係 (最後的 COMMAND 會有父子關係圖) ps aux --forest ``` > ![](https://i.imgur.com/A6LdSvX.png) ### 查看 Thread * 透過 ps 命令選項 `m`,可以查看到用戶下的程序都使用哪些進程 ```shell= ps am ``` > 下圖,PID 下的 `- -` 代表該進程下有幾個 Thread() > > ![](https://hackmd.io/_uploads/HyoOdmx92.png) ### 實時監測 Process - top * 當你下達 `ps` 命令時你所獲取的是當下的進程資訊,當有進程更新時(或有進程關閉、開啟)是不會實時更新的 * 使用 `top` 命令就可以實時監測進程 ```shell= top ``` > ![](https://i.imgur.com/kPkxPX0.png) 1. 第一行:當前時間、系統運行時間、登入用戶數量、平均負載(1min 5min 15min) :::info * 平均負載 15 分鐘仍在 2 以上說明系統在繁忙狀態 * 可以使用 `uptime` 命令來查看 相同效果 > ![](https://hackmd.io/_uploads/SyQk27e53.png) ::: > ![](https://i.imgur.com/RoURFR7.png) 2. 第二行:系統中進程的總數、狀態 :::info * `top` 命令中將進程稱為 `task` ::: > ![](https://i.imgur.com/stUoRyw.png) 3. 第三行之後: * CPU 訊息,根據進程的 owner & 進程的狀態將 CPU 利用率分成幾類輸出 * 記憶體分配、使用、剩餘 * 交換記憶體的訊息 (物理空間當記憶體的狀態) > ![](https://i.imgur.com/NjHiGX5.png) 4. 內容欄位: | 欄位 | 說明 | | - | - | | PID | 進程 ID | | USER | 進程的 Owner | | PR | 進程優先級 | | NI | 進程謙讓度 | | VIRT | 進程所佔用的虛擬記憶體量 | | RES | 物理內存的總量 | | SHR | 與其他進程共享的內存總量 | | S | 進程狀態(D:可中斷、R:運行中、S睡眠、T:跟蹤或停止、Z:僵化) | | %CPU | 進程使用的 CPU 時間百分比 | | %MEM | 進程佔用的內存百分比 | | TIME+ | 進程啟動到目前為止使用了多少 CPU 時間 | | COMMAND | 進程名 | * `top` 命令是一個互動式進程,可以透過以下字符來改變 `top` 設定 | 鍵入字符 | 功能 | | - | - | | `d` | 設定 `top` 更新時間 | | `f` | 改變 `top` 的顯示順序(預設 %CPU 排序)、顯示內容 | | `q` | 退出 | > `f` 的設定頁面 > > ![](https://i.imgur.com/ViRpom2.png) > 改變顯示順序為 `%MEM` 排序 > > ![](https://i.imgur.com/C6sPN3j.png) ### 結束進程 - kill / killall * Unix 進程之間的通訊其中一個辦法就是 **通過 ==信號== 通訊**,Linux 也沿用這個方法;其信號與意義如下表 | 信號 | 名稱 | 意義 | | - | - | - | | 1 | HUP | 掛起 | | 2 | INT | 中斷 | | 3 | QUIT | 結束運行 | | 9 | KILL | 無條中終止 | | 11 | SEGV | 段錯誤 | | 15(Kill 默認) | TERM | 盡可能地終止(如果不能終止也不能怎麼辦) | | 17 | STOP | 無條件停止運行(不等於終止) | | 18 | TSTP | 停止或是暫停,再次啟動後繼續在後台運行 | | 19 | CONT | 在 STOP、TSTP 之後恢復運行 | * `kill` 命令:可以對指定進程傳入不同信號 ```shell= # 對 PID 為 1234 的進程下達強致終止的信號 kill -s 9 1234 ``` * `killall` 命令:透過進程名(`Command` 欄位)來關閉進程 ```shell= ## 關閉 http 開頭相關進程 killall http* ``` :::warning 要小心使用這個命令,否則可能會導致文件系統損壞 ::: <!-- ## CPU、記憶體、IO 監控 ### 測量 CPU 時間 * 除了使用 `top` 命令監控系統之外 --> ## 監看磁碟、檔案 在Linux系統上有幾個命令行命令可以用來幫助管理存儲媒體 ### lsof - 開啟的檔案 * **`lsof` 命令**:`list open files` 會列出 **打開的檔案**、**使用它們的程序**,甚至包括 **網路資源、動態函數、管道** ```shell= lsof | head ``` 其中欄位意義如下 | 欄位 | 說明 | | - | - | | `COMMAND` | 描述擁有該檔案描述符的程序名稱 | | `PID` | 檔案的進程 ID | | `TID` | 檔案的線程 ID | | `TASKCMD` | 任務名,通常跟 `COMMAND` 相同 | | `USER` | 該進程所屬的 User name | | `FD` | **包含兩種意義**;^1.^ 表明檔案的作用(像是 `cwd`)、^2.^ 檔案的描述符(一串數字) | | `TYPE` | 檔案類型(像是目錄、socket... 等等) | > ![](https://hackmd.io/_uploads/HJhHtzlcn.png) 1. **查看當前進程的所開啟的所有檔案** ```shell= # `$$` 是當前進程 ID ls -p $$ ``` > ![](https://hackmd.io/_uploads/SkO1Tzg92.png) 2. **顯示 `/home/alien` 目錄中所有被開啟的檔案** ```shell= ps /home/alien ``` > ![](https://hackmd.io/_uploads/Bkpnpzlqh.png) ### 掛載設備 - mount * 我們知道 Linux 是使用一個虛擬目錄來管理所有的硬碟,當使用新的硬碟時,就 **必須掛載 (mounting) 硬碟到虛擬目錄之下** > 大部分狀況下移動式裝置都會自動掛載 * 單純使用 `mount` 命令可以查詢當前系統上已掛載的裝置;`mount` 輸出的格式代表意義如下表 ```shell= mount ## 輸出格式 ## <設備文件> on <虛擬目錄掛載點> type (<訪問狀態>) ``` > `/dev/mmcblk0p2 on / type ext4 (rw,noatime)` > | 輸出 | 說明 | 補充 | | - | - | - | | `/dev/mmcblk0p2` | 媒體的設備文件 | - | | `/` | 媒體掛載到虛擬目錄的掛載點 | - | | `ext4` | 文件系統類型 | eg. `vfat`, `ntfs`, `iso9660` | | `rw` | 已掛載的媒體訪問狀態 | `ro` 只能讀、`rw` 可寫可讀、`user` 普通用戶掛載文件系統、`loop` 掛載 **文件** | > ![](https://i.imgur.com/wJpGENA.png) * 掛載:手動掛載設備 ```shell= ## 格式:mount -t <type> <device> <directory> ## 掛載檔案類型 ext4 ## 裝置在 /dev/test_device ## 掛載到虛擬目錄的 /mnt/my_device 資料夾中 sudo mount -t ext4 /dev/test_device /mnt/my_device ``` * **卸載**:移除已掛載設備 (如果設備 busy 就不能卸載) ```shell= ## umount [device] ## 卸載虛擬目錄對應的 /dev/test_device 裝置 sudo umount /dev/test_device ``` :::danger * 設備 busy,可以使用 `lsof` 查看使用設備的進程,並將它停止 ::: ### 文件系統空間 - df * df (`report filesytem disk`) 命令:該命令可以透過檔案系統獲取當前硬體已經使用的空間 > 以檔案系統的角度去查看容量,一個硬碟中可能有多個分區,也就是會存在多個檔案系統,所以 `df` 並不代表硬碟總空間) ```shell= df ## 輸出 ## /dev/root 246108604 7232432 228818000 4% / ``` | df 欄位 | 輸出數值 | 說明 | | - | - | - | | FileSystem | /dev/root | 設備(device)位置 | | 1K-blocks | 246108604 | 總容量(1K 為單位) | | Used | 7232432 | 已使用(1K 為單位) | | Available | 228818000 | 還可以使用(1K 為單位) | | Use% | 4% | 已使用的百分比 | | Mounted on | / | 設備(device)掛載到虛擬目錄的位置 | :::info * df 命令注意點 * 輸出顯示是 Linux 系統認為的當前值,並非實時值(尚未釋放的文件不會算數) * 隱藏的預留區域也會算在百分比內(所以怎麼家都不會是 `100%`) ::: > ![](https://i.imgur.com/skvy7xm.png) ### 硬體使用空間 - du * du (`estimate file space usage`) 命令:該命令可以判斷 **特定目錄** 的磁碟使用空間 (與 `df` 的差異是可以指定目錄) > du 默認目錄為當前目錄 ```shell= ## 由於輸出過長,這邊使用 head 限制輸出 du | head ## 輸出更容易讀的單位 du -h | head ``` > 輸出:4 ./Pictures | du 左到右輸出數值 | 說明 | | - | - | | `4.0K` | 佔用的磁碟塊(單位預設為 1K) | | `./Pictures` | 目錄 | > ![](https://i.imgur.com/l0YGgvY.png) `du` 其餘的 options | options | 說明 | | - | - | | `-h` | 更方便檢視大小的格式 | | `-c` | 已列出的文件總大小 | | `-s` | 顯示每個輸出參數的總計(按資料夾) | ```shell= ## 查看當前目錄中的所有資料 du -sh * ``` > ![](https://i.imgur.com/3ybamts.png) ## 處理數據 ### 排序 - sort * sort 默認會按照默認語言的的排序規則對文本中的數據進行排序 ```shell= ## 查看原先數據 cat test_Context ## sort 會按照字元(ASCII)順序排序 sort test_Context ``` > ![](https://i.imgur.com/CW9E7wF.png) * 以下幾個 sort 常用的 options | sort's options | 說明 | 其他 | | - | - | - | | -n | 將文本內容的排序改成透過數字順序排序 | - | | -M | 按照月份排序 | Linux 日誌文件經常會再每行的起始位,會有一個時間戳 | | -k POS1[, POS2]| 指定排序範圍;排序從某個位置(POS1) 到(POS2) | POS2 不指定則代表到尾 | | -t | 按照某個值(由你設定),將文本區分 | - | | -r | reverse 排序 | | 1. 按照月份排序內容 ```shell= ## 查看原先數據 cat test_Context ## 將內容按照月份排序 sort -M test_Context2 ``` > ![](https://i.imgur.com/wMvgY2P.png) 2. 按照用戶 ID 排序 `/etc/passwd` ```shell= ## -t 指定 ':' 區分 -> [root] [x] [0] [0] [root] [/root] [bin/bash] ## -k 3 排序範圍從 區分的第三個範圍開始到最後 ## -n 用數字排序 sort -t ':' -k 3 -n /etc/passwd ``` > ![](https://i.imgur.com/vbjBFAC.png) 3. 按照大到小排序輸出內容 ```shell= ## 排序 du -sh 的輸出(大到小) du -sh * | sort -nr ``` > ![](https://i.imgur.com/upRT0nb.png) ### 過濾數據 - grep * `grep` 命令可以在數據海中找到你所需要的某些特定資料,格式如下 > grep [options] pattern [file] ```shell= ## 找尋 /etc/passwd 文件中,有 root 字的內容 grep root /etc/passwd ``` > ![](https://i.imgur.com/IegEBQC.png) * 以下幾個 grep 常用的 options | grep's options | 說明 | 其他 | | - | - | - | | -v | 反向搜尋 | 顯示所有非 pattern 的內容 | | -n | 添加行號 | - | | -c | 查看符合 pattern 匹配數量 | - | | -e | 多匹配模式 | 可設定多個條件 | | -E | 正則表達式匹配 | `[]` 中括號內任意字串匹配,`''` 指定全符合才匹配 | 1. 查詢 `/etc/passwd` 內所有沒有 root 的內容 ```shell= grep -vn root /etc/passwd ``` > ![](https://i.imgur.com/6nsPC2x.png) 2. 同上,不過這邊輸出所有符合條件的數量 ```shell= grep -vc root /etc/passwd ``` > ![reference link](https://i.imgur.com/u3w15Zm.png) 3. 多匹配模式,找尋 alien 或 root 關鍵字並輸出行數 ```shell= grep -e alien -e root -n /etc/passwd ``` > ![](https://i.imgur.com/0KTUaFJ.png) :::success * 當然我們也可以使用正則表達式的方式去搜尋內容 ```shell= grep -E 'root|alien' -n /etc/passwd ``` > ![](https://i.imgur.com/5HnqCIp.png) ::: ### 壓縮數據 - gzip * 壓縮數據可以讓文件縮小佔有空間,而 Linux 中有多種壓縮數據的工具,可以根據需求做選擇 | 工具 | suffix 文件擴展名 | 說明 | | - | - | - | | compress | .Z | Unix 最早期的壓縮工具 | | bzip2 (主要用於壓縮文字) | .bz2 | Burrow-Wheeler 塊排序文本壓縮算法、霍夫曼編碼 | | gzip | .gz | GNU 壓縮工具、Lempel-Ziv 編碼 | | zip | .zip | PKZIP 工具 Unix 實現 | * `gzip` 是用來替代 `compress` 的免費版工具,gzip 相關工具如下 | gzip 工具 | 說明 | | - | - | | gzip | 壓縮文件 | | zcat | 查看壓縮過的文件 | | gunzip | 解壓文件 | 1. 壓縮檔案,並查看壓縮過後的檔案 ```shell= ## 壓縮 file.txt 並輸出到 done.gz 檔 gzip -c file.txt > done.gz ## 查看壓縮檔 zcat done.gz ``` :::info 如果沒有另外指定 `-c` 操作符,則會將原來的檔案直接替換成壓縮檔案 ::: > ![](https://i.imgur.com/Iq3XNvP.png) 2. 解壓檔案 ```shell= gunzip -c done.gz > finishGunzip cat finishGunzip ``` > ![](https://i.imgur.com/MV34YNY.png) ### 數據歸檔 - tar * **歸檔** 與 **壓縮** 是兩種不同的行為;**歸檔是將多個檔案放置匯聚成一個檔案,壓縮則是將原來的數據透過特別的算法減少其佔用空間** > 這兩個行為常常會一起使用,所以會搞混是很正常的 * `tar` 命令最早上為了將數據寫到外部空間;格式如下 > `tar` function [options] object1 object2 ... | tar's function | 說明 | | - | - | | -A | (--concatenate) 將已有的 tar 文件歸檔到另一個已有的 tar 文件 | | -r | (--append) 將數據追加到已有的 tar 文件 | | -c | (--create) 創建新的 tar 文件 | | -t | (--list) 列出歸檔內容 | | -u | (--update) 將已有的 tar 文件內的相同檔名的文件更新 | | -x | (--extract) 從已有的 tar 歸檔文件中提取文件 | | tar's options | 說明 | | - | - | | -C <\dir> | 切換到指定目錄 | | -f <\file> | 將壓縮結果輸出到指定檔案 | | -p | 保留所有文件的權限 | | -v | 顯示處理時的文件 | | -j | 將輸出內容重新定向給 bzip2 | | -z | 將輸出內容重新定向給 gzip | 1. 歸檔文件 ```shell= ## 將 .txt 結尾的檔案歸檔到 tarFinish.tar 檔案 tar -cvf tarFinish.tar *.txt ls -laF ``` > ![](https://i.imgur.com/zUSLPru.png) 2. 查看歸檔後的文件列表 ```shell= tar -tf tarFinish.tar ``` > ![](https://i.imgur.com/OIfUHRy.png) 3. 解壓文件 ```shell= ## 解壓並輸出到指定資料夾中 tar -xvf ./tarFinish.tar -C ./myExtract ``` > ![](https://i.imgur.com/sBznkMC.png) :::success * **後綴 `.tgz` 文件**? `.tgz` 文件都是將 tar 檔案透過 gzip 算法壓縮過後的檔案,可以透過以下命令進行解壓並提取 ```shell= tar -zxvf <filename.tgz> ``` 其實 suffix 結尾與 `.tar.gz` 相同,也可以使用兩段式命令解開 ```shell= gunzip <filename.tar.gz> tar xvf <filename.tar> ``` ::: ## Appendix & FAQ :::info ::: ###### tags: `Linux Shell`