2010年10月8日 星期五

憑證格式的介紹與轉換

X.509是常見通用的憑證格式標準
其他跟憑證有關係的標準則都定義在 PKCS 當中
PKCS 為 Public-Key Cryptography Standard 的縮寫
目前共發布過15個標準,細節可參考 Reference 中的wiki

管理存儲憑證常用的格式有以下幾類:[from wikipedia 公開金鑰密碼編譯標準]
PKCS#6 證書擴展語法標準(Extended-Certificate Syntax Standard)
將原本X.509的證書格式標準加以擴充。

PKCS#7 密碼訊息語法標準(Cryptographic Message Syntax Standard)
規範了以公開金鑰基礎設施(PKI)所產生之簽章/密文之格式。
其目的一樣是為了拓展數位證書的應用。其中,包含了S/MIME與CMS。

PKCS#10 證書申請標準(Certification Request Standard)
規範了向證書中心申請證書之CSR(certificate signing request)的格式。

PKCS#12 個人訊息交換標準(Personal Information Exchange Syntax Standard)
定義了包含私鑰與公鑰證書(public key certificate)的文件格式。
私鑰採密碼(password)保護。常見的PFX就履行了PKCS#12。

X.509 DER encoding 的 postfix 為 .der (BINARY)
X.509 PEM encoding 的 postfix 為 .pem (ASCII)
<格式轉換>
$ openssl rsa -in privatekey.pem -inform PEM -out privatekey.der -outform DER
$ openssl rsa -in privatekey.der -inform DER -out privatekey.pem -outform PEM

PKCS#7 常用的 postfix 有 .p7b .p7c .spc
p7b 以樹狀展示 certficate chain,不含 private key

PKCS#12 常用的 postfix 有 .p12 .pfx(MS)
p12/pfx 用於存放 certificate 加 private key
將此兩樣打包的過程中會要求輸入 Export Password 來保護這個p12檔案
相反地,你要取用p12檔案中的個別資訊時,就需要輸入 Import Password來解開保護
*Import/Export Password為同樣一組,只是方向不同







[Reference]
公開金鑰密碼編譯標準
http://zh.wikipedia.org/zh-hk/公開金鑰密碼編譯標準
常见证书格式及相互转换
http://355665.blog.51cto.com/345665/265679

2010年8月3日 星期二

OpenSSL Certificate Management

#
# 製作 RSA Private Key,並且將 Key 設定為只有 owner 才能存取的權限
#
openssl genrsa -out myhost.key.pem 2048
chmod og-rwx myhost.key.pem

*** genrsa 是 openssl 產生 rsa key 用的 command ***
*** -out 是表示 output 的 key name ***
*** 2048 是 key 的強度,一定要放在最後面 ***


#
# 填寫憑證申請書
#
openssl req -new -key myhost.key.pem -out myhost.req.pem

*** req 是 openssl 產生 CSR 用的 command ***
X.509 Certificate Signing Request (CSR) Management
*** -new 是產生新的 certificate request ***
*** -key 是指定要從哪邊讀 private key ***
*** -out 是表示 output 的 request name ***


#
# 顯示憑證申請書
#
openssl req -text -noout -in myhost.req.pem

*** -text 是印出 CSR 的 text form ***
*** -noout 是不要印出 CSR 的 encoded form ***
*** -in 表明要處理的 request file ***


#
# 簽憑證 (CA certificate)
#
openssl x509 -req -days 7305 -sha1 \
-extfile ~/etc/ssl/openssl.cnf -extensions v3_ca \
-signkey ~/etc/ssl/private/myrootca.key.pem \
-in ~/tmp/myrootca.req.pem -out ~/etc/ssl/certs/myrootca.crt.pem

*** x509 是證書資料管理的 command ***
*** -req 特別表示處理的是 request file,一般都是 certificate ***
*** -days 表明證書的有效天數 ***
*** -sha1 表示要使用的 digest 演算法 ***
*** -extfile 表示包含 certificate extensions 的檔案 ***
file containing certificate extensions to use
If not specified then no extensions are added to the certificate
*** -extensions v3_ca ***
the section to add certificate extensions from
*** -signkey 表示要用哪一把 private key 去 self-sign request ***
this option causes the input file to be self signed using the supplied private key


#
# 簽憑證 (Server certificate)
#
openssl x509 -req -days 3650 -sha1 \
-extfile ~/etc/ssl/openssl.cnf -extensions v3_req \
-CA ~/etc/ssl/certs/myrootca.crt.pem -CAkey ~/etc/ssl/private/myrootca.key.pem \
-CAserial ~/etc/ssl/myrootca.srl -CAcreateserial \
-in /tmp/myhost.req.pem -out ~/etc/ssl/certs/myhost.crt.pem

*** -CA ***
specifies the CA certificate to be used for signing
*** -CAkey ***
sets the CA private key to sign a certificate with.
If this option is not specified then it is assumed that the CA private key is present in the CA certificate file
*** -CAserial ***
sets the CA serial number file to use
*** -CAcreateserial ***
with this option the CA serial number file is created if it does not


#
# 顯示憑證
#
openssl x509 -text -noout -in myhost.crt.pem



[Reference]
如何製作 SSL X.509 憑證
http://www.imacat.idv.tw/tech/sslcerts.html

OpenSSL official Documents
http://www.openssl.org/docs/apps/openssl.html

2010年7月16日 星期五

major number 和 device node 以及 device driver 間的關係

每個 device 會有一組 (Major number, Minor number) 來作為區別的資訊

到 /dev 資料夾底下,使用 ls -l 就可得到各個 device node的資訊

以 ppp 這個 device node為例,ls -l 輸出如下
crw------- 1 root root 108, 0 Apr 21 12:46 ppp

