2016年7月24日 星期日

scanf()

輸入格式轉換,存到記憶體

#include <stdio.h>

int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

#include <stdarg.h>

int vscanf(const char *format, va_list ap);
int vsscanf(const char *str, const char *format, va_list ap);
int vfscanf(FILE *stream, const char *format, va_list ap);

format 是一個字串,描述如何處理輸入字元。如果沒有輸入、或比對失敗,就停止,回傳轉換成功的次數。format 內容可以是:
  • 空白字元集,包括 space、tab、newline 等,比對 0 個以上的空白字元集。
  • % 開頭的轉換規範。
  • 其它一般字元
每個轉換規範以 '%' 或 %n$ 開始,後者指定用第幾個變數存。接著:
  • (optional) '*':依照轉換定義讀取,但不儲存,不計成功轉換次數。
  • (optional) 'm':用在字串轉換 (%s, %c, %[),自動配置足夠的記憶體放字串,之後需要 free()。
  • (optional) 十位數字說明最大 field width,不含字串結尾 0。
  • (optional) 改變存成的資料型態
    • h:存成 short int 或 unsigned short int
    • hh:存成 signed char 或 unsigned char
    • j:存成 intmax_t 或 uintmax_t
    • l:整數存成 long 或 unsigned long,浮點存成 double
    • ll:跟 L 一樣,用在 %c 或 %s 表示寬字元。
    • L:整數存成 long long,浮點存成 long double
    • t:存成 ptrdiff_t
    • z:存成 size_t
  • 轉換定義
    • %:比對 %
    • d:比對十進位整數,可以是有號,結果存到 int
    • i:比對對整數,可以是有號,可以是 0x 或 0X 開始的十六進位數字、0 開頭的八進位數字、不然則是十進位。結果存到 int。
    • o:比對無號八進位整數,結果存到 unsigned int。
    • u:比對無號十進位整數,結果存到 unsigned int。
    • x, X:比對無號十六進位整數,結果存到 unsigned int。
    • f, e, g, E, a:有號浮點數字,結果存到 float。
    • s:不含 white-space 的字串,存到字串。
    • c:數個字元,存到 char 陣列。
    • [字元集]:比對字元集存到字串。字元集不能是空的,不省略開頭的 white space。排除這些字元集的話,字元集以 ^ 開始。如果字元集要包含 ],可放在第一個字元。兩字元間可夾 - 表示範圍。例如 [^]0-9-] 表示任何字元不含 ]、0 到 9、-。
    • p:比對指標值存到 void 指標。
    • n:計數字元數目存到 int。可能不計成功的轉換次數。

問題:可以忽略開頭字元嗎?

參考:printf

2016年7月23日 星期六

SIP REGISTER

SIP 註冊 [RFC3261 §10]

SIP 用戶在網域 (domain) 中有個帳號「user@domain」,稱為 address-of-record,但連結到用戶實際所在的位址 (binding) 是可以變動的,可透過 SIP REGISTER 請求向 registrar 登記目前的位址。

registrar 專指接受並回覆 SIP REGISTER 請求的 UAS,在其管理的網域維護 binding 對應表,讓其它用戶要建立通話時可透過 proxy server 或 redirect server 查詢。

                                              bob
                                            +----+
                                            | UA |
                                            |    |
                                            +----+
                                               |
                                               |3)INVITE
                                               |   carol@chicago.com
      chicago.com        +--------+            V
      +---------+ 2)Store|Location|4)Query +-----+
      |Registrar|=======>| Service|<=======|Proxy|sip.chicago.com
      +---------+        +--------+=======>+-----+
            A                      5)Resp      |
            |                                  |
            |                                  |
  1)REGISTER|                                  |
            |                                  |
         +----+                                |
         | UA |<-------------------------------+
cube2214a|    |                            6)INVITE
         +----+                    carol@cube2214a.chicago.com
          carol

SIP REGISTER 可進行的動作包括建立 (create)、新增 (add)、更新效期 (refresh)、詢問 (query)、移除 (delete)、及修改 (modify)。每個用戶可有多個位址,可能是多個 UAC 註冊的不同位址,或者是不同情況使用的位址。每個位址都有效期限,到期前需要更新效期才能持續使用。效期設為 0 將位址移除,之後可再新增來達到修改的目的。

