2020年11月28日 星期六

POSIX message queues

POSIX message queues 讓 processe 間用 message 交換訊息,提供 System V message queues (msgget()、msgsnd()、msgrcv()、...) 類似的功能。

mq_open() 建立 message queue,回傳 message queue descriptor (mqd_t) 作為後續參照。每個 message queue 有一個 /somename 格式的 name,最長 NAME_MAX (i.e., 255)。兩個 processe 可以用相同 name 開啟同一個 message queue。

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);

mq_send() 和 mq_receive() 送收訊息。每個訊息有優先權,從低到高是 0 到 sysconf(_SC_MQ_PRIO_MAX) - 1,高優先權先收。

int mq_send(mqd_t mqdes, const char *msg_ptr,
                size_t msg_len, unsigned int msg_prio);

int mq_timedsend(mqd_t mqdes, const char *msg_ptr,
                size_t msg_len, unsigned int msg_prio,
                const struct timespec *abs_timeout);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
                size_t msg_len, unsigned int *msg_prio);

ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
                size_t msg_len, unsigned int *msg_prio,
                const struct timespec *abs_timeout);

process 用 mq_close() 停用 queue。queue 不再需要,用 mq_unlink() 移除。

mq_getaddr() 和 mq_setattr() 取得和修改 queue 屬性。

A process can request asynchronous notification of the arrival of a message on a previously empty queue using mq_notify(3).

fork() 後,child 複製繼承 message queue,share the flags (mq_flags) that are associated with the open message queue description.

https://www.man7.org/linux/man-pages/man7/mq_overview.7.html

MTD SPI Flash

parallel NOR, serial NOR (沒有 Serial NAND?SD Card 是 SPI Flash?)

Multi IO Flash

PropertyNANDeMMCSPI-NOR
DensityUpto128GBUpto128GBUpto512MB
Buswidthx8/x16x4/x8x1/x2/x4/x8
Read speedSlow random accessSimilarto NANDFast random access
WriteFastwritesFastwritesSlower
SetupRequirementsECC and bad block managementNeeds tuning (for higher speed)No overhead

SPI-NOR Flash Hardware

  • Flash is composed of Sectors and Pages
  • Smallest erasable block size is called Sector–May be 4/32/64/256 KB
  • sector 在切成許多 page,May be 256/512 bytes–Flash program is usually in page size chunks (though not necessary)
  • 在 write 或 erase 前需要送 Write Enable(WREN) 指令。
  • 大多支援 Read ID (RDID) 指令偵測型號

1-byte 指令 + 3/4-byte 位址 + n-cycle wait + n-byte data

傳統 SPI 控制器只是直接存取 SPI bus,不針對特定類型的 SPI 裝置。SPI-NOR 控制器內建 flash 通訊格式,以及硬體加速。

Linux SPI-NOR Framework 支援 SPI-NOR 控制器

  • v3.16+
  • MTD 子系統下:drivers/mtd/spi-nor/spi-nor.c
  • Derived from pre-existing m25p80 flash driver code
MTD framework
SPI-NOR framework
m25p80SPI-NOR controller driver
SPI core
SPI controller driver
Hardware (Controller + Flash)Hardware (SPI-NOR Controller + Flash)

參考

  1. https://giros.pixnet.net/blog/post/96929648 
  2. http://events17.linuxfoundation.org/sites/events/files/slides/An Introduction to SPI-NOR Subsystem - v3_0.pdf
  3. JFFS2 

2020年11月27日 星期五

JFFS2

JFFS2 (Journalling Flash File System version 2) 是可寫壓縮的檔案系統,使用 LZMA 壓縮,支援日誌和 wear leveling。

名詞:erase block、page、node

log 架構的檔案系統:log 是有時間順序的紀錄。儲存裝置視為 circular log,將檔案寫入當作 log 寫入。
  • 按照儲存裝置空間循序寫入,可批次進行,不太需要 seek,寫入效率變好。
  • 對「寫入」最佳化。例如大記憶體時「讀取」可很快從 memory cache 滿足,而「寫入」需要實際存入儲存裝置,反而變成瓶頸。
  • 適合 append 方式的儲存裝置。
  • 方便同個檔案建立多個不同時間的版本。
  • 方便從 crash 恢復,採用最後 consistent 版本,不用分析 data structures 細節。
  • 自動 wear leveling
  • garbage collection:舊 log reclaim free。