前面的 permission field 中,第一個字元 c 表示這個 device 是 char device
接下來介於 Group name 之後,Modification date 之前
就是 ppp 這個 device 的 (major number, minor number) 資訊 108,0

有了 device node 與 (major,minor) 之間關係的觀念後

接下來就來探討 device driver 和 (major,minor) 有什麼樣的關係

關係圖:device node --- (major,minor) --- device driver

恰巧,這個關係也就是為什麼 kernel 能正確找到 device 其所有的 driver 的原因

答案很簡單

device driver 需要向 kernel 註冊其所屬 device 的 (major,minor) pair

接下來就來解釋為什麼會有(major,minor)這樣的資訊存在


在系統中新增一個硬體,許多環節彼此相扣
  1. hardware
  2. driver
  3. Linux OS 中配給硬體的 dev node (在 /dev 底下)

driver 向 kernel 註冊一些 handler software 以及一些 hardware 資源 (io ports,etc)
好讓 kernel 得知此 hardware 的存在,並且接受此 hardware 的 interrupt

在 /dev 底下需要替這個 hardware 新建一個 device node 好讓 kernel 可以使用它
但這項工作不是 driver 的 job
因此需要手動建立這個 device node

而 device node 的 (major,minor) 資訊
需要和 driver 中向 kernel 註冊的 (major,minor) 一樣

因此 kernel 就可經由 (major,minor) 搭建起來的橋樑找到 hardware 和 driver 的關連

也就是此關係圖:device node --- (major,minor) --- device driver

Linux 驅動程式的中斷處理

轉貼自 Jollen 的 Blog

Linux 驅動程式的中斷處理, #1: request_irq 基本觀念
http://www.jollen.org/blog/2008/03/interrupt_handling_1.html

Linux 驅動程式的中斷處理, #2: 深入淺出中斷模式
http://www.jollen.org/blog/2008/03/interrupt_handling_semaphore.html

Linux 驅動程式的中斷處理, #3: Bottom Half 的觀念
http://www.jollen.org/blog/2008/03/interrupt_handling_bottom_half.html


[Reference]
http://www.jollen.org/cgi-bin/mt3/mt-tb.cgi/481

2010年6月30日 星期三

網路安全工具 - nmap

nmap - Network Mapper

Network exploration tool and security / port scanner


-PN [No Ping]
$ nmap
This option skips the Nmap discovery stage altogether.
Normally, nmap uses this stage to determine active machines for heavier scanning.
Used to be -P0 <- it's a nubmer zero, not a character O


-sL [List Scan]
$ nmap -sL 192.168.1.0/24
列出子網路所有IP及對應的主機名稱,但不作ping及通訊埠偵測
(degenerate form of host discovery without sending any packets)


-sP [Port Scan]
$ nmap -sP 192.168.1.0/24
僅使用ping掃瞄子網路內的所有IP,並列出有回應的IP,不作進一步測試


-PS [TCP SYN Ping]
$ nmap -PS 192.168.1.1
偵測遠端主機已開啟的通訊埠
為縮短掃瞄時間,可指定特定的port number,例如 -PS22,23,80,25
This option sends an empty TCP packet with the SYN flag set.
-remote host responds with a RST packet (The queried port is closed)
-remote host responds with a SYN/ACK packet (The queried port is open)
利用的原理是 TCP 3-way-handshake mechanism
A --SYN--> B
A <--SYN/ACK-- B
A --ACK--> B


-PU [UDP Ping]
$ nmap -PU 192.168.1.0/24
使用UDP協定 ping 遠端的主機。


-sS [TCP SYN scan]
$ nmap -sS 192.168.1.0/24
使用 TCP SYN packet 掃瞄,這是一個半開放的掃瞄方式,
三方交握不會完成,所以掃瞄速度較快,也比較常被使用。
此選項可以列出有回應的遠端主機已開啟的網路服務埠。


-sT [TCP connect scan]
$ nmap -sT 192.168.1.0/24
如無法以 TCP SYN packet 掃瞄時,就得改用 socket API connect() 來掃瞄
在這種情況底下,connection 會被正式建立完成,花費的時間也較- sS 選項多


-sU [UDP scan]
$ nmap -sU 192.168.1.0/24
用 UDP 協定掃瞄遠端主機群


-sO [IP protocol scan]
$ nmap -sO 192.168.1.1
偵測遠端主機已開啟哪些通訊協定 TCP,UDP,ICMP,等等


-O [Enable OS detection]
nmap -O 192.168.1.1
nmap -A 192.168.1.1
偵測遠端主機的作業系統類型


-v -vv -vvv [Increase verbosity level]
nmap -v scanme.nmap.org
掃瞄遠端主機,並列出相關詳細的資訊


[example]

$ nmap -sS -O 192.168.1.0/24
以 SYN 掃瞄網域為192.168.1.0所屬 C class 網段的所有存在的主機作業系統類型

-sV [Version detection]
-p [Only scan specified ports]
$ nmap -sV -p 22,53,110,143,4564 198.116.0-255.1-127
進行主機列表及TCP掃瞄特定網路服務,-p 指定 22,53,110,143,4564 這幾個 port
遠端主機的網路含括 198.116.(0-255).(1-127) 網段
(version detection is used to determine what application is running)

-iR [Choose random targets]
$ nmap -v -iR 100000 -P0 -p 80
隨機選擇100000台主機,偵測是否開啟 Web 服務,
由於此掃瞄較耗時,所以加上 -P0(ie,-PN) 不作 active machines detection。
(The argument -iR 0(zero) can be specified for a never-ending scan)


[Reference]
http://nmap.org/book/man.html
http://idobest.pixnet.net/blog/post/22040775

2010年6月29日 星期二

虛擬網卡 TUN/TAP 工作原理

TUN/TAP是一個虛擬網卡的介面,在 Linux 以及 window$ 上面都有支援
一些 VPN projects 像是 OpenVPN 都是基於這個介面實現 tunneling 的機制