REGISTER 請求
  • Request-URI (必要):domain,例如 "sip:chicago.com",不能有 "userinfo" 及 "@"。
  • To (必要):要建立、詢問、或修改的 address-of-record,包含有 user name。
  • From (必要):負責註冊者的 address-of-record,通常跟 To: 一致,或者由第三方註冊。
  • Call-ID 及 CSeq (必要):對特定 registrar,UAC 每次開機後應該使用相同的 Call-ID,搭配 CSeq 每次請求加一,依此 registrar 可以偵測出沒按照順序到達的 REGISTER 請求。(反之,如果 Call-ID 不同便無法偵測順序是否正確)
  • Contact (may): 要登記連結的位址 (address bindings),可有多個,可以是 sip:、tel:、mailto: 等。可加 q 參數作為偏好使用。沒此項欄位時用來詢問。
  • Contact 各自的 expires 參數、或 Expires (may):到期秒數,註冊時可建議,但最終由 registar 決定。大於 232-1 (4294967295 秒或 136 年) 可視為 232-1,Malformed 值應該視為 3600。0 表示移除,此時 Contact 可用 * 移除所有位址連結。
  • Route (may)
  • Record-Route (有可能會有,但忽略):因為不會建立 dialog,所以沒效用。
  • Contact 的 action 參數:來自 RFC 2543,deprecated,不該使用。
動作ContactExpire
建立特定位址建議值
新增特定位址建議值
更新效期特定位址建議值
詢問不需要
移除特定位址,或 * 表全部。0
修改特定位址建議值

registrar response:

  • 2xx 的 Contact 會列出所有的 binding。註冊成功的 UA 找出自己那一項,根據其 expires 參數或 Expires 欄位值計算並更新有效期限。
  • Date: 用來設定時間。
  • 423 (Interval Too Brief) 的 Min-Expires:可使用等於或大於 Min-Expires 的 expires 值重新嘗試註冊。
  • 501 Not Implemented:不支援單純詢問。
  • 不能產生 6xx,不能有 Record-Route。

registrar 位址除了使用設定外,也可以用 domain 位址 DNS 取得,或 well-known "all SIP servers" 的 multicast 位址 "sip.mcast.net" (IPv4 是 224.0.1.75,IPv6 ?)。但使用 multicast 只是讓 registrar 知道位置,不會有回應 (為什麼?)。

參考

  • From TS 24.229:Unless either the user or the application within the UE has determined that a continued registration is not required the UE shall reregister an already registered public user identity either 600 seconds before the expiration time if the previous registration was for greater than 1200 seconds, or when half of the time has expired if the previous registration was for 1200 seconds or less, or when the UE intends to update its capabilities according to RFC 3840 [62] or when the UE needs to modify the ICSI values that the UE intends to use in a g.3gpp.icsi-ref media feature tag or IARI values that the UE intends to use in the g.3gpp.iari-ref media feature tag.
  • M. Schooler, "A multicast user directory service for synchronous rendezvous," Master's Thesis CS-TR-96-18, Department of Computer Science, California Institute of Technology, Pasadena, California, Aug. 1996.

2016年2月22日 星期一

手機螢幕擷取

Zenfone 2 螢幕擷取方式
  1. 同時按電源及音量小聲鍵
  2. 快速設定小工具可以選四種,其中一個可設為螢幕截圖
  3. ASUS 個人化設定可將長按最近使用的應用程式鍵設為螢幕截圖
參考來源:http://www.androidcentral.com/how-take-screenshot-asus-zenfone-2

2016年2月13日 星期六

GCC 使用 MIPS DSP ASE

MIPS DSP ASE

