星期日, 8月 26, 2018

IPv4 與 IPv6 辨識和位址

項目IPv4IPv6說明
EtherType0x08000x86DD
header 版本46
header 長度2040不含 options
位址 bit 數32128
位址 byte 數416
familyAF_INETAF_INET6
data typestruct in_addr
(unsigned long)
struct in6_addr
字串表示逗點隔開的 4 個十進位數字
dotted-decimal
255.255.255.255
分號隔開的 8 組 4 位十六進位數字
 colon-separated hexadecimal
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
IPv6 有簡寫規則
字串最長長度INET_ADDRSTRLEN
16
INET6_ADDRSTRLEN
46 (40)
含結尾 0。IPv6 位址 40-byte,但IPv4 tunneling 有 46-byte。

IPv6 簡寫規則:
  • 每組開頭的 0 可省略,若全為 0 ,則可簡寫為 0
  • 若連續好幾組全為 0,可全省略,只留下「::」,但以一次為限。
IPv4 tunneling (IPv4-mapped IPv6 位址)
  • 文字位址最長 46-byte:ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

struct sockaddr_in6 {
    sa_family_t     sin6_family;   /* AF_INET6 */
    in_port_t       sin6_port;     /* port number */
    uint32_t        sin6_flowinfo; /* IPv6 flow information */
    struct in6_addr sin6_addr;     /* IPv6 address */
    uint32_t        sin6_scope_id; /* Scope ID */
};

flowinfo 作用?sin6_scope_id 只用在 link-local。

Kamailio 使用的 IP 位址

typedef struct ip_addr {
        unsigned int af;        /* address family: AF_INET6 or AF_INET */
        unsigned int len;       /* address len, 16 or 4 */

        /* 64 bits aligned address */
        union {
                unsigned long  addrl[16/sizeof(long)]; /* long format*/
                unsigned int   addr32[4];
                unsigned short addr16[8];
                unsigned char  addr[16];
        }u;
} ip_addr_t;

IPv4 位址

IPv6 位址

IPv6 位址分成三類
  • unicast:單一網路界面的位址。
  • anycast:多個網路界面共用的位址,只需要送給一個最近的成員。位址格式和 unicast 相同,通常用在 router,例如 gateway。
  • multicast: 群播給所有成員
    • IPv4 的廣播相當於定址給 link-local all-nodes multicast group ff02::1。
IPv6 沒有廣播位址,由 multicast 位址取代。

IPv6 位址 128-bit,一般前面 64-bit 為 Network ID 作 routing 使用,又可分成 Global Routing Prefix 跟 Subnet ID。後面 64-bit 為 Interface ID 用來識別主機界面,可能來自介面卡的 MAC address (使用 modified EUI-64 格式)、DHCPv6 伺服器、亂數產生、或手動指定。
  • ::/128:位址未指定,例如用在來源位址還不知道的時候,實作上或許作為任何位址使用。相當於 IPv4 的 0.0.0.0/32。
  • ::/96:IPv4-compatible address,剩下 32-bit 放 IPv4 位址。(捨棄)
  • ::/0:default route,相當於 IPv4 的 0.0.0.0/0。
  • ::1/128:localhost,相當於 IPv4 的 127.0.0.1/8。
  • ::ffff:0:0/96:IPv4-mapped IPv6 address
    • ::ffff:192.0.2.128
  • ::ffff:0:0:0/96:IPv4-translated address
  • 64:ff9b::/96:IPv4-Embedded IPv6 Address [RFC 6052]
  • 0100::/64:discarding
  • 2001::/32:Teredo tunneling (IPv6 transition mechanism)
  • 2001:2::/48:benchmarking IPv6,相當於 IPv4 的 192.18.0.0/15。
  • 2001:20::/28:ORCHIDv2
  • 2001:db8::/32:文件範例使用,相當於 IPv4 的 192.0.2.0/24、198.51.100.0/24、和 203.0.113.0/24。
  • 2002::/16:6to4
  • fc00::/7:Unique local address,相當於 IPv4 的 private 位址 10.0.0.0/8、172.16.0.0/12、和 192.168.0.0/16。
  • fe80::/64:link-local address,不跨 link 使用。後面 64-bit 通常使用 modified EUI-64 格式的介面卡 MAC 位址。相當於 IPv4 的 169.254.0.0/16。48-bit MAC 轉成 modified EUI-64 是 OUI 後插入 0xfffe,並將 universal/local bit 相反。
    • fe80::1
  • fec0::/10:Site-local address 不跨 site 使用。(捨棄 [RFC3879])
  • ffFS::/16: multicast address
    • F:4-bit 分別為 rRPT
      • r (reserved)
      • R (Rendezvous)
      • P (Prefix)
      • T (Traniet)。T = 1 表示 non-permanently-assigned ("transient"),T = 0 表示 IANA 有登記的 well-known multicast address
    • S (scope):有 14 種 scope
      • 1 = interface-local,不能跨界面,只能作為 multicast 回送,例如 IPC。不像 unicast loopback,可以指定給任何界面。
      • 2 = link-local,不能跨 link。
      • 4 = admin-local
      • 8 = organization-local
      • e = global
    • ff02::1:all link-local nodes
    • ff02::2:all link-local routers
    • ff02::1:ff00:0/104:Solicited-Node multicast address,後面 24-bit 來自 unicast/anycast 位址。
