Linux
===
###### tags: `OS`
[TOC]
### 1. build 'C' code
```
>> g++ -g -o NREBandSeg NREBandSeg.cpp -lm -lstdc++
```
### 2. linux coding style
https://bit.ly/2p3tsrI
### 3. Digital Signature
``` linux=
# 在Linux環境下使用openssl指定e = 3, 1024 bit產生key pair
$ openssl genrsa -3 -out key.pem 1024
# 取出public key並轉成DER的格式
$ openssl rsa -inform PEM -outform DER -in key.pem -pubout -out key.pub
# 在Linux環境下使用openssl對FW(TBM_N1.bin)產生digital signature並指定SHA256及private key
$ openssl dgst -sha256 -sign key.pem -keyform PEM -binary -out sign.bin TBM_N1.bin
# 附加digital signature到FW的檔案後面
$ cat TBM_N1.bin sign.bin > TBM_N1_sign.bin
```
### 4. 修改adduser的家目錄
``` linux=
$ vi /etc/adduser.conf
>> DHOME=/home2
# 變更使用者家目錄路徑 並移動目錄
$ sudo usermod -d /newhome/username -m username
```
https://stackoverflow.com/questions/20797819/command-to-change-the-default-home-directory-of-a-user
### 5. mount linux掛載硬碟
``` linux=
# 顯示目前系統硬碟的使用情形
$ df -h
# 新硬碟在這裡是看不到的,因為我們都還沒開始分割,看看 /dev 下所有的硬碟情況
$ ls /dev/[sh]d*
# 格式化硬碟格式
$ mkfs -t ext4 /dev/sdb1
# 新增掛載目錄
$ mkdir /home2
# 掛載硬碟
$ sudo mount /dev/sdb1 /home2
# 設定存取權限
$ chmod 777 /home2
```
https://blog.gtwang.org/linux/linux-add-format-mount-harddisk/
https://fengka.pixnet.net/blog/post/29032979
### 6. 更改目錄使用權限
```
sudo chmod 777 /etc/squid
```
https://blog.csdn.net/pythonw/article/details/80263428
### 7. 新增sudo權限
``` linux=
# 新增sudo權限
$ sudo adduser jessechen sudo
$ sudo usermod -aG sudo sammy
# 移除sudo權限
$ sudo deluser USERNAME sudo
# 查看sudo權限
$ grep sudo /etc/group
```
### 8. 查看群組
``` linux=
$ cat /etc/group
# 觀看 /etc/passwd, /etc/group 設定檔確認是否有其帳號
$ grep jonny /etc/passwd
$ grep admin /etc/group
```
### 9. 修改使用者密碼
``` linux=
$ sudo passwd cjhuang
```
### 10. 新增與刪除使用者
``` linux=
$ adduser USERNAME
# 增加一名為USERNAME的帳號
useradd USERNAME
# 刪除USERNAME這個帳號
userdel USERNAME
# 更改USERNAME的密碼,若無參數,則改自己的密碼。
passwd [USERNAME]
# 查詢使用者資料
finger USERNAME[@HOSTNAME]
# 讓新增的使用者具有管理者權限
adduser jonny admin
```
https://bit.ly/2OlObCq
http://note.drx.tw/2008/03/ubuntuadduser-part1.html
### 11. samba
- 設定 Samba 分享檔案
``` linux=
$ sudo apt-get install samba
$ sudo vim /etc/samba/smb.conf
[joshuachao] # [資料夾的名稱] 可以自行變更
path = /home/smbuser # 這個是要分享的資料夾路徑
available = yes
valid users = smbuser # 指定使用者
read only = no
browseable = yes
public = yes
writable = yes
force user = joshuachao
force group = joshuachao
write list = @joshuachao
security = share
# 重新啟動 samba 服務
$ sudo /etc/init.d/smbd restart
$ sudo service smbd restart
$ sudo smbpasswd -a [username] #建立samba帳號密碼
$ sudo smbpasswd -z [username] #刪除帳號
$ smbpasswd [username] # 變更密碼
```
https://bit.ly/2NHJohh
https://bit.ly/2yNXIvr
- windows10 連不上samba的解决
https://blog.csdn.net/jiay2/article/details/123689399
https://www.asus.com/tw/support/FAQ/1037477/
### 12. scp
- 遠端複製檔案與目錄
``` linux=
# 從本地端複製到遠端
$ scp /path/file1 myuser@192.168.0.1:/path/file2
# 從遠端複製到本地端
$ scp myuser@192.168.0.1:/path/file2 /path/file1
-r 複製整個目錄
# sample:
$ scp -r louiswang@192.168.105.63:/home/louiswang/AQ360_v3/ /home/easanjhang/AQ360_backup/
```
https://bit.ly/2pcKpAp
### 13. du
- 顯示文件或目錄Size
``` linux=
$ du -h test
```
https://bit.ly/2N7YrS5
### 14. Android編譯之lunch命令
- 按照google给出的編譯步驟如下:
``` linux=
$ source build/envsetup.sh:加载命令 (設定環境變數)
$ lunch:選擇平台編譯選項
$ make:執行編譯
```
https://bit.ly/2CSWcgA
### 15. diff
- 比對兩個文字檔,比較時忽略空白
``` linux=
$ diff -w name_list.txt name_list_new.txt
```
### 16. ifconfig
- 查看Linux系统 網路IP
``` linux=
$ ifconfig -a
```
### 17. chown
- Linux 更改檔案擁有者與群組
``` linux=
# 將 myfile 的擁有者改為 myuser
$ sudo chown myuser myfile
# 將 myfile 的群組改為 mygroup
$ sudo chown :mygroup myfile
# 遞迴更改整個目錄下的所有檔案
sudo chown -R myuser:mygroup myfolder
```
https://bit.ly/2DWlwDg
### 18. date
- 更改系統時間
``` linux=
# 修改系統時間
$ sudo date -s "2016/11/11 10:21:32"
$ sudo date -s "2016-11-11 10:21:32"
# 取出系統時間
$ date
```
### 19. 遠端桌面若登不進去,可嘗試 xrdp restart
``` linux=
$ sudo service xrdp restart
```
https://ifun01.com/RHIDF9V.html
### 20. cat 命令:
- Cat命令主要有三大功能:
1. Linux Cat命令一次顯示整個檔案。$ cat filename
2. Linux Cat命令從鍵盤建立一個檔案。$ cat > filename(只能建立新檔案,不能編輯已有檔案.)
3. Linux Cat命令將幾個檔案合併為一個檔案。$cat file1 file2 > file
https://www.itread01.com/p/204415.html
``` linux=
$ cat filename # 顯示整個檔案
```
### 21. rm:
``` linux=
$ sudo rm -Rf dir_path # 強制刪除資料夾(不管他底下有無檔案)
```
### 22. fast boot bootimge.bat:
``` linux=
/******* fastbootbootimge.bat 內容 *******/
adb reboot bootloader
pause;
fastboot flash boot boot.img
pause;
fastboot reboot
pause;
/******* fastbootbootimge.bat 內容 *******/
```
### 23. linux build code
``` linux=
/* 至AQ360_v3的目錄下進行底下操作 */
/* 設定環境變數 */
>> source ./build/envsetup.sh
>> lunch 31
/* 對linux driver做編譯 */
>> make bootimage –j4
```
### 24. Putty 免密碼登入
https://blog.haohtml.com/archives/13170
``` linux=
chmod 700 ~/.ssh
```
### 25. 透過 adb shell cat & echo 來測試API
``` linux=
adb devices
adb root
adb remount
adb shell
shell>> cd sys\class\mini_isp\mini_isp_device
shell>> cat mini_isp_exposure_param
shell>> echo 11 22 33 > mini_isp_exposure_param
shell>> dmesg | grep miniisp
```
### 26. adb shell dmesg查看log
```linux=
adb devices
adb shell dmesg
adb shell dmesg | grep miniisp
```
### 27. vim 指令
```linux=
$ vim test.c
...
>> $ :/xxx_string // 往後搜尋字串
>> $ :?xxx_string // 往前搜尋字串
>> $ :set nu // 顯示行號
>> $ :set nonu // 取消顯示行號
```
### 28. find 指令
```linux=
// 在目前的目錄底下,找尋檔案名稱為 dt_gpio.c 的檔案
>> $ find . -name dt_gpio.c
// 在home目錄底下,找尋檔案名稱為 dt_gpio.c 的檔案
>> $ find /home -name dt_gpio.c
// find 加上 -type 參數可以指定檔案的類型,常用的選項有:
// d:目錄。
// p:具名的 pipe(FIFO)。
// f:一般的檔案。
// l:連結檔,如果與 -L 或 -follow 參數同時使用時,就只會搜尋到有問題的連結檔,如果想要與 -L 同時使用,請改用 -xtype。
// s:socket 檔案。
>> $ find . -type d -name rpi3_folder
>> $ find . -type f -name "*.c" // 列出目前目錄底下所有的.c檔案
```
### 29. grep指令
``` linux=
>> $ grep -arn 'rev 1.2' --include=*.dtb
// 顏色標示
// --color=always
// -r 遞迴搜尋檔案
// -a 搜尋text
// -n 標示行號
>> $ grep -i rpi * // 在當前目錄下搜尋所有文件,-i 忽略大小寫
>> $ grep -ir rpi * // 在當前目錄下搜尋所有文件,-r 遞迴子目錄也搜尋
>> $ grep -w "bcm2710-rpi-zero-2-w" * // 在當前目錄下搜尋所有文件,-w 精確搜尋某字串
>> $ grep -l "rpi" * // 在當前目錄下搜尋所有文件,-l 只顯示配對成功的文件檔名
```
### 30. sfdisk指令
```linux=
# 用參數 -d 可以 dump 資料
>> sudo sfdisk -d /dev/sdb > /backup/disk-part.dmp
# 把備份的檔案丟給 sfdisk
>> sudo sfdisk /dev/sdb < /backup/disk-part.dmp
# 可看SD卡的分區和總大小
>> sudo fdisk -l /dev/sdb
# -------------------------------------------------------------
# Disk /dev/sdb: 29.14 GiB, 31293702144 bytes, 61120512 sectors
# Disk model: SD Transcend
# Units: sectors of 1 * 512 = 512 bytes
# Sector size (logical/physical): 512 bytes / 512 bytes
# I/O size (minimum/optimal): 512 bytes / 512 bytes
# Disklabel type: dos
# Disk identifier: 0x3c4ef1b4
#
# Device Boot Start End Sectors Size Id Type
# /dev/sdb1 8192 532479 524288 256M c W95 FAT32 (LBA)
# /dev/sdb2 532480 61120511 60588032 28.9G 83 Linux
# -------------------------------------------------------------
# 檢查在已掛接檔案系統上調整大小的效果
>> df -h
# ------------------------------------------------
# Filesystem Size Used Avail Use% Mounted on
# tmpfs 1.1G 1.5M 1.1G 1% /run
# /dev/sda3 196G 94G 93G 51% /
# tmpfs 5.5G 0 5.5G 0% /dev/shm
# tmpfs 5.0M 4.0K 5.0M 1% /run/lock
# /dev/sda2 512M 6.1M 506M 2% /boot/efi
# tmpfs 1.1G 108K 1.1G 1% /run/user/1000
# /dev/sdb1 255M 128M 128M 51% /media/easonchang/bootfs
# /dev/sdb2 107M 85M 15M 86% /media/easonchang/rootfs
# ------------------------------------------------
# 改變檔案系統大小
>> sudo e2fsck -f /dev/sdb2
>> sudo resize2fs /dev/sdb2
# 查詢 Ext 家族 superblock以及GDT 資訊。
>> sudo dumpe2fs /dev/sdb1
```
### 31. mount image file
- [Partition ID definition]( https://www.win.tue.nl/~aeb/partitions/partition_types-1.html)
```linux=
>> sudo mount -o loop /home/easonchang/rpi3_2/buildroot-2023.08.3/output/images/rootfs.ext2 ~/rpi3_2/sd_mnt/
>> vim sd_mnt/etc/fstab // 可以看檔案系統type
>> file ~/rpi3_2/buildroot-2023.08.3/output/images/rootfs.ext2
>> sudo umount ~/rpi3_2/sd_mnt/
```








### 32. fdisk
``` linux=
# 可查詢sdcard.img的分區訊息
>> fdisk -u -l rpi-test-image-raspberrypi4-20240109023052.rootfs.wic
```

### 33. Ubuntu login information

### 34. ssh Cryptography
- SSH key type常用的加密演算法DSA、RSA、 ECDSA、Ed25519
1. 可分為兩類
- DSA 以及 RSA,依賴於對兩個大質數之積進行分解
- ECDSA 以及 Ed25519,依賴於橢圓曲線離散對數問題
2. DSA因為安全問題,已不再使用
3. ECDSA因為政治原因和技術原因,也不推薦使用。
4. RSA是目前兼容性最好,應用最廣泛的key類型,為ssh-keygen使用的default類型
5. Ed25519是目前最安全、加解密速度最快的類型,但它目前存在兼容性問題,舊版SSH工具無法使用。
- 
- 
- https://medium.com/@RiverChan/%E5%9F%BA%E7%A4%8E%E5%AF%86%E7%A2%BC%E5%AD%B8-%E5%B0%8D%E7%A8%B1%E5%BC%8F%E8%88%87%E9%9D%9E%E5%B0%8D%E7%A8%B1%E5%BC%8F%E5%8A%A0%E5%AF%86%E6%8A%80%E8%A1%93-de25fd5fa537
### 35. openssl產生一組Ubuntu passwd
- 透過openssl可以手動產生一組passwd hash,取代/etc/shadow亦能順利登入
- Sample: openssl passwd -6 -salt xyz 556677
- (Note: -6 SHA512)
- 可順利使用密碼 “556677”登入
- 
- 修改/etc/shadow,即可使用密碼 “556677”登入
- 
- https://unix.stackexchange.com/questions/81240/manually-generate-password-for-etc-shadow
- yescript
```
perl -e ' print crypt "P@ssW0Rd", "\$y\$j9T\$tEsTSalT\$" '
```
- 說明:
- “perl -e” 表示直接在命令列執行後方提供的 Perl 程式碼,而不需要另外建立檔案。該指令會呼叫 Linux 系統中的 crypt() 函式(由 libcrypt.so 提供),利用指定的明碼與 salt 組合產生對應的雜湊值。此範例也是 /etc/shadow 標準格式的 SALT。
- \$y$ :表示此雜湊使用 yescrypt
- j9T :yescrypt 成本參數(work factor)
- TESTSALT :自己定義的 salt
### 36. Practical Effects of Setting “UsePAM yes” on SSH in Linux
- https://www.baeldung.com/linux/usepam-yes-ssh-effects
### 37. Ubuntu Password: yescrypt vs. sha-512
- Ubuntu Password hash存在/etc/shadow
- 參考下面方式可以手動重建Password hash
- 若要使用mkpasswd要安裝以下package
- sudo apt install whois
- 安裝後,可以在/usr/bin/mkpasswd找到package
- 
- 
- https://medium.com/@hillsborosui/unix-shadow-file-%E5%8A%A0%E5%AF%86%E9%A1%9E%E5%9E%8B-87480f15ab90
- https://unix.stackexchange.com/questions/690679/what-does-j9t-mean-in-yescrypt-from-etc-shadow
- https://github.com/openwall/yescrypt/blob/main/yescrypt.h
- https://manpages.ubuntu.com/manpages/focal/en/man5/crypt.5.html
- https://manpages.debian.org/unstable/libcrypt-dev/crypt.5.en.html#Hashed~5
- https://www.baeldung.com/linux/hashing-methods-password
### 38. Shell scipt: Parsing sshd_config [key value]
```bash=
# /etc/ssh/sshd_config
# ...
# ListenAddress 198.51.100.1:22
# ...
# get interface ip address
# >> 198.51.100.1:22
eth1_addr=$(ip addr show eth1 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
# >> 198.51.100.1
eth1_domain=$(echo $eth1_addr | cut -d. -f1,2,3)
user=$(awk '/^ListenAddress\s+'$eth1_domain'/{ sub(/^ListenAddress\s+/, ""); sub(/:.*/, ""); print }' /etc/ssh/sshd_config.d/test.conf)
echo $user
# (^ListenAddtess[ ]+)([\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}):([\d]{1,5})
```
- /etc/NetworkManager/dispatcher.d/test.sh
```bash=
#!/usr/bin/env bash
interface=$1
event=$2
sshd_cfg_file=/etc/ssh/sshd_config.d/test.conf
if [[ $interface = "eth1" ]] || [[ $event = "up" ]]
then
eth1_addr=$(ip addr show eth1 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
eth1_domain=$(ip addr show eth1 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1 | cut -d. -f1,2,3)
if [ -e $sshd_cfg_file ]
then
# ToDo...
# sed -i 's#ListenAddress '$eth1_domain'.*#ListenAddress '$eth1_addr':22#g' $sshd_cfg_file
fi
return 0
fi
```
### 38. sshd_config: limit user accounts
- /etc/ssh/sshd_config
```sshd_config=
ListenAddress 198.51.100.1:55
ListenAddress 172.16.252.84:33
PermitRootLogin no
PermitEmptyPasswords no
Match Address 198.51.100.*
AllowUsers msitest
Match Address 172.16.252.*
AllowUsers eason
PubkeyAuthentication yes
PasswordAuthentication yes
```
- NG 案例
- Case 01
- msitest也會被Deny
```
DenyUsers *@198.51.100.*, !msitest
# 對於同一帳號而言,如果同時 Allow 跟 Deny 的話,結果會視為 Deny
```
- Case 02
```
# 原本預期只允許msitest從usb0連進來
# 但無法有效被限制,其他User也可以從usb0進來
Match User msitest
AllowUsers msitest@198.51.100.*
```
### 39. remount folder讀寫(r/w)權限
```
sudo mount -o remount,rw /FW_backup
sudo mount -o remount,rw /
```
### 40. 清空垃圾桶
```
rm -rf ~/.local/share/Trash/files/*
```
### 41. 尋找某指令目錄 (whereis)
```
whereis iptables
```
### 42. echo 重新導向,需要sudo權限的用法
```
echo "Some content" | sudo tee /path/to/file > /dev/null
sudo ./gen_usb_default_shadow.sh 1234 2024-12-01 2024-12-31 msisalt | sudo tee /etc/extrausers/msishadow.txt > /dev/null
```
### 43. 查詢檔案data,使用Hex檢視
```linux=
# 查看檔案最後 64 bytes 的 hex dump
tail -c 64 yourfile.bin | xxd
# 快速查偏移區的值
xxd -s 0x100 -l 64 yourfile.bin
```
### 44. 指令寫入journal log(logger)
```linux=
# 測試log寫入
logger -t OCPP -s -p user.info "your message"
logger -t OCPP -s -p user.info "This Eason0 is a test log $(date +%s)"
# 檢視log紀錄
journalctl -n 100
# 檢視log紀錄,指定Tag
journalctl -t OCPP
# 查詢指定.journal檔案
sudo journalctl --file=/var/log/journal/60dc736c5ff6467eb7463faf14277fea/system.journal -n 100
# 統計每個service總筆數
sudo journalctl -b -o json | grep -o '"_SYSTEMD_UNIT":"[^"]*"' | cut -d':' -f2 | tr -d '"' | sort | uniq -c | sort -nr >> "$LOG_FILE"
```
### 45. iotop 監控磁碟 I/O 活動(讀寫)
- **iotop** 是一個在 Linux 上使用的命令列工具,類似於 top,但它專門用來監控磁碟 I/O 活動(讀寫),可即時顯示哪些程序正在大量讀寫磁碟,幫助找出「磁碟 I/O 吃重的程式」。
```linux=
# 使用 iotop 以累積模式(accumulated mode)
sudo iotop -ao
# 每隔 10 秒,顯示一次有 I/O 活動的進程累積數據,
# 共輸出 3 次(總時間為 30 秒),
# 每列前會加上時間戳記,
# 格式為非互動的純文字模式(適合寫 log)。
sudo iotop -ao -n 3 -d 10 -b -t
# -a 累積模式,顯示從程式啟動以來各程序的總 I/O 用量
# -o 只顯示目前有 I/O 活動的程序(非 0 I/O)
# -n 3 執行 3 次後自動結束
# -d 10 每 10 秒更新一次(每次採樣間隔)
# -b 批次模式,不進入互動介面,適合寫入檔案
# -t 顯示每一行前面加上時間戳(timestamp)
```
### 46. inotifywait 監控檔案系統事件
inotify-tools 是一套 Linux 上用來監控檔案系統事件的工具,基於 Linux 核心的 inotify 機制。這套工具提供了命令列介面,讓使用者可以監看檔案或目錄的變化,例如檔案被讀取、寫入、刪除、移動等等。
| 事件 | 說明 |
| ------ | -------- |
|access | 檔案被讀取 |
|modify | 檔案內容被修改 |
|attrib | 屬性被改變 (如權限、時間戳記等) |
|close_write | 以寫入方式開啟後被關閉 |
|move | 移動(包括 move_from / move_to) |
|create | 新檔案或目錄建立 |
|delete | 檔案或目錄被刪除 |
```
sudo apt install inotify-tools
```
- 建立監控腳本:/usr/local/bin/journal_copy.sh
```linux=
#!/bin/bash
SRC_DIR="/run/log/journal/$(cat /etc/machine-id)/"
DST_DIR="/var/log/journal/$(cat /etc/machine-id)/"
mkdir -p "$DST_DIR"
inotifywait -m -e close_write --format '%w%f' "$SRC_DIR" --recursive | while read FILE
do
if [[ "$FILE" == *.journal ]]; then
echo "[COPY] $FILE -> $DST_DIR"
cp -u "$FILE" "$DST_DIR/"
fi
done
```
### 47. 讀取emmc info
```linux=
cat /sys/class/mmc_host/mmc0/mmc0:0001/pre_eol_info
cat /sys/class/mmc_host/mmc0/mmc0:0001/life_time
```
### 48. vsftp
- 透過ftp連線到遠端抓檔案的指令
```linux=
ftp <server-ip>
ftp 172.16.252.82
Name: your_username
Password: your_password
ftp> cd /FW_backup/share/upload # 切換到遠端目錄
ftp> get report.txt # 下載單一檔案
ftp> mget *.log
```
- windows透過FileZilla

- vsftp無法支援若家目錄透過symlink 到/run/
- 原因:如果家目錄是 /FW_backup/share,symlink 指向 /run/log/hmi,FTP 使用者可能會無法跟隨 symlink 出去(因為 /run/log/hmi 不在 chroot 內)
```linux=
sudo mkdir -p /FW_backup/share/upload
sudo mkdir -p /run/log/hmi
sudo chmod 777 /FW_backup/share/upload
sudo chmod 777 /run/log/hmi
sudo mount --bind /run/log/hmi /FW_backup/share/upload
```
- vsftpd.conf 設定檔
```linux=
# 允許匿名登入
anonymous_enable=YES
# 允許匿名使用者上傳檔案
anon_upload_enable=YES
# 允許匿名使用者建立目錄(可選)
anon_mkdir_write_enable=YES
# 允許匿名使用者刪除/改寫檔案(可選)
anon_other_write_enable=YES
# 寫入功能要開啟
write_enable=YES
# 指定本地使用者登入後的家目錄為 /FW_backup/share
local_root=/FW_backup/share
anon_root=/FW_backup/share
# 匿名使用者登入時不需要密碼
no_anon_password=YES
# 啟用使用者白名單機制
userlist_deny=NO
# 表示 /etc/vsftpd.user_list 裡的使用者允許登入(白名單),若為 YES 則表示列出者不允許登入(黑名單)
userlist_enable=YES
userlist_file=/etc/vsftpd.user_list
# 將log導向syslog或journal
xferlog_enable=NO
syslog_enable=YES
```
### 49. 動態重新mount為f2fs的分區
```linux=
# 若為Yocto請安裝
# IMAGE_INSTALL:append = " f2fs-tools"
# 確認已安裝 whereis mkfs.f2fs
# 格式化為 F2FS
sudo umount /dev/mmcblk0p5
sudo /usr/sbin/mkfs.f2fs /dev/mmcblk0p5 -f
# 重新掛載
sudo mkdir -p /FW_backup
sudo mount -t f2fs /dev/mmcblk0p5 /FW_backup
# 檢查
df -T /FW_backup
```
Reference
===
[1. 常用Unix/Linux命令](https://gywbd.github.io/posts/2014/8/50-linux-commands.html)
[2. 在linux kernel裡操作open file(create/open/read/write](https://bit.ly/2E5bDlI)
[3. SPIdev (Implement SPI driver)](http://linux-sunxi.org/SPIdev)
[4. SPI driver source code](https://elixir.bootlin.com/linux/latest/source/drivers/spi/spi.c)
[5. Linux-DEVICE_ATTR()介绍及使用示例](https://www.cnblogs.com/lifexy/p/9799778.html)
[6. Linux-I2C driver](https://www.cnblogs.com/lifexy/p/7816324.html)