gcc 編譯選項
  • 加「-mdsp」(24KE, 34K) 或「-mdsp2」(74K, M14KE),不然會有「undefined reference to `__ssaddhq3'」之類的錯誤
  • 加「-mips32r2」可使用 INS 指令,存取 SIMD 變數的單元有較好的效能
  • M14KE 的 DSP instructions 可組譯成 microMIPS opcode (加「-mmicromips」)
C 定義
  • __mips_dsp 或 __mips_dsp_rev=1
  • __mips_dspr2 或 __mips_dsp_rev=2
  • __MIPSEL__
Data types 及變數初始化
  • 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
C 運算子
  • fractional data 的 +, - 跟 integer data 一樣 (addu, subu),但 * (乘法) 後需要位移來對齊小數點
  • SIMD 變數可以使用 +, -, *, /, unary minus, ^, |, &, ~ 作運算,但只有 +, - 有指令 (addu.qb, subu.qb, addq.ph, subq.ph),其它 GCC 則 synthesizes 一系列指令。
暫存器多三組 HI-LO (共四組) 及 DSPControl
DSP Control Register
  • 有 6 欄位:
    namebitsmask說明
    CCOND 27:2416condition code
    OUFLAG 23:168overflow/underflow。只能用 wrdsp 清掉
    16:HI-LO 0
    17:HI-LO 1
    18:HI-LO 2
    19:HI-LO 3
    20:
    21:
    22:
    23:
    EFI 1432extract fail indicator
    C134carry bit,用在 addsc, addwc
    SCOUNT 12:72size count
    POS5:01position 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.
比較:使用內建函數 (Intrinsic) 及組合語言巨集 (macro)
內建函數已對 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 運算子
參考資料:
  1. MD00485 -- 主要參考來源
  2. MD00086 The MIPS32® Instruction Set
  3. MD00374 The MIPS® DSP Application-Specific Extension to the MIPS32® Architecture
  4. GCC: Using Vector Instructions through Built-in Functions
相關文章:
  1. GCC Inline Assembly

2016年2月4日 星期四

Linux 書

Linux Kernel
參考來源
  1. Rex 的 Linux 系統軟體開發書籍推薦與 TLPI
延伸閱讀

Linux CPU 排程

Linux 程式執行以 thread 為單位,決定誰取得 CPU 使用權稱排程。Thread 在 userspace 是 preemptible 的 (非 cooperative multitasking),任何時間點 kernel tick 都有可能會中斷程式並切換到其它 thread,一個無限迴圈並不會卡住整個系統。至於透過 syscall 進入 kernel 的階段,是否 preemptible 由 kernel 設定 CONFIG_PREEMPT 決定。在 non-preemptive kernel,無限迴圈是會卡住整個系統。

註:preempty 是一種插隊的行為,像救護車,可翻作...。

由於 thread 是 preemptive 的,同一 process 內不同 thread 共用的資料,可能修改到一半有其它 thread 存取而造成錯誤 (修改被覆蓋、資料結構破壞等),所以需要 mutex 保護。函式可能重複呼叫,要看是否可以 reentry

每個 thread 有各自的 policy 及 priority 來決定排程,都會 preemptive 執行 priority 值最大的,相同 priority 的再由 policy 安排如何執行。thread 建立時會繼承 policy 及 priority。

等級policypriority說明
一般SCHED_OTHER
(預設)
0參考 nice 值動態調整 CPU 時間的使用比例,讓每個 thread 輪流、不用等候太久都能分配到 CPU 時間,滿足互動式多工系統。
SCHED_BATCH2.6.16+。類似 SCHED_OTHER,但排程會減少喚醒次數來減少 thread 切換,一般用在非互動式的程式。
SCHED_IDLE2.6.23+。以最低優先權執行。
(soft) real timeSCHED_FIFO1~99一開始先插隊執行,之後次序照先後排隊。每次執行直到等待 IO 或讓出。
SCHED_RR同 SCHED_FIFO,但每次執行有 time slice 時間限制。
SCHED_DEADLINELinux 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
POSIX realtime scheduling API 決定排程 policy 和 priority => 更緊密控制 CPU 時間
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_RESET_ON_FORK
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

參考來源:
  1. man sched
  2. :《The Linux Programming Interface》chap. 35
  3. http://fcamel-life.blogspot.tw/2012/02/linux-process-priorities-and-scheduling.html (整理得不錯)
  4. http://stackoverflow.com/questions/10287561/sched-fifo-thread-is-preempted-by-sched-other-thread-in-linux
  5. man setpriority 
20200713 最後更新

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。

典型 x86-32 Linux layout

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 指標)
sizeprevnext剩下可使用的部份

每次 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()

參考

  1. TLPI §7
  2. man alloca 
  3. malloc() man-page
  • asprintf()
  • mmap()
  • memalign()、posix_memalign()
  • malloc_trim()

SIP header Via

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