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

SIP header Via

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