大位址空間
階層式位址配置 (prefix 依據網路拓樸分配,可縮小主幹路由表來提昇效率)

參考

  1. RFC3513 IPv6 Addressing Architecture
  2. IPv6 簡介
  3. IP 協定沿革
  4. RFC4213 Basic Transition Mechanisms for IPv6 Hosts and Routers
  5. https://en.wikipedia.org/wiki/IPv4
  6. RFC8200 IPv6 Specification
  7. RFC4007 IPv6 Scoped Address Architecture
    • 除了「::」,每個 IPv6 位址都隱含 scope 資訊。
    • scope zone
    • 同一種 scope 的 zone 不會重疊。因為非 global 位址會重複使用,所以 node 內部對每種 scope 的 zone 作 index,表示為 address%zone_id。
    • scope global 的 zone 是全世界,只有一個。
    • scope interface-local 的 zone 是每個界面一個。
    • zone 是靜態的,即使因斷線分成多個區塊,仍屬於同一 zone。
  8. link、interface、node、host、router

星期四, 8月 23, 2018

IP 協定沿革

  • IP 協定出現之前就有 TCP,TCP v1 在 1973 (RFC 675),TCP v2 在 1977。後來 Jon Postel 發現 TCP 同時作為 end to end 協定,以及封包包裝 和路由的協定,違反網路階層原則的發展方向,TCP 跟 IP 才分家,並同時使用版本 v3 (1978)。
  • v4 是第一個 IP 穩定版本。RFC 760 (1980) -> RFC 791 (1981)
  • v5 被 Internet Stream Protocol 用掉
  • v6:RFC 1883 (1995) -> RFC 2460 (1998) -> RFC 8200 (2017)

星期四, 8月 16, 2018

USB 連接器

USB 規範有幾個面向,其中一個是關於連接器,其它面向有供電傳輸速度類別

USB 連接上游主機到下游週邊,由主機供電並主導傳輸。連接器分 Type A 和 Type B,分別區別角色是主機、還是週邊。主機上放 Type A 插座,週邊放 Type B 插座,中間用 Type A 轉 Type B 的 USB 線連起來。週邊也可以直接提供 Type A 插頭的 USB 線。

小結:有 Type B 插座或 Type A 插頭就是週邊。

  ↓主機↓   Type-A       ↓USB 線↓        Type-B  ↓週邊↓
[Type A 插座]←→[Type A 插頭----Type B 插頭]←→[Type B 插座]
[Type A 插座]←→[Type A 插頭                             ]

標準 USB 有 4 個訊號:VBUS、D-、D+、GND。

隨著手持設備出現,發展出較小的 mini USB 連接器及 micro USB 連接器,各自都有 Type A 和 Type B 兩種 -- mini-A、mini-B、micro-A、micro-B,共四種插座、四種插頭,而之前的就稱為 Standard-A、Standard-B。在主機或週邊的用法沒變,只是連接器較小。
USB 標準1.01.12.02.0 rev
3.03.13.24
最高速 12 Mbps480 Mbps5 Gbps10 Gbps20 Gbps40 Gbps
Type A??Type-A 2.0Type-A SuperSpeedX
Type BType-BType-B SuperSpeedX
Mini-A-Mini-AX
Mini-B-Mini-BX
Mini-AB-Mini-ABX
Micro-A-Micro-AMicro-A SuperSpeedX
Micro-B-Micro-BMirco-B SuperSpeedX
Micro-AB-Micro-ABMicro-AB SuperSpeedX
Type Cbackward 相容Type C