tun (network TUNnel) 虛擬的是 點對點 設備
-simulates a network layer device
-layer 3 packets, such as IP packet
-used with routing

tap (network TAP) 虛擬的是 乙太網路 設備
-simulates an Ethernet device
-layer 2 packets, such as Ethernet frames
-used to create a network bridge

下圖是原作者麻利輝所畫的簡圖 (請查閱Reference中的網頁)



以下就已建立好的 VPN 連線來探討封包流經的順序:

1. Outgoing

首先,應用程式會利用 tun 這個網卡將資料送到 VPN 的 peer去
而這個應用程式正是圖中的「使用tun/tap驅動的進程」
在經過 TCP/IP protocol stack 之後來到 tun (Virtual NIC Part) 成為 VPN 封包

下一步,「數據處理進程(OpenVPN)」
會由 tun ( Char device Part) read 出剛剛的VPN封包
再次丟往 TCP/IP protocol stack,最後到達 Real NIC 而送往「物理鏈路」(外部網路)

下圖是就原圖所做的一點更動,明確表示出資料流的順序


2. Incoming

從物理鏈路收到給 OpenVPN 的封包,
接下來此封包經過 protocol stack 被拔掉一層層的 headers
最後就是原始封包的 data payload 部份,也就是 VPN"封包"

OpenVPN 再把這個 VPN"封包"經由 tun (Char device part) write 到 Virtual NIC

Virtual NIC 收到封包後,再把他送往 protocol stack
最後就回到使用 tun interface 的應用程式

圖例與 outgoing 一樣,只是數字順序相反


[Reference]
虛擬網卡 TUN/TAP 驅動程序設計原理 by 麻利輝
http://www.ibm.com/developerworks/cn/linux/l-tuntap/index.html
TUN/TAP - Wikipedia
http://en.wikipedia.org/wiki/TUN/TAP

2010年6月28日 星期一

Linux的防火牆 - iptables

防火牆的概念在鳥哥的網站有很棒很詳盡的介紹
鳥哥關於防火牆的介紹文章網址放在reference中

下圖是鳥哥所簡化的 chain 圖,
觀賞度10,實用度10,綜合係數10 (...某足球十大精妙過人影片爛梗XD)



作為普通PC的Linux,其防火牆處理流入本機封包的流程是
1. nat-PREROUTING [跳過]
2. Forward封包轉遞 [走 flow A,因此導向本機 Linux]
3. filter-INPUT [!!本機防火牆主要工作的地方!!]
4. Linux本機的資源
5. nat-OUTPUT [跳過]
6. filter-OUTPUT [!!本機防火牆主要工作的地方!!]
7. nat-POSTROUTING [跳過]

整理以後的流程如下,
1. Forward封包轉遞
2. filter-INPUT --負責把關流入本機的封包
3. Linux本機的資源
4. filter-OUTPUT --負責把關流出本機的封包


===============================================
若是Linux作為NAT(router)的機器,其防火牆處理流入本機封包的流程是
1. nat-PREROUTING --更改 DESTINATION
2. Forward封包轉遞 [走 flow B,NAT]
3. filter-FORWARD
4. nat-POSTROUTING --更改 SOURCE


===============================================
VPN的亂入,tun介面來襲,防火牆流程是?!

首先,先看從VPN安全通道流入的封包
[VPN client] ---> [VPN server with firewall]
(以下內容基於VPN通道已建立好的前提)

一開始,server會從對外開放的 *interface 拿到封包
(*interface,例如擁有 Public IP 的 eth0,:p)
其source IP和destination IP都是public的
source IP 是 client 的 public address
destination IP 是 server 的 public address
也就是該 server 對外的 interface 上的 IP

所以一開始是判斷為流入本機的封包
因此 forward 到 filter-INPUT 檢查
接下來就進入Linux存取本機資源

通過 Protocol Stack,在封包被拔掉 header 之後
便交由 application layer 的 VPN program 處理
此時會發現 application data payload 又是一個"封包"
其 source IP 和 destination IP 都是 private 的
source IP 是 client 的 VPN private address (通道建立過程中,由 server 分配)
destination IP 是:
1. server 的 VPN private address (表示存取 VPN server 本機資源)
2. LAN 內 VPN private address (表示存取企業內部網路的資源,VPN server as NAT-Router)

接下來,就是TUN上場的時候了
這個 VPN program 會把這個"封包"往本機上名為 tun 的 virtual interface 丟
然後呢....

tun 也是主機上的一個 interface (雖然是虛擬的),所以收到"封包"的時候
防火牆處理的過程也跟其他實體 interface 一樣,需要通過罩杯的考驗

在封包進來 tun interface 之後,若是封包是要傳給 server 的
則封包會被 forward 到 filter-INPUT 處理
(由此可知,VPN的使用也需要對防火牆做好適當的設定,免得VPN封包被擋掉...)

過了 filter-INPUT之後,進入本機
一樣也是通過 Protocol Stack,一層層的 header 被拔掉
最後就可以到達 server 的 application layer... finally :)

廢話連篇,一圖解千文



在封包進來 tun interface 之後,若是封包是要傳給 Private LAN 其他主機的話
則封包會被 forward 到 filter-FORWARD 處理

以下是廢圖二連發



[Reference]
鳥哥的Linux私房菜 - 防火牆與NAT
http://linux.vbird.org/linux_server/0250simple_firewall.php

2010年6月10日 星期四

Dynamic Programming

There is an ordering on the subproblems, and a relation that shows how to solve a subproblem given the answer to "smaller" subproblems, that is, subproblems that appear earlier in the ordering.

解釋之,就是一個問題可以細分為許多小問題,而這些分解下去的小問題之間又有順序上的關係,使得小問題的答案可以成為大問題答案的基石。