傳統硬碟 seek 是相對較慢的,所以檔案系統傾向於將檔案放在鄰近處 (spatial locality) 並就地修改,而有不同設計理念。

write-through caching

為了跳過壞的 eraseblock,寫入 jffs2 影像檔不是用 cp jffs2.img /dev/mtd2,而是用 flash_eraseall 後再 nandwrite。另外,讀取影像檔是用 nandread。

一開始為 NOR Flash 開發的 log-structured 檔案系統。Flash 寫之前需要抹除,抹除有下列限制:抹除很慢、抹除單位區塊很大、抹除有次數限制。JFFS 強制 wear levelling,將 flash device 視為 circular log,所有檔案和目錄的改變都寫到 the tail of the log in nodes。每個 node,包含 metadata 的 header 先寫入,再來才是檔案資料。在 header 的 offset pointer 可將 node 串起來。node 一開始是 valid,建立新版本後變成 obsolete。The free space remaining in the file system is the gap between the log's tail and its head. When this runs low, a garbage collector copies valid nodes from the head to the tail and skips obsolete ones, thus reclaiming space.

對 flash 的運用

從每個 erase block 開頭開始運用,正常 free space 都在後面。中間如果有 free space 只是浪費,不會造成問題。JFFS2 寫 node 到 flash 不會跨 erase block。可能是用錯誤的 erase block size 產生 JFFS2 image 造成的。erase block 預設是 64KiB,用過小 eraseblock 產生的 image 是無害的。

/proc/mtd

Erase Block Summary (EBS):加速掛載速度。儲存摘要資訊在每個 erase block 結束的地方,稱為 JFFS2_FEATURE_RWCOMPAT_DELETE 的 node,掛載食指需要讀取這部份,不需要 scan 每個 nodes (及讀取所有 page)。摘要資訊的 node 稱為 JFFS2_FEATURE_RWCOMPAT_DELETE,在寫入時啟用 EBS 時自動產生,但 mkfs.jffs2 產生的影像檔需要用 sumtool 插入摘要資訊。較大的 erase block,如 NAND,有較大的加速效果。

參考:
  1. http://www.sourceware.org/jffs2/
  2. YAFFS2
  3. UBIFS
  4. https://lirobo.blogspot.com/2019/11/memory-technology-device.html

LED Flasher

LED 閃爍電路

利用無穩態多諧振盪器 (Astable Multivibrator) 電路

http://ectechnic.com/diy/elect/flasher/index.htm

https://home.gamer.com.tw/creationDetail.php?sn=4479257

1.2V 點亮 LED

https://xiaolaba.wordpress.com/2016/08/27/1-2v-led-閃爍燈電路-動作的原理/

需求:1.2V 充電電池 太陽能 效率

Charlieplexing

利用 n 個 IO 的 High、Low、和 Hi-Z (Input) 多工控制控制 n(n-1) 個 LED

由三個部份,八位元移位暫存器、八位元儲存暫存器、及八個三態輸出,堆疊而成。 Tri-state buffer。八位元序列移位暫存器,加上並列儲存暫存器及輸出 Tri-state buffer。可以參考 WiKiPedia 上關於 Charlieplexing 技術的說明

2020年11月21日 星期六

Linux device number

device number 由 major number 和 minor number 組成[註1]

major 號碼識別使用的驅動程式,例如 /dev/null 和 /dev/zero 使用 driver 1、virtual consoles 和 serial terminals 使用 driver 4、 vcs1 和 vcsa1 使用 driver 7[註1]。現代 Linux kernel 允許多個驅動程式共用同一個 major 號碼,但看到的大部分還是一個 major 對一個 driver。