參考:https://en.wikipedia.org/wiki/USB#Connector_type_quick_reference

之後,USB OTG 支援有些裝置有時可以作為週邊,有時可以作為主機,新增另外兩種插座 -- mini-AB 插座和 micro-AB 插座,可以插 Type A 或 Type B 插頭,決定作為主機還是週邊。辨別方式是 mini USB 和 micro USB 界面多了一個 ID 接點,在 Type A 插頭接到 GND 會判斷為主機,Type B 插頭空接會判斷為週邊。後續仍可經由 Host Negotiation Protocol 協調交換彼此的角色。

mini/micro USB 界面有 5 個訊號:VBUS、D-、D+、ID、GND,但在 USB 線內並沒有 ID 訊號線。
訊號線界面接腳描述
Standardmicro/mini
VBUS115V
D-22Data-
D+33Data+
4ID:主機接地、週邊空接
GND45接地
SSTx--6USB 3.0
SSTx+-7USB 3.0
DGND-8USB 3.0
SSRx--9USB 3.0
SSRx+-10USB 3.0

在 USB 3.0,要支援更快的 SuperSpeed 傳輸速度,需要額外多 SSRX-、SSRX+、DGND、SSTX-、SSTX+ 5 個訊號線和接點,相容並擴充 Standard-A、Standard-B、和 mini-B 界面。

大約在 USB 3.1 發表同時期,發表了 USB Type-C 界面,是上下對稱的扁形接口,不用考慮插頭方向,也沒分主機或週邊端,共有 24 的接點。

插座訊號排列:
GNDTX1+TX1-VBUSCC1D+D-SBU1VBUSRX2-RX2+GND
GNDRX1+RX1-VBUSSBU1D-D+CC2VBUSTX2-TX2+GND

插頭可以轉 180° 插,訊號排列跟插座是左右或上下鏡射,CC1 和 CC2 變成 CC 和 VCONN,D+ 和 D- 只有一邊有。

GNDRX2+RX2-VBUSSBU1D-D+CCVBUSTX1-TX1+GND
GNDTX2+TX2-VBUSVCONN

SBU1VBUSRX1-RX1+GND

訊號說明:
  • D+、D-、VBUS、GND:原本 USB 訊號,在 USB-C 線 D+、D- 只有一組。
    • 在類比耳麥模式時,D- 和 D+ 分別變成左音道和右音道,此時 VBUS 和 GND 可提供 5V 500mA。
  • CC1、CC2:Channel Configuation,用來偵測 USB 有接、方向、角色,以及電源、Alternate Mode 溝通。沒有這兩個訊號時,按照 USB 2.0 方式運作。
    • 兩者在主機用 Rp pull-high,在週邊用 Rd pull-low。主機和週邊雙角色則兩種切換。主機端的 Rp 值可用來決定可供電的電流是預設、1.5A、還是 3A。
    • 在 USB 線裡,只有一個 CC 是兩端接在一起,並決定 D+ 和 D- 訊號位置。如果 USB 線需要吃電,另一個 CC 則用 Ra pull-low 變成 VCONN 提供至少 1W 的電。
    • 當主機和週邊接上時,雙方可偵測到 CC1 和 CC2 電壓變化,得知接上和接的方向,決定了 CC 位置。如果有一端是雙角色,也可以得知是哪種角色。如果雙端都是雙角色需要手動設定或自動設定。
    • CC 可進行 Power Delivery 和 Alternate Mode 溝通。Alternate Mode 可以是 DisplayPort、MHL、Thunderbolt、HDMI 等。
    • 類比耳麥模式:此時 CC1 和 CC2 直接接地,D- 和 D+ 分別變成左音道和右音道,SBU 變成 MIC 和類比 GND (需要能自動切換,由於可能反插,且 TRRS 耳麥插頭並沒有一致的標準)。同時可透過 VBUS 和 GND 提供 5V 500mA。
  • TX1+、TX1-、TX2+、TX2-、RX1+、RX1-、RX2+、RX2-:Super Speed/Super Speed+ 訊號,或 Alternate Mode 訊號。
  • SBU1、SBU2:Side Band Use,用在 Alternate Mode。
    • 在類比耳麥模式時,是 MIC 和類比 GND (需要能自動切換,由於可能反插,且 TRRS 耳麥插頭並沒有一致的標準)。