一般來說,我們會以陣列的形式來依序儲存小問題的答案,以供後續大問題解答時使用。

經典問題包含:
背包問題 (knapsack)
最長遞增序列 (Longest increasing subsequences)


[Reference]
Chapter 6 DP, from "Algorithms"
by S. Dasgupta, C.H.Papadimitriou, and U.V. Vazirani

2010年5月29日 星期六

GNU automake manual 筆記

在正常遵循 GNU 標準的 packages(套件) 內容中
標準的安裝程序就是
  1. $ ./configure
  2. $ make
  3. $ make install (或許需要sudo)
第一步的 configure 檔案
會去偵測該 package 所在主機上的一些設定
以及檢測一些相依性 package 是否存在
最後產生一個符合該主機的 Makefile 供下一步的 make 使用

以上所述為 native compilation 的情況
也就是 package 是在要使用該 package 的主機上編譯

在 cross compilation 的情況下事情就變得有些棘手

首先 configure 原先是要偵測主機上的設定
但現在 package 是要在 HOST machine 上編譯 (ex. X86)
最後是安裝在 TARGET machine 上執行 (ex. arm)

所以要是直接執行 configure 的話
偵測到的是 HOST 上的設定,而非 TARGET 的

舉個顯例,configure 假設去抓 HOST 上 CC 變數的設定 (C Compiler)
會得到 CC=gcc,可是這個 gcc 是編出 x86 binary 使用的
並非我們要的 arm binary compiler
所以 configure 要抓的 CC 變數應該是 CC=linux-arm-gcc

那要怎麼做才能讓 configure script 在 cross compilation 的情形下
得到 TARGET machine 上的偵測設定值呢?

這個學問真是太高了 XDDD 撒謀啊


以下的參考是慢慢演進的,會說是參考表示他可能不是正確解答
google 過這個議題,但找不到顯而易見的解答,只好自己 try and error
--------------------------------------------------------------------------------------------------------

首先提及幾個 configure script 可使用的參數 (可用./configure -h 查看)

[GNU automake manual text]
--build=BUILD
The system on which the package is built.

--host=HOST
The system where built programs and libraries will run.

當使用--host參數的時候,configure script 會搜尋這個HOST的 cross-compiling suite
例如說,當設定--host=arm-linux-gnu,則以下的編譯工具會被使用
編譯器 arm-linux-gnu-gcc
連結器 arm-linux-gnu-ld
組譯器 arm-linux-gnu-as

上述的 HOST 的名詞跟先前提到的 HOST machine 中的 HOST 定義剛好相反
這邊要特別注意,一開始沒看 manual 之前,我的 HOST 都給它設成 HOST machine...

為了怕混淆,這裡再特別解釋一次好了
在 cross compilation 的語境中
HOST 指的是進行cross compilation的機器
此處借重HOST的高運算能力來負責編譯的工作
所以一般HOST都是以高效能的 desktop 居多
TARGET 指的是執行程式的目標機器,也就是執行HOST幫忙編譯的程式的目標機器

而在 configure script 的語境中
HOST 指的是執行程式或函式庫的目標機器,BUILD 才是進行 cross compilation 的機器
在 configure 的參數中也剛好有個 --target=TARGET 可以設定
這裡的 TARGET 是設定 Canadian cross 用的,對於一般人用不太到,可以忽略

*Canadian cross (3-way cross-compilation)
簡言之是在A機器 cross-compile 出一個在B機器使用的 cross-compiler
而這個在B機器的 cross-compiler 會產出在C機器使用的 binary


所以在 cross compilation 的環境下
安裝套件的第一步 ./configure 似乎是得到解決的方案

[example]
$ ./configure --build=i686-pc-linux-gnu --host=i586-mingw32msvc

2010年5月21日 星期五

Concurrency and Race conditions

在 semaphore 的術語中,有分為
1.鎖定的 P 動作
2.解鎖的 V 動作

P 動作簡單說就是對 semaphore 的 value 值作遞減(down)的動作
V 動作則恰好相反,他是執行對 semaphore 的 value 值作遞增(up)的動作

而在 Linux kernel function 中
P 動作是以 down 為函式名稱
V 動作則是 up 為函式名稱

down又分為三個函式

void down(struct semaphore* sem);
int down_interruptible(struct semaphore* sem); /*建議使用版本*/
int down_trylock(struct semaphore* sem);

2010年4月28日 星期三

Android 編譯 Native C module

Android編譯環境本身比較複雜,不像普通的編譯環境:
只有頂層目錄下才有Makefile文件,而其他的每個component都使用統一標準的Android.mk。Android.mk文件本身是比較簡單的,不過它並不是我們熟悉的Makefile,而是經過了Android自身build system的很多處理,因此要真正釐清其中的聯繫還蠻複雜的,不過這種方式的好處在於,編寫一個新的Android.mk來給Android增加一個新的Component會比較簡單。

編譯Java程式可以直接採用Eclipse的IDE來完成,這裡就不重複了。
我們主要針對C/C++來說明,下面通過一個小例子來說明,
如何在Android 中增加一個C程式的Hello World:

1. 在$(YOUR_ANDROID)/external 目錄下創建hello目錄,
其中$(YOUR_ANDROID)指的是Android source code所在的目錄。
# mkdir $(YOUR_ANDROID)/external/hello

2. 在$(YOUR_ANDROID)/external/hello/目錄中編寫hello.c文件,
hello.c的內容當然就是經典的HelloWorld程式:

#include stdio.h

int main()
{
printf("Hello World!\n");

return 0;
}



3. 在$(YOUR_ANDROID)/external/hello/目錄編寫Android.mk文件。
這是Android Makefile的標準命名,不要更改。
Android.mk文件的格式和內容可以參考其他已有的Android.mk文件的寫法,
針對helloworld程式的Android.mk文件內容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \

hello.c

LOCAL_MODULE := helloworld

