- 同時按電源及音量小聲鍵
- 快速設定小工具可以選四種,其中一個可設為螢幕截圖
- ASUS 個人化設定可將長按最近使用的應用程式鍵設為螢幕截圖
星期二, 2月 23, 2016
星期日, 2月 14, 2016
GCC 使用 MIPS DSP ASE
gcc 編譯選項
- 加「-mdsp」(24KE, 34K) 或「-mdsp2」(74K, M14KE),不然會有「undefined reference to `__ssaddhq3'」之類的錯誤
- 加「-mips32r2」可使用 INS 指令,存取 SIMD 變數的單元有較好的效能
- M14KE 的 DSP instructions 可組譯成 microMIPS opcode (加「-mmicromips」)
- __mips_dsp 或 __mips_dsp_rev=1
- __mips_dspr2 或 __mips_dsp_rev=2
- __MIPSEL__
- typedef short q15;
q15 a = 0.1234 * 32768.0; - typedef int q31;
q31 b = 0.2468 * 2147483648.0; - typedef long long a64;
- typedef signed char v4i8 __attribute__ ((vector_size(4)));
v4i8 a = {1, 2, 3, 4};
v4i8 b;
b = (v4i8) {5, 6, 7, 8}; - typedef short v2q15 __attribute__ ((vector_size(4)));
v2q15 a = {0x0fcb, 0x3a75};
v2q15 b;
b = (v2q15) {0.1234 * 32768.0, 0.4567 * 32768.0}; - endian 問題,詳見 MD00485 2.4 2.5
- fractional data 的 +, - 跟 integer data 一樣 (addu, subu),但 * (乘法) 後需要位移來對齊小數點
- SIMD 變數可以使用 +, -, *, /, unary minus, ^, |, &, ~ 作運算,但只有 +, - 有指令 (addu.qb, subu.qb, addq.ph, subq.ph),其它 GCC 則 synthesizes 一系列指令。
DSP Control Register
- 有 6 欄位:
name bits mask 說明 CCOND 27:24 16 condition code OUFLAG 23:16 8 overflow/underflow。只能用 wrdsp 清掉
16:HI-LO 0
17:HI-LO 1
18:HI-LO 2
19:HI-LO 3
20:
21:
22:
23:EFI 14 32 extract fail indicator C 13 4 carry bit,用在 addsc, addwc SCOUNT 12:7 2 size count POS 5:0 1 position bits - 專用來存取的指令:rddsp/wrdsp
- SCOUNT 及 POS 視為全域變數,會改變它們的指令或函數不會優化掉,包括 wrdsp, extpdp, extpdpv、及 mthlip。
- For correctness, programmers must assume that a function call clobbers all fields of the DSP control register. That is, programmers cannot depend on the values in CCOND, OUFLAG, EFI or C across a function-call boundary. They must re-initialize the values of CCOND, OUFLAG, EFI or C before using them.
內建函數已對 pipeline 延遲做最佳化,而 macro 沒有,而造成 code scheduling 較差、及增加 stall。
C 內建函數 (Intrinsics)
- Q31
- q31 __builtin_mips_addq_s_w (q31, q31);
q31 __builtin_mips_subq_s_w (q31, q31); - q31 __builtin_mips_absq_s_w (q31);
- q31 __builtin_mips_shll_s_w (q31, imm0_31);
q31 __builtin_mips_shll_s_w (q31, i32); - q31 __builtin_mips_shra_r_w (q31, imm0_31);
q31 __builtin_mips_shra_r_w (q31, i32); - a64 __builtin_mips_dpaq_sa_l_w (a64, q31, q31); // Q63 + Q31 * Q31 => Q63
a64 __builtin_mips_dpsq_sa_l_w (a64, q31, q31); // Q63 - Q31 * Q31 - q31 __builtin_mips_mulq_rs_w (q31, q31); // DSPR2
- q31 __builtin_mips_mulq_s_w (q31, q31); // DSPR2
- q31 __builtin_mips_addqh_w (q31, q31); // DSPR2
q31 __builtin_mips_addqh_r_w (q31, q31); // DSPR2 - q31 __builtin_mips_subqh_w (q31, q31); // DSPR2
q31 __builtin_mips_subqh_r_w (q31, q31); // DSPR2 - Q15
- v2q15 __builtin_mips_addq_ph (v2q15, v2q15);
v2q15 __builtin_mips_addq_s_ph (v2q15, v2q15);
v2q15 __builtin_mips_subq_ph (v2q15, v2q15);
v2q15 __builtin_mips_subq_s_ph (v2q15, v2q15); - v2q15 __builtin_mips_absq_s_ph (v2q15);
- v2q15 __builtin_mips_shll_ph (v2q15, imm0_15);
v2q15 __builtin_mips_shll_ph (v2q15, i32);
v2q15 __builtin_mips_shll_s_ph (v2q15, imm0_15);
v2q15 __builtin_mips_shll_s_ph (v2q15, i32); - v2q15 __builtin_mips_shra_ph (v2q15, imm0_15);
v2q15 __builtin_mips_shra_ph (v2q15, i32);
v2q15 __builtin_mips_shra_r_ph (v2q15, imm0_15);
v2q15 __builtin_mips_shra_r_ph (v2q15, i32); - v2q15 __builtin_mips_mulq_rs_ph (v2q15, v2q15); // Q15 * Q15 => Q15
- a64 __builtin_mips_dpaq_s_w_ph (a64, v2q15, v2q15); // 累加兩個乘積
a64 __builtin_mips_dpsq_s_w_ph (a64, v2q15, v2q15); // 累減兩個乘積 - a64 __builtin_mips_mulsaq_s_w_ph (a64, v2q15, v2q15); // 累加第一個乘積,減第二個乘積,跟 endian 有關
- a64 __builtin_mips_maq_s_w_phl (a64, v2q15, v2q15); // 只累加其中一個乘積
a64 __builtin_mips_maq_s_w_phr (a64, v2q15, v2q15);
a64 __builtin_mips_maq_sa_w_phl (a64, v2q15, v2q15);
a64 __builtin_mips_maq_sa_w_phr (a64, v2q15, v2q15); - q31 __builtin_mips_muleq_s_w_phl (v2q15, v2q15); // Q15 * Q15 => Q31,只相乘一組
q31 __builtin_mips_muleq_s_w_phr (v2q15, v2q15); - Replicate a Fixed Half-word into Elements
v2q15 __builtin_mips_repl_ph (imm_n512_511);
v2q15 __builtin_mips_repl_ph (i32); - void __builtin_mips_cmp_eq_ph (v2q15, v2q15);
void __builtin_mips_cmp_lt_ph (v2q15, v2q15);
void __builtin_mips_cmp_le_ph (v2q15, v2q15); - v2q15 __builtin_mips_pick_ph (v2q15, v2q15);
- v2q15 __builtin_mips_packrl_ph (v2q15, v2q15);
- v2q15 __builtin_mips_mulq_s_ph (v2q15, v2q15); // DSPR2
- v2q15 __builtin_mips_addqh_ph (v2q15, v2q15); // DSPR2
v2q15 __builtin_mips_addqh_r_ph (v2q15, v2q15); // DSPR2 - v2q15 __builtin_mips_subqh_ph (v2q15, v2q15); // DSPR2
v2q15 __builtin_mips_subqh_r_ph (v2q15, v2q15); // DSPR2 - a64 __builtin_mips_dpaqx_s_w_ph (a64, v2q15, v2q15); // DSPR2
a64 __builtin_mips_dpaqx_sa_w_ph (a64, v2q15, v2q15); // DSPR2
a64 __builtin_mips_dpsqx_s_w_ph (a64, v2q15, v2q15); // DSPR2
a64 __builtin_mips_dpsqx_sa_w_ph (a64, v2q15, v2q15); // DSPR2 - 8-bit
- v4i8 __builtin_mips_addu_qb (v4i8, v4i8);
v4i8 __builtin_mips_addu_s_qb (v4i8, v4i8);
v4i8 __builtin_mips_subu_qb (v4i8, v4i8);
v4i8 __builtin_mips_subu_s_qb (v4i8, v4i8); - i32 __builtin_mips_raddu_w_qb (v4i8);
- v4i8 __builtin_mips_shll_qb (v4i8, imm0_7);
v4i8 __builtin_mips_shll_qb (v4i8, i32);
v4i8 __builtin_mips_shrl_qb (v4i8, imm0_7);
v4i8 __builtin_mips_shrl_qb (v4i8, i32); - a64 __builtin_mips_dpau_h_qbl (a64, v4i8, v4i8);
a64 __builtin_mips_dpau_h_qbr (a64, v4i8, v4i8);
a64 __builtin_mips_dpsu_h_qbl (a64, v4i8, v4i8);
a64 __builtin_mips_dpsu_h_qbr (a64, v4i8, v4i8); - v4i8 __builtin_mips_repl_qb (imm0_255);
v4i8 __builtin_mips_repl_qb (i32); - void __builtin_mips_cmpu_eq_qb (v4i8, v4i8);
void __builtin_mips_cmpu_lt_qb (v4i8, v4i8);
void __builtin_mips_cmpu_le_qb (v4i8, v4i8);
i32 __builtin_mips_cmpgu_eq_qb (v4i8, v4i8);
i32 __builtin_mips_cmpgu_lt_qb (v4i8, v4i8);
i32 __builtin_mips_cmpgu_le_qb (v4i8, v4i8);
i32 __builtin_mips_cmpgdu_eq_qb (v4i8, v4i8); // DSPR2
i32 __builtin_mips_cmpgdu_lt_qb (v4i8, v4i8); // DSPR2
i32 __builtin_mips_cmpgdu_le_qb (v4i8, v4i8); // DSPR2 - v4i8 __builtin_mips_pick_qb (v4i8, v4i8);
- v4i8 __builtin_mips_absq_s_qb (v4i8); // DSPR2
- v4i8 __builtin_mips_adduh_qb (v4i8, v4i8); // DSPR2
v4i8 __builtin_mips_adduh_r_qb (v4i8, v4i8); // DSPR2 - v4i8 __builtin_mips_shra_qb (v4i8, imm0_7); // DSPR2
v4i8 __builtin_mips_shra_r_qb (v4i8, imm0_7); // DSPR2
v4i8 __builtin_mips_shra_qb (v4i8, i32); // DSPR2
v4i8 __builtin_mips_shra_r_qb (v4i8, i32); // DSPR2 - v4i8 __builtin_mips_subuh_qb (v4i8, v4i8); // DSPR2
v4i8 __builtin_mips_subuh_r_qb (v4i8, v4i8); // DSPR2 - a64
- i32 __builtin_mips_extr_w (a64, imm0_31);
i32 __builtin_mips_extr_w (a64, i32);
i32 __builtin_mips_extr_r_w (a64, imm0_31);
i32 __builtin_mips_extr_r_w (a64, i32);
i32 __builtin_mips_extr_rs_w (a64, imm0_31);
i32 __builtin_mips_extr_rs_w (a64, i32); - i32 __builtin_mips_extr_s_h (a64, imm0_31);
i32 __builtin_mips_extr_s_h (a64, i32); - i32 __builtin_mips_extp (a64, imm0_31);
i32 __builtin_mips_extp (a64, i32); - i32 __builtin_mips_extpdp (a64, imm0_31);
i32 __builtin_mips_extpdp (a64, i32); - a64 __builtin_mips_shilo (a64, imm_n32_31);
a64 __builtin_mips_shilo (a64, i32); - a64 __builtin_mips_mthlip (a64, i32);
- Precision Reduce/Expand
- v4i8 __builtin_mips_precrq_qb_ph (v2q15, v2q15);
- v4i8 __builtin_mips_precrqu_s_qb_ph (v2q15, v2q15);
- v2q15 __builtin_mips_precequ_ph_qbl (v4i8);
v2q15 __builtin_mips_precequ_ph_qbr (v4i8);
v2q15 __builtin_mips_precequ_ph_qbla (v4i8);
v2q15 __builtin_mips_precequ_ph_qbra (v4i8); - v2q15 __builtin_mips_preceu_ph_qbl (v4i8);
v2q15 __builtin_mips_preceu_ph_qbr (v4i8);
v2q15 __builtin_mips_preceu_ph_qbla (v4i8);
v2q15 __builtin_mips_preceu_ph_qbra (v4i8); - v4i8 __builtin_mips_precr_qb_ph (v2i16, v2i16); // DSPR2
- v2q15 __builtin_mips_precrq_ph_w (q31, q31);
- v2q15 __builtin_mips_precrq_rs_ph_w (q31, q31);
- q31 __builtin_mips_preceq_w_phl (v2q15);
q31 __builtin_mips_preceq_w_phr (v2q15); - Int8 * Q15 => Q15
- v2q15 __builtin_mips_muleu_s_ph_qbl (v4i8, v2q15);
v2q15 __builtin_mips_muleu_s_ph_qbr (v4i8, v2q15); - Add and Set Carry/Add with Carry
i32 __builtin_mips_addsc (i32, i32);
i32 __builtin_mips_addwc (i32, i32); - Modular Subtraction on an Index Value
i32 __builtin_mips_modsub (i32, i32); - Bit Reverse a Half-word
i32 __builtin_mips_bitrev (i32); - Insert Bit Field Variable
i32 __builtin_mips_insv (i32, i32); - Load Unsigned Byte/Halfword/Word Indexed
i32 __builtin_mips_lbux (void *, i32);
i32 __builtin_mips_lhx (void *, i32);
i32 __builtin_mips_lwx (void *, i32); - Signed Multiply and Add
a64 __builtin_mips_madd (a64, i32, i32); - Unsigned Mulitply and Add
a64 __builtin_mips_maddu (a64, ui32, ui32); - Signed Multiply and Subtract
a64 __builtin_mips_msub (a64, i32, i32); - Unsigned Multiply and Subtract
a64 __builtin_mips_msubu (a64, ui32, ui32); - Signed Multiply
a64 __builtin_mips_mult (i32, i32); - Unsigned Multiply
a64 __builtin_mips_multu (ui32, ui32); - Left Shift and Append Bits (DSPR2)
i32 __builtin_mips_append (i32, i32, imm0_31); // DSPR2 - Byte Align Contents from Two Registers (DSPR2)
i32 __builtin_mips_balign (i32, i32, imm0_3); // DSPR2 - Right Shift and Prepend Bits (DSPR2)
i32 __builtin_mips_prepend (i32, i32, imm0_31); // DSPR2 - 3.9, 3.10
- 全組合語言,或夾雜在 C 裡面
- 使用 Fixed-point data types (_Frac) 搭配 C 運算子
- MD00485 -- 主要參考來源
- MD00086 The MIPS32® Instruction Set
- MD00374 The MIPS® DSP Application-Specific Extension to the MIPS32® Architecture
- GCC: Using Vector Instructions through Built-in Functions
星期四, 2月 04, 2016
Linux 書
- The Linux Programming Interface (October 2010, 描述 Linux/UNIX 系統程式設計的細節,作者 Michael Kerrisk 是 Linux man pages 的維護者)
- Linux in a Nutshell by Ellen Siever, Stephen Figgins, Robert Love and Arnold Robbins (Sep 29, 2009)
- Embedded Linux Primer: A Practical Real-World Approach, 2/E by Christopher Hallinan (Nov 5, 2010,開發圖書代理,中文版)
- Computers as Components: Principles of Embedded Computing System Design, 3e (2012 全華代理)
- Making Embedded Systems: Design Patterns for Great Software by Elecia White (Nov 9, 2011,中文版)
- Linux Device Drivers, 3rd Ed. (內容列表)
- Linux Kernel Development (3rd Ed. 2010)
- ch. 2 Process Management 的筆記
- Rex 的 Linux 系統軟體開發書籍推薦與 TLPI
malloc(), calloc(), realloc() 和 alloca()
可以從兩個所在動態取得記憶體,一般 malloc() 是從 heap,另一款是從 stack。
從 stack 動態配置記憶體,函數結束自動釋出。只是調整 stack 指標,效率足好。
#include <alloca.h> void *alloca(size_t size);
- 可簡化 longjmp() 或 siglongjmp() 的記憶體釋放,不鼓勵用在其它地方 (為什麼???)
- 不能直接用在函數參數內,配置的記憶體夾在函數參數間可能會有問題。
- stack overflow 時不會傳回 NULL,SIGSEGV
從 heap 動態配置記憶體
#include <stdlib.h> // 取得 size 大小位元組、未初始化的記憶體空間。 void *malloc(size_t size); // 從 heap 動態配置的記憶體,都需要用 free() 歸還,或者在程式結束時自動消失。 // 重複釋出可能發生不可預期的結果。ptr 可以是 NULL,不進行任何動作。 void free(void *ptr); // 基於 malloc(),取得 nmemb * size 大小的記憶體空間,並清為 0。 void *calloc(size_t nmemb, size_t size); // 記憶體空間重新配置為 size 大小,重複部份會相同。 // 加大配置的空間時,位置可能變或不變。位置改變時會複製重複的部份,效能會較差。 void *realloc(void *ptr, size_t size);
其它使用 malloc() 的函式:strdup() 等。
malloc() 細節
heap 一般放在 Uninitialize data (bss) 後面生向高位址,另一端是 stack 生向 heap。
Kernel | |
0xC000 0000 | |
argv, environment | |
Stack | |
Top of stack → | |
↓ (unallocated memory) ↑ | |
Program break → | |
Heap | |
Uninitialized data (bss) | |
Initialized data | |
Text (program code) | |
0x0804 8000 | |
0x0000 0000 | |
heap 跟 stack 中間是實際沒用到的記憶體。heap 的盡頭就是 program break,不夠用時會用 sbrk() 調整 program break 來加大 heap,但通常不會縮減 heap。sbrk(0) 傳回目前 program break 位置。
藏在每一個 malloc() 的記憶體前面,會紀錄這段記憶體的大小。當 free() 時,利用記憶體空間形成 doubly link list 歸還回到 free 列表,並嘗試合併相鄰的記憶體區塊。
配置的記憶體區塊 (前置區塊大小) | |||
size | 可使用的部份 | ||
已釋出可使用的列表 (利用可使用的部份存放 link list 指標) | |||
size | prev | next | 剩下可使用的部份 |
每次 malloc() 會先從 free 列表取得記憶體,例如用 first-fit 或 best-fit 等演算法,如取不到則嘗試加大 heap。
當配置的記憶體區塊大於 MMAP_THRESHOLD,glibc 改用系統呼叫 mmap() 配置,在 Linux 4.7 之後會受到 RLIMIT_DATA 限制。MMAP_THRESHOLD 預設 128 kB,可透過 mallopt() 調整。
在 multithread 應用,內部使用 mutex 保護相關資料結構,glibc 並建立額外的 memory allocation arenas。每個 arena 是內部用 brk() 或 mmap() 配置的大記憶體空間。
每個 process 有自己的虛擬記憶體,虛擬記憶體中實際有使用的區塊才會對應到實體記憶體。malloc() 不保證可以取得實體記憶體,萬一沒有實體記憶體體了,有些 process 會被 OOM killer 結束掉 (見 proc 檔案系統 /proc/sys/vm/overcommit_memory 和 /proc/sys/vm/oom_adj 的說明,以及 Linux kernel 的 Documentation/vm/overcommit-accounting)。
malloc() 除錯
mtrace()、muntrace()mcheck()、mprobe()
malloc() 除錯函式庫:Electric Fence (http://www.perens.com/FreeSoftware/)、dmalloc (http://dmalloc.com/)、Valgrind (http://valgrind.org/)、Insure++ (http://www.parasoft.com/)。
mallopt()、mallinfo()
參考
- TLPI §7
- man alloca
- malloc() man-page
- asprintf()
- mmap()
- memalign()、posix_memalign()
- malloc_trim()
Linux CPU 排程
註:preempty 是一種插隊的行為,像救護車,可翻作...。
由於 thread 是 preemptive 的,同一 process 內不同 thread 共用的資料,可能修改到一半有其它 thread 存取而造成錯誤 (修改被覆蓋、資料結構破壞等),所以需要 mutex 保護。函式可能重複呼叫,要看是否可以 reentry。
每個 thread 有各自的 policy 及 priority 來決定排程,都會 preemptive 執行 priority 值最大的,相同 priority 的再由 policy 安排如何執行。thread 建立時會繼承 policy 及 priority。
等級 | policy | priority | 說明 |
---|---|---|---|
一般 | SCHED_OTHER (預設) | 0 | 參考 nice 值動態調整 CPU 時間的使用比例,讓每個 thread 輪流、不用等候太久都能分配到 CPU 時間,滿足互動式多工系統。 |
SCHED_BATCH | 2.6.16+。類似 SCHED_OTHER,但排程會減少喚醒次數來減少 thread 切換,一般用在非互動式的程式。 | ||
SCHED_IDLE | 2.6.23+。以最低優先權執行。 | ||
(soft) real time | SCHED_FIFO | 1~99 | 一開始先插隊執行,之後次序照先後排隊。每次執行直到等待 IO 或讓出。 |
SCHED_RR | 同 SCHED_FIFO,但每次執行有 time slice 時間限制。 | ||
SCHED_DEADLINE | Linux 3.14+ |
nice 值 (在 POSIX 是 process 層級的屬性,但目前 Linux/NPTL 實作是 thread 層級的屬性。)
- 範圍 -20 ~ 19,預設 0,值越大越「好心」,排程分配的份量越少。對應到 kernel 內部表示的範圍是 40 ~ 1,相當於 20 - nice 值。不同 kernel 版本排程演算法有所不同。
- 可以提高自己的 nice 值,只有 privileged thread 才能降低或設為負值 (chap35.3.2)。自從 Linux 2.6.12,process 有適當的 RLIMIT_NICE 軟性限制 (見 getrlimit()) 可以降低 nice 值。
- round-robin timing-sharing 的 CPU 時間分配,滿足互動式多工系統需要的 Fairness (公平) 和 Responsive (不用等太久),按順序都會分配到 (不會餓死) time slice 或 quantum 的時間,除非自願放棄 (休息或等候) 才會縮短。
- 會繼承
- Linux 2.6.38 新增 "autogroup" 功能代表 nice 值在許多 circumstances 不再有其傳統效果。
- getpriority(which, who):取得 which 中 who 裡 nice 值最小的。
- which 可以是 PRIO_PROCESS (指 thread)、PRIO_PGRP、或 PRIO_USER,決定 who 是 process ID、process group ID、或 user ID。who 為 0 表示本身所屬 process、process group、或 user。
- 由於 -1 可能是有效的值或錯誤,需先設 errno 為 0,如果回傳值是 -1 時,需檢查 errno 判別。
- setpriority(which, who, value):
- 有 CAP_SYS_NICE 權限可設其它 thread nice 值
- 沒權限只能增加符合 real 或 effective user ID 的 thread。Kernel v2.6.12+ 的 RLIMI_NICE 限定可設定的範圍。
- nice(incr)
- 指令 nice
- 指令 renice
CPU affinity mask => 在多處理器系統決定可使用哪些處理器。
註:sched_get_priority_min(policy) 及 sched_get_priority_max(policy) 取得特定 policy 的 priority 範圍。
註:sched_setscheduler() 設定 pid 或自己的 policy 及 priority,而sched_setparam() 只設定 priority。sched_getscheduler()、sched_getparam() 分別取得 policy 和 prioity。在 2.6.12 以前, CAP_SYS_NICE 的 process 才能設定 policy 和 priority。另外,SCHED_OTHER process 的 real/effective user ID 和我的effective user ID 相同的,也可以設定。2.6.12 之後新增 RLIMIT_RTPRIO...。
s
避免 realtime process locking up 整個系統
- 用 setrlimit() 設定適合的 low soft CPU time resource limit (RLIMIT_CPU, described in Section 36.3),如果 consumes too much CPU time 會送 SIGXCPU signal,預設 kill the process。
- 用 alarm() 設定 alarm timer,如果執行超過時間會 killed by a SIGALRM signal。
- 建立 watchdog process 執行在較高 realtime priority,無限迴圈 sleep 一段時間檢查其它 process 狀態,可順便量測 CPU time (Section 23.5.3 的 clock_getcpuclockid()) 和檢查 policy 和 priority,可降低 priority 或送 signal 停止會結束它。
- 2.6.25 開始提供 RLIMIT_RTTIME 限制一次持續 CPU time 用量,送 signal SIGXCPU。細節見 Documentation/ scheduler/sched-rt-group.txt。
sched_yield()
註:sched_rr_get_interval() 取得 SCHED_RR 每次執行時間。
註:2.6.25+ /proc/sys/kernel/sched_rt_period_us/proc/sys/kernel/sched_rt_runtime_u
參考來源:
20200713 最後更新