註:文件上的 Downstream Facing Port (DFP) 指只能當主機,Upstream Facing Port (UFP) 指只能當週邊,Dual Role Port (DRP) 可能是 Dual Role Data (DRD) 指資料傳輸角色可當主機或週邊,或 Dual Role Power (DRP) 指供電可當 Source 或 Sink。

參考

  1. https://en.wikipedia.org/wiki/USB_hardware

星期日, 8月 12, 2018

syslog: syslog() and syslogd

syslog 收集應用程式和 Kernel 產生的執行紀錄,執行紀錄含有分類和等級,讓系統管理員好監控管理。
  • 標準函數 syslog() 將紀錄放到 socket /dev/log。
  • daemon syslogd 接收 /dev/log 和 UDP port 514 的訊息,依據設定檔 syslog.conf 中對於分類和等級的規則,選擇將紀錄訊息導到終端機、FIFO、硬碟、遠端、或登入的使用者。
  • 遠端可能是另一個 syslogd,更進一步集中紀錄,也可以有備份紀錄的作用。
  • process 用標準函數 syslog() 產生紀錄。
  • kernel 用 printk() 產生紀錄,經過系統呼叫 syslog() 或 /proc/kmsg 給 daemon klogd 收集。klogd 再呼叫標準函式 syslog()。
  • 遠端產生的紀錄傳給 UDP port 514。
註:glibc 提供 klogctl() 使用系統呼叫 syslog()。
擷取自 TLPI Figure 37-1

syslog API

#include <syslog.h>

/* 開啟和關閉,非必要 */
void openlog(const char *ident, int option, int facility);
void closelog(void);

/* 產生紀錄 */
void syslog(int priority, const char *format, ...);
void vsyslog(int priority, const char *format, va_list ap);
這些函數都沒回傳值,因為會假設 syslog 系統都會正常。如果 syslog 系統出錯,那也就無處回報,系統管理員應該會先注意到。

syslog() 或 vsyslog()  產生紀錄,priority 是分類和等級的 OR 組合,format 之後的參數和 printf() 和 vprintf() 仝款,不同的是 syslog() 會自動在最後加上換行,另外「%m」會取代為目前 errno 的錯誤字串,相當於 strerror(errno)。
分類 (facility)名稱說明
0LOG_KERNkern來自 Kernel。user process 使用會轉為 user。
1LOG_USERuser來自 user process (預設)
2LOG_MAILmail來自郵件系統。
3LOG_DAEMONdaemon來自其它系統 daemons。
4LOG_AUTHauth (security)Security 或 authorization 訊息 (e.g., su)
5LOG_SYSLOGsyslog來自 syslogd 內部。
6LOG_LPRlpr來自印表機系統 (lpr, lpd, lpc)。
7LOG_NEWSnews來自 Usenet 網路新聞。
8LOG_UUCPuucp來自 UUCP 系統
9LOG_CRONcron來自 cron 和 at daemons。
10LOG_AUTHPRIVauthprivPrivate 的 security 或 authorization 訊息。訊息包含密碼或其它敏感資訊位置不同於 LOG_AUTH。
11LOG_FTPftp來自 ftpd。
16
~
23
LOG_LOCAL0
~
LOG_LOCAL7
local0
~
local7
保留為本機使用。

等級 (level)名稱說明
0LOG_EMERGemerg (panic)系統當機或無法使用
1LOG_ALERTalert需要立即處理 (例如系統資料庫毀壞)
2LOG_CRITcrit危急發生 (例如硬碟裝置錯誤)
3LOG_ERRerr (error)錯誤發生
4LOG_WARNINGwarning (warn)警告發生
5LOG_NOTICEnotice正常,但需要注意
6LOG_INFOinfo一般資訊
7LOG_DEBUGdebug除錯訊息


none

syslog() 用下列方式輸出使用者提供的字串是危險的:

syslog(priority, user_supplied_string);
如果使用者提供的字串包含 format specifiers (例如 %s),無法預期造成什麼結果。較安全作法是改用:
syslog(priority, "%s", user_supplied_string);