include $(BUILD_EXECUTABLE)


注意上面LOCAL_SRC_FILES用來指定 source file;
LOCAL_MODULE指定要編譯的module的名字,下一步驟編譯時就要用到;
include $(BUILD_EXECUTABLE)表示要編譯成一個可執行文件,
如果想編譯成動態庫則可用 BUILD_SHARED_LIBRARY,
這些可以在 $(YOUR_ANDROID)/build/core/config.mk 查到。

4. 回到Android source code頂層目錄進行編譯:

# cd $(YOUR_ANDROID) && make helloworld

注意make helloworld中的目標名helloworld就是上面Android.mk文件中
由LOCAL_MODULE指定的module名。編譯結果如下:

target thumb C: helloworld <= development/hello/hello.c target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld) target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld) target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld) Install: out/target/product/generic/system/bin/helloworld



5.如上面的編譯結果所示,編譯後的可執行文件存放在
out/target/product/generic/system/bin/helloworld
,通過”adb push”將它傳送到模擬器上,
再通過”adb shell”登錄到模擬器終端,就可以執行了


[Reference]
http://www.j2medev.com/android/ShowArticle.asp?ArticleID=5436

2010年4月27日 星期二

[書評] 嵌入式系統—使用 C 和 GNU 開發工具

花了幾天的時間把這本兩百多頁的薄書看完
個人是覺得內容有一看的價值
不過要花錢入手一本還是緩緩先

這本書有點像是給RD看的小說一樣
技術面其實沒有很深入
可是卻也不會太淺
我覺得有點像雞肋
不太適合初學者與進階者看
好在個人就是在這中間 XD
所以就花了點時間把它看完

可能嵌入式系統的技術真的是太雜太廣
沒辦法有一個專門的介紹
但通盤的講解所帶來的效應就是不深入