minor 號碼由 major 號碼的驅動程式使用,來控制數個 device,kernel 本身只知道對應到某個驅動程式。kernel 用 minor 號碼決定確切是那個 device,取決於驅動程式寫法,device 可以直接用指標,或使用 minor number 對應。

kernel 內部用 dev_t 存 major 和 minor,在 Linux 2.6 是 32-bit,其中 major 12-bit、minor 20-bit (兩者在之前的 kernel 都只有 8-bit),但程式不能做這樣假設,應該用 <linux/kdev_t.h> 的 macros MAJOR(dev_t dev) 和 MINOR(dev_t dev) 分別取得,並用 MKDEV(int major, int minor) 轉換成 dev_t。

簡單來說就是:註冊時用 0 會動態指定,mknod 時再去 /proc/devices 查實際使用的 major number。

取得 device number

有些常見設備的 major number 是靜態指定的,可查 kernel 原始碼的 Documentation/devices.txt。新驅動程式可以自己選一個看起來沒正在使用的 device number,或者動態取得。如果驅動程式擴散使用,自選 device number 就不適合,比較適合動態取得。動態取得的缺點是不能事先建立 device node,但通常不需要事先建立。

如果事先知道要用的 device number,用

int register_chrdev_region(
    dev_t first,
    unsigned int count,
    char *name);

first 是範圍起始的 device number,其中 minor number 部份通常是 0,但非必要。count 是連續的數目,如果大的話可能會跨到下個 major 號碼,仍可以正常使用。name 是關聯的 device 名稱,出現在 /proc/devices 和 sysfs。成功回傳 0,失敗回傳負值。

不知道要用的 device number,則動態取得,用

int alloc_chrdev_region(
    dev_t *dev,
    unsigned int firstminor,
    unsigned int count,
    char *name);

此時,dev 只用在輸出,成功時放範圍起始的 device number。firstminor 是請求的第一個 minor number,通常為 0。

device number 不用了要釋出,通常在 module 的 cleanup 函式。

void unregister_chrdev_region(dev_t first, unsigned int count);

Linux 2.4 引入可選用的 device file system (devfs),但 ...。

不用 devfs 時,major 號碼由驅動程式登記時指定,出現在 /proc/devices。major 可設為 0 動態配置沒用的 major 號碼:

int register_chrdev(
    unsigned int major,
    const char *name,
    struct file_operations *fops);

回傳動態配置的 major 號碼,0 使用指定 major 號碼成功,負值表示失敗。多於 256 driver?register_chrdev_region()?

再來要 mknod 建立 device node 時,去 /proc/devices 查使用的 major:

major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices`
mknod /dev/${device}0 c $major 0

在 /dev ,使用指定的驅動程式

註:register_chrdev_region() 和 alloc_chrdev_region() 在 kernel 內部都是呼叫 __register_chrdev_region() 註冊一個範圍 major:minor 給 char 驅動程式,如果 major 為 0 動態取得 major。alloc_chrdev_region() 強制動態取得 major 並回傳。register_chrdev_region() 則會無法得知動態取得的 major。另外 register_chrdev() 註冊一個 major 的整個 minor,除了 __register_chrdev_region() 外,還進行 cdev_alloc()、cdev_add()。

char devices
namemajorminor說明
/dev/mem11?
/dev/kmem12?
/dev/null13沒輸出
/dev/zero15提供無限的 0
/dev/random18?
/dev/urandom19?
/dev/ptypN2N舊式 BSD Pseudo-TTY 對,pty 是 master,tty 是 slave。第四個字母順序是 pqrstuvwxyzabcde,第五個字母是 0123456789abcdef。Unix98 用 major 128 和 136 以上,並使用 PTY master multiplex (/dev/ptmx) 動態取得 PTY。
/dev/ttypN3N
/dev/ttyN4Nvirtual console
/dev/ttySN464+NUART
/dev/tty50Current TTY device
/dev/console51System Console
/dev/ptmx52PTY master multiplex
/dev/psaux101PS/2 型式滑鼠
/dev/rtc10135Real Time Clock
/dev/input/eventN1364+N?
/dev/fbN29Nframe buffer
/dev/ttyPN57NHayes ESP serial card
/dev/ppp1080?
/dev/ttySA2045+N?
/dev/ttyAM20416+N?
/dev/ttyCPM20446+N?
/dev/ttyAMA
/dev/ttySAC
20464+N?
/dev/ttyPSC204148+N?
/dev/ttyUL204187+N?
/dev/ttymxc20716?
block devices
namemajorminor說明
/dev/ram11?
/dev/ramN1N?
/dev/loopN7N?

參考

  1. LDD ch3
  2. https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
  1. char 或 block 設備透過 /dev 下類型為 c 或 b 的檔案存取,ls -l /dev 可列出並看到 major 及 minor 號碼出現在時間之前。

2020年11月20日 星期五

devpts

devpts 是 Linux 虛擬檔案系統,一般掛載在 /dev/pts,包含 slaves to the multiplexing master /dev/ptmx 的 device file。

以往實作是提供固定數目的 master/slave pairs,如 /dev/ptyp63 和 /dev/ttyp63。見 The Linux Programming Interface §62 "Terminals" 和 §64 "Pseudoterminals"。

pseudoterminal ("pseudo TTY" 或 "PTY") 是包含 master 和 slave 的一對 pseudo-devices,slave emulates 一個實體電腦終端機,master 是程式的文字模式 UI 界面。兩者間像是雙向的 pipe,一方寫入會出現在另一方。Linux 提供很多 PTY 支援文字模式 UI,如 xterm、gnome-terminal、SSH 等。

參考

  1. https://en.wikipedia.org/wiki/Devpts
  2. https://www.kernel.org/doc/html/latest/filesystems/devpts.html

Linux ls 指令

-l:輸出 Long Listing 格式,包括下列資訊:
  • 檔案 type and permissions:有 10 個字母,第 1 個字母是 file type:
    • -:一般檔案
    • b:block device
    • c:character device
    • d:directory 目錄
    • l:Symbolic link
    • n:Network file
    • p:FIFO
    • s:socket
    剩下 9 個字母是 permissions
  • hard link 數目
  • owner
  • group
  • size
  • 最後更動日期時間
  • 名稱

-i:inode

參考

https://linuxize.com/post/how-to-list-files-in-linux-using-the-ls-command

2020年11月14日 星期六

busybox httpd

參數:
-iInetd mode
-fDo not daemonize
-v[v]Verbose
-c FILE設定檔 (預設 httpd.conf)
-p [IP:]PORTBind to ip:port (預設 *:80)
-u USER[:GRP]Set uid/gid after binding to port
-r REALMAuthentication Realm for Basic Authentication
-h HOME網頁目錄,預設 .
-m STRINGMD5 crypt STRING
-e STRINGHTML encode STRING
-d STRINGURL decode STRING
參考:https://busybox.net/downloads/BusyBox.html#httpd

設定檔:非必要,預設是 /etc/httpd.conf。

CGI 執行程式產生網頁:cgi-bin 目錄下

indexcgi, ssi

參考

  1. 我的簡易 Linux 程式 WEB UI 框架 
  2. https://www.geekyhacker.com/2018/06/03/bash-shell-cgi-http-server-using-busybox/

fstab

/etc/fstab 是 mount 的設定檔。開機過程中,1 號 process 會執行 inittab 的 sysinit 動作,可能有個動作就是 mount -a,會照 /etc/fstab 自動掛載所有需要的目錄。最後所有實際掛載狀況紀錄在 /proc/mounts (或 /etc/mtab)。

/etc/fstab 每行有 6 個欄位:device、dir、type、option、dump、pass。

  • device (儲存裝置):裝置路徑、UUID= 或 LABLE=,虛擬檔案系統用 none。如 /dev/sda1。
  • dir (掛載點)
  • type (檔案系統類型):如:ext4, reiserfs, xfs, jfs, smbfs, iso9660, vfat, ntfs, swap。
  • option (掛載參數):mount 的參數,不同檔案系類型會有不同的特殊選項可以使用。共通選項:
    • auto / noauto - 開機時或執行 'mount -a' 時是否自動掛載。預設 auto。
    • exec / noexec - 是否允許二進位執行檔執行。預設 exec。
    • ro - 唯讀。
    • rw - 可讀寫。
    • sync - synchronous I/O
    • async - asynchronous I/O
    • user - 允許任何用戶掛載 (implies noexec,nosuid,nodev unless overridden.)
    • nouser - 只允許 root 掛載 (default)
    • defaults - 使用預設掛載設定 (相當於 rw,suid,dev,exec,auto,nouser,async).
    • suid - Allow the operation of suid, and sgid bits. They are mostly used to allow users on a computer system to execute binary executables with temporarily elevated privileges in order to perform a specific task.
    • nosuid - Block the operation of suid, and sgid bits.
    • noatime - 不更新 inode access time。可增進效能 (see atime options).
    • nodiratime - 不更新 directory inode access time。可增進效能 (see atime options).
    • relatime - Update inode access times relative to modify or change time. Access time is only updated if the previous access time was earlier than the current modify or change time. (Similar to noatime, but doesn't break mutt or other applications that need to know if a file has been read since the last time it was modified.) Can help performance (see atime options).
  • dump:有 dump 工具程式時,用來決定何時備份。0 = 不備份、1 = 每日備份、2 = 隔日備份。
  • pass:決定 fsck 檢查順序:0 = 不檢查、1 = 根目錄優先檢查、2 = 其它要檢查的目錄。

http://linux.vbird.org/linux_basic/0230filesystem.php#bootup

https://wiki.archlinux.org/index.php/Fstab_(正體中文)

https://www.opencli.com/linux/fstab

udev 和裝置熱插拔

dnsmasq

dnsmasq (Wikipedia) 是輕量級的 DNS forwarder 加 DHCP Server:

  • DNS server,包括轉送查詢和查詢 /etc/hosts
  • DNS cache
  • DHCP server 支援 DHCPv4、DHCPv6、BOOTP 和 PXE
  • 唯讀的 TFTP server

設定

--server=[/[domain]/[domain/]][ipaddr[#port]][@interface][@source-ip[#port]]

  • ipaddr 是指定的上游 server 網址,可有 port
  • domain 是指定使用的網域,前後有斜線。多個 domain 只需要後面加斜線。
  • @interface, @source-ip 可有 port

https://thekelleys.org.uk/dnsmasq/doc.html

resolv.conf

2020年11月7日 星期六

busybox ping

支援 IPv4 (ping -4) 和 IPv6 (ping -6, ping6),或不指定 (ping)。

@networking/ping.c

簡單來說,首先透過 xhost_and_af2sockaddr(hostname, 0, af) 取得 sockaddr 格式的網址。

  • hostname 可以是名稱網址或數字網址。
  • 0 是沒用到的 port。
  • af 可以是 AF_INET 強制使用 IPv4、AF_INET6 強制使用 IPv6,或 AF_UNSPEC 不指定,由 hostname 判斷或依據 DNS 結果決定最終是 IPv4 還是 IPv6。
  • xhost_and_af2sockaddr() 實際上是呼叫 str2sockaddr(host, port, af, ai_flags),亦用在 traceroute、arping 等許多程式。host 格式也可以是 local:、[xx]:port、xx:port。host 中如果有 port 會取代函數傳入的 port。inet_aton() 檢查是否為數字 IPv4,inet_pton(AF_INET6) 檢查是否為數字 IPv6,否則需要 getaddrinfo()

ping 回應逾時等候使用 signal(SIGALRM) 和 alarm()。

最後發封包,IPv6 呼叫 ping6(),否則呼叫 ping4()。

參考

  1. busybox ping.c
  2. https://lirobo.blogspot.com/2014/11/tcpdump.html
  3. https://lirobo.blogspot.com/2010/10/dns.html
  4. https://lirobo.blogspot.com/2010/06/mtu.html

2020年11月6日 星期五

Rechargeable Battery 充電池

常見充電電池種類有鉛酸電池、鎳鎘電池 (NiCd)、鎳氫電池 (NiMH)、和鋰離子電池 (Lithium-ion battery)。鋰聚合物電池 (lithium polymer,Li-Po) 也是一種鋰離子電池,電解質使用膠狀或固態的聚合物。鋰電池 (Lithium battery) 內含純態的鋰金屬,為一次性使用、不可充電,如 CR123A (3V) 或鈕扣電池 CR2025、CR2032、BR2032。

鎳氫電池 (NiMH) 1900mAh x 1.2V = 2.2Wh

鋰離子電池 (Lithium-ion battery) 主要依靠鋰離子在正極和負極之間移動來工作,使用鋰化合物作為一個電極材料,主要常見的有:

電池正極工作電壓能量密度說明
ICR
鈷酸鋰 LiCoO23.7 V mAh/g漏液時會發生劇烈的化學反應而導致著火甚至爆炸。
IMR錳酸鋰 LiMn2O43.6 V mAh/g使用安全的化學原料,不需要保護電路,能承受更大的壓力和衝擊,雖然也會漏液,但是要比 ICR 電池的反應好太多。

鎳酸鋰 LiNiO4? V mAh/g
NCR
三元鋰
3.6 V mAh/g
IFR
磷酸鋰鐵 LiFePO4, LFP
3.2 V mAh/g

常見的圓柱型鋰離子電池規格 (五位數字:兩位直徑、兩位長度、一位 0 表示形狀圓柱型):

  • 26650
    • 5000mAh x 3.7V = 18.5 Wh
  • 21700
  • 18650:可透過套筒裝三顆 4 號電池取代。
    • 3350mAh x 3.7V = 12.3 Wh
    • 2200mAh x 3.7V = 8.1 Wh
    • 1800mAh x 3.7V = 6.6 Wh
  • 17670
  • 18500
  • 18350
  • 17500
  • 16340:相當於 CR123A 電池大小,兩節加起來和一節 18650 電池差不多。
  • 14500:接近 AA 電池 (台規 3 號)。
    • 600mAh x 3.7V = 2.2 Wh
  • 10440:接近 AAA 電池 (台規 4 號)。
    • 320mAh x 3.7V = 1.1 Wh

另外電池正極形狀也分為平頭與凸頭,平頭電池即標準尺寸,沒有保護板。凸頭體積稍微大一點,有保護板或沒有保護板。保護板防止電池過充、過放、過電流發生。RC (radio control, 遙控用) 鋰離子電池放電電流可達 20C,沒有保護板。

一般鋰離子電池正常電壓 3.6V ~ 4.2V。3.6V 電量 0%,4.2V 電量100%,約 80% 以下恆電流充電。約 80% 以上用 4.2V 左右恆電壓充電,直到電流 < 0.1C 時 (C 通常為電池標的 XX mAh 的 XX) 停止。高於 4.2V 是過度充電,繼續充電會爆炸。< 3V 過度放電,需要用低電流充電,等到 3V 時才能繼續使用。< 2.x V (依溫度而定) 就廢了。

磷酸鐵鋰電池電壓為 3.2V,充電截止電壓為3.6v。

原本 Gogoro 電池芯為 18650 規格,額定電量為 30.3Ah/1,309Wh。相除為電壓 43.2V,再除 3.6V = 12 顆電池蕊串聯。每顆 NCR18650BE 電池 3030mAh,需要 10 組並聯。
新 Gogoro電池改採 21700 鋰三元電池芯,額定電量 37.02Ah/1,612.6Wh。

容量

最大可持續放電電流

瞬間電流

循環壽命:鋰離子一般可達到 500 次以上,甚至1000次以上。磷酸鐵鋰可達 2000 次以上。

依放電能力分成兩類,以容量為主的能量型電池 (如松下 18650be) 和以放電輸出為主的動力型電池 (如 Sony 的 VTC 系列)。

放電指標 C 用來表示電池充放電時電流和容量的比率,如 2500mAh 電池,1C 表示2.5A。

充電電池沒匹配不適合並聯充放電,也不適合串聯充電。[參考]

參考

  1. 行動電源

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...