openlog():改變使用 syslog() 的預設值,開啟 /dev/log。
  • ident:每個訊息前要加的字串,通常是程式名稱。NULL 時,glibc 自動用程式名稱,但不是所有實作皆如此。註:openlog() 只複製字串指標,沒複製字串內容。
  • option:下列 bit mask 組合。
    • LOG_CONS:訊息寫到 /dev/console。
    • LOG_PERROR:訊息也寫一份到 standard error。(非 SUSv3 標準)
    • LOG_PID:訊息包含 process ID 供辨別。
    • LOG_NDELAY 和 LOG_ODELAY (預設 LOG_ODELAY):LOG_ODELAY 在 log 第一個訊息時才連結到 /dev/log。LOG_NDELAY 則是立刻連結到 /dev/log,例如用在 chroot()。chroot() 後 /dev/log 就看不到了,需要事先 LOG_NDELAY openlog(),tftpd 就是如此使用。
    • LOG_NOWAIT:不 wait() log 訊息的 child process 建立。在 Linux 並不建立 child process,所以沒作用。
  • facility:facility 預設是 LOG_USER,這裡可以提供 syslog() 未指定時的預設分類。
closelog():關閉 /dev/log。如果 daemon 持續在跑並不需要。

過濾 log 訊息

int setlogmask (int mask_priority );
預設不過濾。LOG_MASK() 將 level 轉換成 mask_priority 用的 bit mask,訊息 level 不在目前的 mask_priority 會丟棄。回傳原本的 mask_priority。
setlogmask(LOG_MASK(LOG_EMERG) | LOG_MASK(LOG_ALERT) | LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR));
大部份實作也提供 LOG_UPTO(),建立特定 level 以上的 mask_priority:
setlogmask(LOG_UPTO(LOG_ERR));

logger 指令

logger 是用指令方式產生紀錄訊息到 syslog,可指定 level、ident 等。

設定檔 /etc/syslog.conf

/etc/syslog.conf 是 syslogd 的設定檔,內容格式如下:
facility.level action # 註解
設定符合 facility.level 的訊息所要進行的動作 (action)。facility 指定分類,「*」表示全部。level 指定某個等級以上,其中 debug 是最低的,表示所有等級,有些實作 (包括 Linux) 也可以用「*」。等級 none 則排除符合的分類。多個 facility.level 可用「;」隔來共用 action。一些例子:
*.err /dev/tty10
auth.notice root
*.debug;mail.none;news.none -/var/log/messages

action 可能是

  • 檔案。寫入的檔案,前置「-」表示每次寫入不進行 sync (TLPI §13.3),可以有較快的寫入速度,但當機可能會造成資料流失。
  • 裝置。特定終端機 /dev/tty10 console device
  • 使用者名稱。如 root 登入的終端機。
  • 遠端主機

修改 syslog.conf 後,需要送 SIGHUP 叫 syslogd 重新初始化:

$ killall -HUP syslogd

參考

  1. TLPI §37.5 全部
  • man-page syslog.conf(5) (不存在?)
  • OpenWrt AA 以前用 busybox syslogd 和 logread,BB 以後用 ubox logd 和 logread。
  • RFC 5424: The Syslog Protocol
  • 限制:
  • http://linux.vbird.org/linux_basic/0570syslog.php
  • http://taiwanwolf.blogspot.com/2011/08/centos-6-rsyslogd-log.html
  • journald
  • 思考
    • 一種儲存 log 的方式或者檔案系統,隨時存 log、當機不會造成損毀,又盡量延長 flash 使用壽命。
    • log merge:隨著進展,更新某項 log 內容,只需要最後的情況。
    • 非文字的 log。
    • 動作是執行程式?

星期五, 8月 10, 2018

pcap file format

pcap 是廣泛用來儲存封包的檔案格式,內容包括 1 個 Global Header 加上 0 個以上封包。每個封包有 Record Header 放時間和長度,以及 Record Data 放封包本身。封包可能任何長度,存放時不管位元組對齊 (byte alignment):

Global Header
24-byte
Record Header
16-byte
Record Data
Record Header
16-byte
Record Data
...