這本書在網路上也有原文版電子書
可以google一下,應該不難找
推薦看原文的
O'Reilly 的中文譯者...
我不曉得是翻譯工作太枯燥還是如何
本書章節翻譯品質參庛不齊 (O'Reilly其他本也有這毛病...)
有些地方的語意端詳半天還是不得其解
查看原文才知道其意...
這就是翻譯書的大缺點啊

好險這本書是跟我同事借的
幾天就可以看完的雞肋本
因此沒有浪費錢的感覺 XDrz

順帶一提
今年六月 O'Reilly 要出三本必敗新書
希望不要太貴..

Autotools
A Practioner's Guide to GNU Autoconf, Automake, and Libtool

The Linux Programming Interface
A Linux and UNIX System Programming Handbook

LPI Linux Certification in a Nutshell (3rd)

2010年4月26日 星期一

bitfield 位元欄位

蠻特殊的一種bitwise的宣告方法
可以視方便性與bitmask交換使用

struct
{
     unit8_t bit0 : 1;
     unit8_t bit1 : 1;
     unit8_t bit2 : 1;
     unit8_t bit3 : 1;
     unit8_t nibble : 4;
} foo;

2010年4月23日 星期五

I/O-mapped and Memory-mapped

The first address space is called the memory space and is intended mainly for memory devices;
the second is reserved exclusively for peripherals and is called the I/O space.

However, peripherals can also be located within the memory space,
at the discretion of the hardware designer.
When that happens, we say that those peripherals are memory-mapped and
that system has memory-mapped I/O.
Some processors support only a memory space,
in which case all peripherals are memory-mapped.

2010年4月19日 星期一

Ubuntu 9.10 Koala on ASUS W7S

service pack一包接一包
在不錯的硬體上面跑也是有如老牛龜車的M$ XP
實在是讓人好生失望

話說其實會使用XP也是為了一些PC game
現在開始工作以後
因為沒啥時間玩遊戲
所以必玩的遊戲都要過濾再過濾
想玩的遊戲硬體要求已是W7S不堪應付了
Intel CoreDuo T7300
2.5G RAM
nVIDIA GeForce 8400M G 128MB
以上正是W7S的硬體規格
要拿來跑Diablo III以及StarCraft II
應該會有很大的問題 XD

基於以上廢話的種種原因
決定將W7S的OS換成Ubuntu/Linux

使用的版本是Ubuntu 9.10 Karmic Koala Desktop edition

整個安裝過程就是一個順字
所有的硬體驅動也順利完成
WiFi
Bluetooth
Ethernet
Sound Card
都沒問題啦!

唯一需要手動選擇的只有nVIDIA的官方顯示驅動
因為nVIDIA這片顯卡的Linux驅動程式 source code沒有 open source
所以 Ubuntu community 不推薦使用此種 proprietary driver
也造成此driver不會被自動安裝
但還是可以自行手動選擇安裝此driver

=======
安裝完作業系統後
接下來就是一些使用者程式的選擇

輸入法我選擇使用ibus
只要安裝好語言包
就可以使用其他語言的輸入法
例如
ibus+Chewing (酷音)
ibus+Anthy (日文輸入)

使用到現在也沒有什麼特別問題,穩定。

看影片推薦使用SMPlayer

BT下載推薦 Deluge or 內建的 Transmission

本篇網誌就是在W7S+Ubuntu的組合下完成!

2010年4月15日 星期四

Linux Shell 彩色控制以及色碼簡介

\33[ pattern ; foreground ; background m
YOUR_CONTENT
\33[0m

例如: \33[1;33;40mMYMESSAGE\33[0m
==> 高亮度(1);黃字(33);黑底(40)
  • pattern
0 一般亮度
1 高亮度
4 加底線
5 灰底

[Reference]
Tinux Blog
http://tinux.no-ip.com/wordpress/?p=89

2010年4月14日 星期三

Linux Device Driver note

Linux 驅動程式的類別:
- 字元設備 char device
- 區塊設備 block device
- 網路設備 network device

一些重要的資料結構:
  • Char Device Driver
struct file_operations
struct file
struct inode
  • Block Device Driver
struct block_device
struct block_device_operations
struct gendisk
struct request
  • Network Device Driver
struct net_device
struct sk_buff

2010年4月8日 星期四

archive 程式 ar 簡介 with ranlib

ar ( archive ) 以下節錄自 Linux man page

ar - create, modify, and extract from archives
ar 這支程式可以對 archive(靜態函式庫) 做以下的動作,
create(建造),modify(修改),extract(擷取)

以下列出幾個常用的 options 解釋:

d: delete
=> 將 files 從 archive 中移除
r: replace
=> 將 files 以取代的方式插入 archive 中
u: update
=> 通常r option 是取代所有,但u option 僅會將較 archive中新的 files 插入
v: verbose
=> 顯示執行情況以及額外的資訊

ar uv mylib.a first.o second.o
上述指令就是將兩個obj 檔建立成一個 mylib.a,並且顯示建立過程的執行資訊


ranlib - generate index to archive

ranlib generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file.
ranlib這支程式會產生一個index檔,存的是archive的組成內容,並且將這個index也塞入archive中。index 會列出每個 archive 中的 member 所定義的 symbol,這些 member 說穿了就是 relocatable object file (可重定位的目的檔)。

可以使用 nm -s 或者 nm --print-armap 來列出這個 index

跑這支程式的作用是增加 linking 到 library 的速度,而且可以讓在 library 內的 routines 可以互相呼叫,而不必顧慮到這些 routines 在 archive 中的擺置順序。

實際上,GNU ranlib 就是執行 ar -s

2010年3月30日 星期二

IPC facility - Pipes

Process Pipes

#include stdio.h

FILE* popen( const char* command, const char* open_mode );
int pclose( FILE* pipe_stream );

open_mode 必須是 "r" or "w",但不允許同時開放讀寫的權限,
這是 pipe 與一般 file stream 不同的特性

FILE* read_fp;
read_fp = popen( "cat prog.c | wc -l", "r" );
nread = fread( buf, sizeof(char), sizeof(buf), read_fp );
pclose( read_fp );


The Pipe Call

#include unistd.h

int pipe( int file_descriptor[2] );
任何寫到 file_descriptor[1] 的資料,可以從 file_descriptor[0] 讀回來
*資訊的順序是 FIFO

另外由於 file descriptor 是屬於較低階的檔案處理,
所以不可以使用 file streams 相關的函數,
而需要使用較低階的 read 和 write 等等的 system calls來讀寫

一般來說,pipe 都會使用在相關的兩個 process 之間,例如 parent & child
file descriptors 會跨 fork,所以 parent 開啟的 fd,child 會有同樣的值

int pipefd[2]
pipe( pipefd );
pid_t pid = fork();

switch( pid ) {
case 0: /* parent */
close( pipefd[0] ); /* parent 關閉讀的 pipe */
write( pipefd[1], data, sizeof(data) );
break;

case -1: /* error */
exit( 1 );
break;

default: /* children */
close( pipefd[1] ); /* children 關閉寫的 pipe */
read( pipefd[1], data, sizeof(data) );
break;
}


Named Pipe: FIFO

#include sys/types.h
#include sys/stat.h

int mkfifo( const char* filename, mode_t mode );
mode 是設定 RWX 的權限,例如 0666,實際建置結果還要看系統的 mask 設定
mkfifo 執行成功後,就會產生一個 pipe file
例如 prwxr-xr-x ...... /tmp/myfifo
之後就使用 open, close, read, write 等低階函數來操作它

open 一個 pipe file,有以下三個 flag 可以使用
O_RDONLY, O_WRONLY, O_NONBLOCK (會對 open read write 3個函數產生影響 )





[Reference]
Begining Linux Programming 4th edition
Chapter 13 Inter-Process Communication: Pipes

2010年3月26日 星期五

IPC facility - Message Queue

IPC facility - Shared Memory

IPC facility - Semaphore

Semaphore

#include sys/types.h
#include sys/ipc.h
#include sys/sem.h

int semget ( key_t key, int num_sems, int sem_flags );
key在這邊是要做 IPC 的各個 process 之間共有的一個鍵值 (key value)
key需要相同,才能確保 semget 會回傳代表共用 semaphore 的 sem_id
每個 process 都會拿到只供自己使用的 sem_id 但是其指向的是共用的 semaphore
後文所述另外兩種 IPC facilities 也是採用同樣的原理

num_sems 表示需要使用的 semaphore 數量,一般最常見的值是 1
表示只使用一個 semaphore,如有需要也可以使用多個,會以陣列表示
*所以一個 sem_id 可以表示一個 semaphore 也可以表示一組 semaphores

sem_flags 可以設定類似檔案擁有的權限值,如 0666 這種值
另外可與 IPC_CREAT 或 IPC_EXCL 使用 bitwise OR 運算結合
IPC_CREAT: create a semaphore
IPC_EXCL: exclusive action

[example]
key_t key = (key_t) 54321;
int sem_id = semget ( key, 1, 0644 | IPC_CREAT );



int semop ( int sem_id, struct sembuf* sem_ops, size_t num_sem_ops );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

struct sembuf {
short sem_num; /* index */
short sem_op; /* operations on semaphore */
short sem_flg; /* SEM_UNDO */
}

num_sem_ops 表示 sem_ops 陣列的 size

[example]
/* P action [wait] */
struct sembuf sembuf_p;
sembuf_p.sem_num = 0;
sembuf_p.sem_op = -1; /* for P( ) action */
sembuf_p.sem_flg = SEM_UNDO;

/* V action [signal] */
struct sembuf sembuf_v;
sembuf_v.sem_num = 0;
sembuf_v.sem_op = 1; /* for V( ) action */
sembuf_v.sem_flg = SEM_UNDO;

semop( sem_id, &sembuf_p, 1 );



int semctl ( int sem_id, int sem_num, int command, ... );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

sem_num 用來表示 semaphore number,用在 semaphore 陣列
其值通常是 0,表示第一個而且是唯一的 semaphore ( 單一 semaphore 的情況 )

command 表示要採取的 action,主要使用的是 SETVAL 以及 IPC_RMID
SETVAL 是拿來設定值,會用到第四個參數 union semnu 中的 val 成員
IPC_RMID 是用來移除 semaphore

第四個參數,如果有設定的話,是一個 C union,定義如下

union semnu{
int val;
struct semid_ds* buf;
unsigned short array;
}

[example]
union semun sem_union;
sem_union.val = 1;
semctl ( sem_id, 0, SETVAL, sem_union );
------
semctl ( sem_id, 0, IPC_RMID, sem_union );



[Reference]
Begining Linux Programming 4th edition
Chapter 14 Semaphore, Shared Memory, and Message Queues

2010年3月24日 星期三

POSIX Threads

POSIX Threads

#include pthread.h

int pthread_create( pthread_t* thread, pthread_attr_t* attr,
void *(*start_routine)(void*), void* arg );

pthread_attr_t 可以利用其他設定 thread attribute 的函數 customize
arg 是丟入 start_routine 的參數

void pthread_exit( void* retval );

在成為 thread 的 routine 當中,呼叫 pthread_exit以傳回 retval

int pthread_join( pthread_t th, void** thread_return );

thread_return 會存 retval 的 address

跟 process-based fork-exec 組合不同的是
同屬一個 process 的 threads 之間是可以共享 全域變數
thread 是去執行一個 program 裡面的一個 function
而 fork-exec 是去執行一個 program

使用 thread 有一點需要特別注意:
critical timing condition
在使用 thread 共享的資源中,包含 call by address 的資源
要注意 thread 執行順序並沒有固定
所以資料值可能會因為 timing 的關係而產生錯誤

[example]
函數A使用 thread 的方式執行函數B,並且使用 call by address 的方式傳遞參數 x
若之後 A 會繼續改變 x 變數,而 B 在執行開始就會讀取 x 的值 (單純讀取)
Here comes the timing problem of scheduling :
A before B => B 原本要讀的值被 A 改變了
B before A => B 正確執行

[solution]
將參數傳遞的方式改為 call by value
則 A 對 x 所做的改變就不會影響到 B 的執行結果

不過若是 B 也需要改變 x 的值,則需要更進一步的處理

==========================================================

Synchronization with Semaphores

#include semaphore.h

int sem_init ( sem_t* sem, int pshared, unsigned int value );
int sem_destory ( sem_t* sem );
A semaphore is created with sem_init function, and destoryed by sem_destroy

value 的值決定是 binary semaphore 或 counting semaphore

int sem_wait ( sem_t* sem ); /* value-- */
atomically decrease the value of the semaphore by 1
int sem_post ( sem_t* sem ); /* value++ */
atomically increase the value of the semaphore by 1

利用 semaphore 可以防止 polling 所帶來的 計算資源浪費

==========================================================

Synchronization with Mutexes

#include pthread.h

int pthread_mutex_init ( pthread_mutex_t* mutex,
const pthread_mutexattr_t* mutexattr );
int pthread_mutex_destroy ( pthread_mutex_t* mutex );

int pthread_mutex_lock ( pthread_mutex_t* mutex );
int pthread_mutex_unlock ( pthread_mutex_t* mutex );

To control access to a critical section of code you lock a mutex
before entering the code section and then unlock it when you have finished.

[example]
pthread_mutex_lock( &mutex );
/***************************
***** critical section ******
***************************/
pthread_mutex_unlock( &mutex );


[Reference]
Begining Linux Programming 4th edition
Chapter 12 POSIX Threads

Processes and Signals

Starting New Processes

#include stdlib.h
int system (const char* string);
/* not recommened for use */

==========================================================

Replacing a Process Image
#include unistd.h
exec family

[example]
/* argument list vector version */
char* const ps_argv[] = { "ps", "ax", 0 };
/* environment variable vector */
char* const ps_envp[] = { "PATH=/bin:/usr/bin", "TERM=console", 0 };

-------End with null pointer-----------
execl ( "/bin/ps", "ps", "ax", 0 );
execlp ( "ps", "ps", "ax", 0 ); /* assumes /bin is in PATH */
execle ( "/bin/ps", "ps", "ax", 0, ps_envp ); /* passes own environment */
-----------vector version--------------
execv ( "/bin/ps", ps_argv );
execvp ( "ps", ps_argv );
execve ( "/bin/ps", ps_argv, ps_envp );
---------------------------------------

l 代表 list [ 表示在exec內的命令列參數直接以字串 list寫入 ]
v 代表 vector [ 表示以陣列方式將命令列參數傳入exec函數 ]
p 代表 path [ 表示 command 可以不需要標明絕對路徑 ]
e 代表 environment [ 表示以陣列方式將環境變數傳入exec函數 ]

=========================================================

Duplicating a Process Image
#include sys/types.h
#include unistd.h
pid_t fork( void );

open file descriptors are preserved across calls to fork and exec

Waiting for a Process
#include sys/types.h
#include sys/wait.h
pid_t wait( int* stat_val );
pid_t waitpid( pid_t pid, int* stat_val, int options );

WIFEXITED( stat_val )
WEXITSTATUS( stat_val )

for parent process to release zombie child processes

=========================================================

Sinals
#include signal.h
void (*signal (int sig, void (*func)(int) ) ) (int);
/* not recommened for use */

#include sys/types.h
#include signal.h
int kill ( pid_t pid, int sig );

#include unistd.h
unsigned int alarm ( unsigned int seconds );

#include signal.h
int sigaction( int sig, const struct sigaction* act, struct sigaction* oldact );


Functions that are safe to call inside a signal handler,
either to be re-entrant or not to raise signals themselves.


[Reference]
Begining Linux Programming 4th edition
Chapter 11 Processes and Signals

2010年3月23日 星期二

Linux 程式設計 --使用C語言--

Linux開發工具介紹
--GNU make
--GNU gcc

Linux檔案處理
--低階 file descriptor [ int fd = open( ) ]
open, close, read, write, creat, link, unlink, symlink, ...

--高階 file stream [ FILE* fp = fopen( ) ]
fopen, fclose, fread, fwrite, ...

Linux環境
command line argument
environment variable
date and time
temporary file creation
user information
host information
logging
resource limits

struct file 簡介

struct file 定義在 /linux/include/linux/fs.h(Linux 2.6.11 核心)中,其原型是:




struct file 代表一個open的 file,
系統中的每個open的file在kernel space都有一個關連的struct file,
此資料結構由kernel在open file時創建,
並傳遞給在file上進行操作的任何函數。
在file的所有instance都close後,kernel會釋放這個資料結構,
在kernel或者driver source code中,
struct file的pointer通常被命名為file或filp。


[Reference]
http://linux.chinaunix.net/techdoc/system/2008/08/19/1026178.shtml

2010年3月17日 星期三

GDB 使用簡介

*編譯原始程式碼時,gcc需加-g選項,以在程式中加入除錯資訊
  • $ gcc -g -o hello hello.c
*執行GDB有兩種方式
  • 先單獨鍵入gdb,等進入gdb環境之後,再鍵入file hello
  • 直接鍵入 gdb hello
*退出GDB有兩種模式
  • (gdb) quit 完全退出GDB程式,回到 linux console
  • (gdb) kill 終止當前正在除錯的程式,仍在gdb環境下
*原始程式碼的檢視,指令是 list,預設顯示10行
  • (gdb) list [行號|函數名稱]
  • (gdb) list 9 或者是 list main
*設定程式的命令列參數,指令是 set args [arg1 ... argn]
  • (gdb) set args add 2 3
  • 如同執行 ./hello add 2 3
*在GDB中執行程式
  • run
  • step [單步執行,若有函數呼叫,會進入函數內部]
  • next [單步執行,不會進入函數內部]
  • continue
*中斷點 (break points) 的操作
  • 設定:break [行號|函數名稱|條件運算式]
  • 撤銷:delete [中斷點編號]
  • 查看:info break
*監看點 (watch points) 的操作
  • 設定:watch [變數名稱]
  • 查看:info watchpoints



[Reference]
Linux系統程式設計 使用C

2010年3月5日 星期五

GNU make manual 閱讀整理

Chpater 4 Writing Rules

Chapter 5 Writing the Commands in Rules

@command: suppress the echoing
-command: suppress the error, keep going
+command: ignore -n -t -q options
=======================
-n: --just-print
-t: --touch
-q: --question
=======================

2010年3月3日 星期三

C語言的Ternary Operator

C Variants

A GNU extension to C allows the second operand to be omitted,

and the first operand is implicitly used as the second as well:

a = x ? : y;

The expression is equivalent to

a = x ? x : y;

except that if x is an expression, it is evaluated only once.

The difference is significant if evaluating the expression has side effects.

2010年2月24日 星期三

Shell Script 進階變數功能

變數擴展 ${varname}
命令替換 $(command)
算術擴展 $((expression))

${var:-default}
測試var,若未定義或為null值,則傳回default,否則傳回var
${var:=default}
測試var,若未定義或為null值,則將var設為default
${var:?message}
測試var,若未定義或為null值,則印出message,並且中斷script執行
${var:+value}
測試var,若未定義或為null值,則傳回value,否則傳回null

上述各式,若移去 :,則只測有無定義,不測是否為空值

-----------------------------------------------------------
取字串切片
${var:pos}
${var:pos:len}

計算字串長度
${#var}

取得陣列元素個數
${#array[@]}
${#array[*]}

比對樣式,做刪除字串的動作
${var#pattern}
${var##pattern}
${var%pattern}
${var%%pattern}

#: 從最左邊開始
%: 從最右邊開始

比對樣式,做取代的動作
${var/pattern/sub_str}
${var//pattern/sub_str}
${var/#pattern/sub_str}
${var/%pattern/sub_str}


[Reference]
Linux Shell 程式設計實務 Chapter 7

2010年1月21日 星期四

指標 pointers 重點整理

char multi[5][10];

What is (multi+1)?
What is *(multi+1)?

它們是一樣的!
都是 &multi[1][0] 這個 address

multi[row][col] 等於 *( *( multi + row) + col)


*ptr++ 等價於 *(ptr++) ... 什麼鬼東西= =

*dynamic memory allocation of n-dimensional array (n>1)

A. 二段式分配法,先尻重拳,再追加昇龍拳!

int** rowptr;
rowptr = malloc( nrows * sizeof(int *) );
rowptr[i] = malloc( ncols * sizeof(int) );

總共有1+nrows次的malloc呼叫
雖然可以使用rowptr[i][j]這種array notation的方式來存取
但是實際上memory是不連續的


B. 放大绝先,再慢慢消血!

int** rowptr;
int* aptr;
aptr = malloc( nrows * ncols * sizeof(int) );
rowptr = malloc( nrows * sizeof( int * ) );
/* ............................ */
rowptr[0] = aptr + ( 0 * ncols );
rowptr[1] = aptr + ( 1 * ncols );
rowptr[2] = aptr + ( 2 * ncols );
/* ............................ */

總共只有兩次的malloc
優點是事先分配的一大塊空間是連續的
所以使用像memset( ) 這樣的函數可以一次到位
缺點是記憶體空間的使用比較沒有彈性
例如使用 free( ) 函數時
需要一整塊還回去