Global Header 有 24-byte:
  • 32-bit magic_number:用來偵測檔案格式和位元組順序,用原生位元組順序寫入 0xa1b2c3d4 或 0xa1b23c4d (奈秒精度)。讀取時如果位元組有調換 (swap),接下來的欄位也需要調換。
  • 16-bit version_major, 16-bit version_minor:格式版本,目前是 2.4。
  • 32-bit thiszone:Record Header 的 ts_sec 調整成 GMT 時間要加的秒數。實際上,ts_sec 都用 GMT,所以 thiszone 都是 0。
  • 32-bit sigfigs:是要放 time stamp 的 accuracy,但實際上都設為 0。
  • 32-bit snaplen (snapshot length): 最多只擷取封包前面的 byte 數。
  • 32-bit network: 封包一開始的 header type,例如 1 是 Ethernet,還有 802.11、PPP、LAPD 等。
16-byte Record Header
  • 32-bit ts_sec:擷取時的 Epoch 秒數。如果不是 GMT 時間,用 thiszone 調整。
  • 32-bit ts_usec:秒下的微秒或奈秒 (由不同 magic_number 決定)。
  • 32-bit incl_len:封包擷取的長度,最大應該是 orig_len 或 snaplen。
  • 32-bit orig_len:封包原始長度。
限制:
  • 無處放其它資訊,例如註解 (如:哪個封包後開始斷線)、界面資訊 (如:界面編號、方向、網卡製造商等)、計數值 (如 packet drop count) 等。

參考

  1. https://wiki.wireshark.org/Development/LibpcapFileFormat
  • libpcap
  • pcapng 檔案格式
  • 取出語音:https://stackoverflow.com/questions/6073868/how-to-stream-pcap-file-to-rtp-rtcp-stream
  • 串流語音:http://sipp.sourceforge.net/doc/reference.html#PCAP+Play
  • http://www.signal11.us/oss/playcap/
  • http://www.cs.columbia.edu/irt/software/rtptools/
  • wireshark

星期一, 8月 06, 2018

bash: INVOCATION

login shell:選項有 -l 或 --login,或者指令名稱由「-」開頭,有登入 (或登出) 動作。

interactive shell:指令搭配 prompt 從標準輸入讀取,shell 需要支援一些互動所需要的動作。
  • 沒有非選項的引數,或有選項 -s。其標準輸入及錯誤輸出連接到 isatty() 決定的終端機。或者有選項 -i。不能有選項 -c 在引數指定指令。
  • 會設定 PS1 作為輸入指令的 prompt,且 $- 包含 i,讓 shell 指令檔或起始執行的檔案測試是否為 interactive。
接下來說明 bash 開始和結束時執行的檔案,如果檔案存在但無法讀取,bash 回報錯誤。檔名 tilde 擴展見 bash EXPANSION 的 Tilde Expansion。

當 bash 以名稱 sh 執行,會採用傳統 sh 的起始行為,並進入 posix 模式。

shelllogin
(不管是否為 interactive)
interactive
(不含 login)
非 interacitve
bashshbashshbashsh
啟始/etc/profile
~/.bash_profile
~/.bash_login
~/.profile 三者之一
/etc/profile
~/.profile
/etc/bash.bashrc
~/.bashrc
. "$ENV". "$BASH_ENV"
選項--noprofile --norc
--rcfile file
結束~/.bash_logout
bash 由 rshd 或 sshd 等 remote shell daemon 執行時,偵測標準輸入是否連到網路連線,使用 intreactive shell 的行為。

如果 shell 開始時 effective user (group) ID 跟 real user (group) ID 不同,啟始檔案不執行,且不自環境繼承 shell function,忽略變數 SHELLOPTS、BASHOPTS、CDPATH、和 GLOBIGNORE。此時選項 -p 沒設,effective user ID 設為 real user ID。

判定是否在互動模式

方式一:看 $- 是否有 i
case "$-" in
*i*) echo This shell is interactive ;;
*)   echo This shell is not interactive ;;
esac

方式二:PS1 有設

if [ -z "$PS1" ]; then
    echo This shell is not interactive
else
    echo This shell is interactive
fi

參考來源

man bash INVOCATION 節

延伸閱讀

multithread: Condition Variables

在 multi-thread 程式,mutex 用來避免同時存取共用變數而造成衝突,但 thread 有時需要看共用變數狀態改變了才進行處理,需要類似如下步驟的無窮迴圈:
  1. 鎖住共用變數的 mutex
  2. 共用變數是想要的狀態則進行處理
  3. 解鎖 mutex
步驟 2 共用變數的狀態常常不是想要的,而忙於一直進行無意義的 mutex 鎖住和解鎖 (步驟 1→3)。上述例子是一個進行處理的 consumer,另外有其它 producer thread 產生需要處理的狀態。condition variable 讓 consumer 先去休息,直到 producer 通知狀態改變了才喚醒進行處理,不用忙於 mutex 鎖住和解鎖的迴圈:
  1. 鎖住共用變數的 mutex
  2. 共用變數不是想要的狀態則休息直到等候到 condition variable 才進行處理
  3. 解鎖 mutex
等候 condition variable 需要搭配檢查共用變數的狀態,也就是需要搭配共用變數的 mutex,而等候 condition variable 的函數內部需要暫時解鎖 mutex,讓其它 thread 可以改變共用變數,所以稍後在等候 condition variable 的函數可以看到需要傳遞搭配的 mutex。

初始化及銷毀

#include <pthread.h>

condition variable 必須初始化之後才能使用,不能複製。例如靜態配置:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
或動態配置:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
condition variable 沒有 consumer 等候才能銷毀。
int pthread_cond_destroy(pthread_cond_t *cond);

Consumer 進入等候

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
等待 cond,呼叫前必須先鎖住 mutex。也確保收到通知後是鎖住的。一般等候結束後,需要再檢查共享變數的狀態是不是想要的,特別是:
  1. 有多個 consumer,不能確定哪個被喚醒。
  2. 有時候應用程式設計成狀態可能發生比必定發生容易。
  3. 有可能出現假的喚醒 (Spurious  wakeup)。在有些多處理器系統的實作,為了效率而罕見地出現,這是 SUSv3 所允許的。

如果等候時間有上限:
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
引數 abstime 是 timespec (TLPI §23.4.2),為從 Epoch (TLPI §10.1) 開始的絕對時間 ( seconds 和 nano seconds)。到期回 ETIMEDOUT。

Producer 通知結束等候

依據排程讓其中一個 thread 結束等候
int pthread_cond_signal(pthread_cond_t *cond);

全部結束等候

int pthread_cond_broadcast(pthread_cond_t *cond);

如果當下沒有 thread 等候,通知就失去作用,不會延遲給後續的等候。一般來講,先解鎖相關的 mutex 後再通知效能較好。

參考

  1. TLPI §30.2 §30.2.1 §30.2.2 §30.2.3 §30.2.5
  • uClibc condattr 沒作用,沒有 pthread_condattr_setclock()。
  • uClibc v0.9.31 pthread_cond_timedwait() 用 timedsuspend() 等候 abstime。而 timedsuspend() 內部是用 nanosleep() 等候相對時間。

星期日, 8月 05, 2018

system()

system() 讓程式執行 shell 指令。
#include <stdlib.h>

int system(const char *command);
system() 使用系統呼叫 fork() 建立子行程呼叫 execl() 在 shell 下執行 command
execl("/bin/sh", "sh", "-c", command, (char *) 0);
使用 system() 的好處是簡單,不用處理 fork()、exec()、wait()、和 exit() 的細節,不用管錯誤和 signal 處理,也自動進行一般 shell 處理、取代、和轉向。但使用 system() 較沒效率,使用較多系統呼叫,執行一個指令至少需要建立兩個行程,一個是 shell,另外的是每個指令本身都需要 exec()。

回傳值
  • command 是 NULL,回傳 shell 是否存在,有 shell 回傳非 0,沒 shell 回傳 0。
  • 無法建立子行程或無法取得結束碼,回傳 -1。
  • 如果無法在子行程執行 shell,回傳如同 shell 呼叫 _exit(127) 結束。
  • 回傳指令執行後的結束碼。也可能是 127,例如 shell 只不到給定名稱的 program 來執行。
後兩者回傳值跟系統呼叫 waitpid() 有相同定義 (WIFEXITED()、WEXITSTATUS() 等),可用 TLPI §26.1.3 描述的函數來剖析和 printWaitStatus() 顯示。


command 執行期間,SIGCHLD 會 blocked,SIGINT 和 SIGQUIT 會忽略。

參考來源

man system, TLPI §27.6
延伸閱讀

  • 使用 system() 的範例 [TLPI Listing 27-7]
  • system() 可用 fork()、exec()、wait()、和 exit() 實作。[TLPI §27.7]
  • popen() 和 pclose() 也可用來執行 shell 指令,並可以讀取結果或送 input 給指令。[TLPI 44.5]