2023年12月25日 星期一

discontinuous points of 12-hour vs 24-hour time systems

12小時制 (12-hour  time system) 和24小時制時間 (24-hour time system) 乍看很容易轉換。凡遇到12小時制的 PM (Post Meridem, 下午) 就數字加12小時,刪掉 PM,變成24小時制;遇到12小時制的 AM (Ante Meridem,上午) 就數字不動,刪掉 AM,變成24小時制。大部份情況是如此沒錯,但是在半夜12點,半夜1點,中午12點,中午1點前後,兩者轉換有點跳躍,不連續,易生混淆。

簡單講,12小時制是沒有半夜 00:00AM-00:59AM 或中午 00:00PM-00-59PM 這兩種寫法的。

    12小時制的四處不連續點 (discontinuous points) 包括
  1. 中午 11:59AM 的下一分鐘是 中午12點 12:00PM,由 AM 跳成 PM
  2. 中午 12:59AM 的下一分鐘是 中午1點 01:00AM,由 12:59 跳成 01:00
  3. 半夜 11:59PM 的下一分鐘是 半夜12點 12:00AM,由 PM 跳成 AM
  4. 半夜 12:59PM 的下一分鐘是  半夜1點 01:00PM,由 12:59 跳成 01:00
    而相形之下,24小時制只有一處不連續點
  1. 半夜 23:59 的下一分鐘為半夜 00:00,由 23:59 跳成 00:00

以下為兩者24小時對照表,其中紅色標記處為常易混淆處。即半夜0-1點,在12小時制稱為半夜 12:00AM-12:59AM,而非 00:00AM-00:59AM。中午12-13點,在12小時制稱為中午 12:00PM-12:59PM,而非 00:00PM-00-59PM

24小時制 12小時制
00:00 - 00:59 半夜 12:00AM - 12:59AM
01:00 - 01:59 01:00AM - 01:59AM
02:00 - 02:59 02:00AM - 02:59AM
03:00 - 03:59 03:00AM - 03:59AM
04:00 - 04:59 04:00AM - 04:59AM
05:00 - 05:59 05:00AM - 05:59AM
06:00 - 06:59 06:00AM - 06:59AM
07:00 - 07:59 07:00AM - 07:59AM
08:00 - 08:59 08:00AM - 08:59AM
09:00 - 09:59 09:00AM - 09:59AM
10:00 - 10:59 10:00AM - 10:59AM
11:00 - 11:59 11:00AM - 11:59AM
12:00 - 12:59 中午 12:00PM - 12:59PM
13:00 - 13:59 01:00PM - 01:59PM
14:00 - 14:59 02:00PM - 02:59PM
15:00 - 15:59 03:00PM - 03:59PM
16:00 - 16:59 04:00PM - 04:59PM
17:00 - 17:59 05:00PM - 05:59PM
18:00 - 18:59 06:00PM - 06:59PM
19:00 - 19:59 07:00PM - 07:59PM
20:00 - 20:59 08:00PM - 08:59PM
21:00 - 21:59 09:00PM - 09:59PM
22:00 - 22:59 10:00PM - 10:59PM
23:00 - 23:59 11:00PM - 11:59PM

考慮到12小時制有4處不連續跳躍點,若要設定任何日期時間期限到半夜12點,建議截止期 (deadline) 選擇截止日期的 11:59PM,比較不會出錯。因為選擇截止日期的 12:00PM 將會變成當天中午截止,短少12小時。而若選擇截止日期加1日的 12:00AM,雖然正確,卻有點不自然,會想東想西,怕哪裏出問題。

2023年11月24日 星期五

how to add libraries for a NetBeans project

如何為 NetBeans 專案添加類別庫,下面以添加 Apache Commons-CSV 讀取CSV檔案的類別庫為例。

下載及解開 Apache Commons-CSV 類別庫
    URL: https://commons.apache.org/proper/commons-csv/download_csv.cgi
	     點選 Binaries/commons-csv-1.10.0-bin.zip
    解壓後路徑: C:\...\commons-csv-1.10.0 

從 NetBeans 左欄面板,如下點選填入名稱,類別檔,原始碼,註解檔路徑。
Projects/Project Name/Libraries/Add Library.../Create
	Library Name: Commons-CSV
	Library Type: Class Libraries

	Classpath/Library Classpath: 類別檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0.jar

	Sources/Library Sources: 原始碼路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0-sources.jar

	Javadoc/Library Javadoc: 註解檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\apidocs

設定類別檔,原始碼,註解檔的完整路徑,安裝類別庫之後,NetBeans專案就可以使用如下類別庫套件:
  org.apache.commons.csv.*

遇到有疑問的類別或方法,可選擇其名字,按如下選單或快速鍵,查看其註解或原始碼:
看註解: Source/Show Documentation (Ctrl-Shift-Space)
看原始碼: Navigate/Go to Source (Ctrl-Shift-B)

2023年11月3日 星期五

why thrashing occurs in virtual memory systems

依據現代電腦的馮紐曼運算架構,CPU始終只能從記憶體讀取資料執行,不能直接從硬碟讀取資料。因此,程式常受限於記憶體的容量限制而無法載入執行。可是明明電腦系統的硬碟容量常是記憶體的上千倍,難道不能克服困難,將硬碟當成記憶體使用嗎?

虛擬記憶體 (Virtual Memory)就是這樣一個應急措施,可將硬碟分一塊置換空間 (Swap Space),當成記憶體用。其明顯好處是從此電腦不再受記憶體的容量限制,再大、再多的程式也可執行。只要硬碟夠大,頂多暫時存放在硬碟的置換空間,要用時再搬進記憶體執行即可。

但記住虛擬記憶體始終只是一個應急措施,不能當成常態使用。理由是資料在記憶體和硬碟之間搬移 (swapping) 時,要花費很久的等候時間,互動式用戶將等的不耐煩,故只適合非不得已偶一為之。例如,記憶體的批次用戶較多,可暫時挪到硬碟休息,先讓互動用戶使用記憶體。

採用虛擬記憶體有一個副作用是如果管理不好,分配給程式的實體記憶體不夠,須要先將暫時不用的程式從記憶體搬出到硬碟 (page out),才能騰出空間,讓須要的程式碼從硬碟搬回到記憶體 (page in) 執行。萬一記憶體極端不足情況下,很可能剛搬出的程式,等下又要再搬入,造成所謂的掙扎 (thrashing) 現象。出現此反常現象時,系統效能將變極差,只見硬碟不斷亮燈,苦苦忙著將資料從記憶體搬出搬進,I/O使用率陡升,而CPU卻坐等資料搬好,閒閒沒事幹,CPU使用率陡降。

註: 上述 掙扎現象的英文 thrashing 查字典有如下諸多意思:
           掙扎,輾轉,擺盪,振盪,顛簸,抖動,猛移,往復移動,頻繁置換;
           徒勞,作虛功,無進展,白費力氣,原地踏步;打敗,痛打,窮忙,窮打,窮追猛打
       常見直譯有 輾轉,擺盪,甚至意譯 窮忙,作虛功,也都很傳神。
       選擇 掙扎 則兼有直譯及意譯,猶如泳者奮力浮水,離水之魚奮拍入水之勢。

active versus passive multiprogramming

多元規劃 (multiprogramming) 是作業系統演進上,早期一項重要發明。早期電腦的記憶體一次只放一支程式,遇到程式執行I/O指令,等候I/O結果,CPU就閒置浪費掉。因此,利用多元規劃技術,在記憶體放進多支程式,遇程式等I/O時,就將CPU分配給其他程式使用,如此即可提升CPU使用率 (utilization)。

至於為何將技術取名為多元規劃,讓人從字面不易猜透其義? 猜測可能是參考當時流行的線性規劃 (linear programming),整數規劃 (integer programming) 等最佳化技術的命名。顧名思義,多元 指的是記憶體一次載入多支程式,規劃 指的是安排多支程式執行。而某種意義上,這也的確是讓CPU使用率提升的一種最佳化技術。如果對多元用語的語義不明有芥蒂,也許稱為 多程式規劃 會更一目暸然。一個電腦系統的 多元規劃度 (degree of multiprogramming) 為記憶體可放進多少支程式的數量,其值越高,表示CPU有越多程式可執行,越有利於提升CPU使用率。

多元規劃依據作業系統排班器對CPU掌控程度分為兩種。主動式多元規劃 (active multiprogramming) 利用計時器中斷主動介入, 給定時間一到,CPU即須讓出跑其他程式,可防止CPU遭程式霸佔;被動式多元規劃 (passive multiprogramming) 的排班器只能被動等候程式志願退出或作I/O,才能重新取得CPU分配給其他程式,容易造成CPU遭程式霸佔。 

2023年10月18日 星期三

how to solve the tower of hanoi by recursion versus simulated call stack?


/*
   TowerOfHanoi.java  遞迴版 及 模擬呼叫堆疊版 求解河內塔 

> java TowerOfHanoi
Hanoi Tower by Implicit Call Stack 遞迴版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]

Hanoi Tower by Explicit Stack 模擬呼叫堆疊版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]
*/
import java.util.Stack;

public class TowerOfHanoi 
{
    static Stack stackA = new Stack<>();  // 柱A
    static Stack stackB = new Stack<>();  // 柱B
    static Stack stackC = new Stack<>();  // 柱C
    static int nDisks = 5; // Number of disks 盤數
    static int count = 0;  // 步數
    static boolean printGoal = false;  // 列印目標否
    static boolean printOperation = true;  // 列印步驟否
    static boolean printStack = false;  // 列印模擬呼叫堆疊否

    // 印n格空白
    public static void printSpaces(int n)
    {
        for(int i=0; i <= n -1; i++) System.out.print(" ");
    }
    
    // 印柱A,柱B,柱C堆疊,前面n層內縮
    public static void printStacks(int n)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("A:" + stackA);
        sb.append(", B:" + stackB);
        sb.append(", C:" + stackC);

        System.out.println();
        printSpaces(n*8);  // 每層內縮8格
        System.out.print(sb.toString());
    }
    
    // 搬移柱from頂一個盤子到柱to
    public static void transfer(char from, char to)
    {
        if(from=='A' && to=='B') stackB.push(stackA.pop());
        if(from=='A' && to=='C') stackC.push(stackA.pop());
        if(from=='B' && to=='A') stackA.push(stackB.pop());
        if(from=='B' && to=='C') stackC.push(stackB.pop());
        if(from=='C' && to=='A') stackA.push(stackC.pop());
        if(from=='C' && to=='B') stackB.push(stackC.pop());
    }
    
    // 遞迴版解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void solveHanoi(int n, char source, char auxiliary, char target) 
    {        
        if(printGoal) 
        {
            System.out.println();
            printSpaces((nDisks - n)*8);
            System.out.print(String.format("hanoi(n:%d,s:%c -> t:%c)",n,source,target));
        }

        if (n == 1) 
        {
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

            transfer(source, target);            
        } 
        else 
        {
            solveHanoi(n - 1, source, target, auxiliary);

            printStacks(nDisks - n);            
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
            transfer(source, target);
            printStacks(nDisks - n);

            solveHanoi(n - 1, auxiliary, source, target);
        }
    }

    // 模擬呼叫記錄    
    static class HanoiCallRecord
    {
        int num;
        char source;
        char auxiliary;
        char target;
        int stage; // 0 for moving n-1 disks from source to auxiliary rods; 
                   // 1 for moving the disk n from source to target rods
                   //   and moving n-1 disks from auxiliary to target rods
                   // 2 for backtracking to the previous call record
        
        // 建構子
        public HanoiCallRecord(int num, char source, char auxiliary, char target)
        {
            this.num = num;
            this.source = source;
            this.auxiliary = auxiliary;
            this.target = target;
            this.stage = 0;  // 預設從階段0開始
        }
        
        // 列印呼叫記錄
        public String toString()
        {
            return String.format("(n:%d,s:%c,a:%c,t:%c,s:%d)",
                    num, source, auxiliary, target, stage);
        }
    }
    
    // 模擬呼叫堆疊,解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void hanoiUsingStacks(int num, char src, char aux, char tgt) 
    {
        Stack stack = new Stack<>();
        
        HanoiCallRecord initial = new HanoiCallRecord(num, src, aux, tgt);
        stack.push(initial);  // 壓入第1層呼叫記錄

        while (stack.isEmpty()==false) 
        {
            if(printStack) System.out.print("\n" + stack);
            
            HanoiCallRecord current = stack.peek();  // 檢視本層呼叫記錄
            int n = current.num;
            char source = current.source;
            char auxiliary = current.auxiliary;
            char target = current.target;
            int stage = current.stage;
            
           if (n == 1) // 執行特別任務,然後退回上一層任務
           {
                if(printOperation) 
                    System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

                transfer(source, target);
                stack.pop();  // 彈出本層呼叫記錄,
            } 
            else if(stage == 0) // 階段0, 執行本層第0階段任務
            {
                // solveHanoi(n - 1, source, target, auxiliary);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, source, target, auxiliary);
                stack.push(next); // 壓入下層呼叫記錄
                current.stage++;  // 更新本層呼叫記錄的階段欄位
            }
            else if(stage == 1) // 階段1, 執行本層第1階段任務
            {
                printStacks(nDisks - n);            
                if(printOperation)
                    System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
                transfer(source, target);
                printStacks(nDisks - n);

                // solveHanoi(n - 1, auxiliary, source, target);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, auxiliary, source, target);
                stack.push(next);  // 壓入下層呼叫記錄
                current.stage++; // 更新本層呼叫記錄的階段欄位
            }
            else if(current.stage == 2) // 階段2,本層任務完成,退回上一層任務
            {
                stack.pop();  // 彈出本層呼叫記錄,
            }
        }
    } 

    // 測試主程式
    public static void main(String[] args) 
    {
        nDisks = 3; // Number of disks
        count = 0;
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
        
        System.out.print("Hanoi Tower by Implicit Call Stack 遞迴版");

        printStacks(0);
        solveHanoi(nDisks, 'A', 'B', 'C');
        printStacks(0);
        
        // ===================================
    
        count = 0;
        stackA.clear();
        stackB.clear();
        stackC.clear();
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
                    
        System.out.print("\n\nHanoi Tower by Explicit Stack 模擬呼叫堆疊版");
        
        printStacks(0);
        hanoiUsingStacks(nDisks, 'A', 'B', 'C');
        printStacks(0);
    }
}    

2023年7月20日 星期四

how to categorize the various VPN technologies?

虛擬私有網路(VPN,Virtual Private Network) 技術可在公有網路上傳送加密封包,讓外部主機猶如處於公司內部網路。VPN依管理者是誰分為企業VPN服務供應商VPN企業VPN依據使用情境分成站點對站點類遠端存取類VPN

站點對站點類VPN使用技術包含IPsec VPN,GRE over IPsec,思科DMVPN,IPsec VTI。遠端存取類VPN使用技術包含客戶端IPsec VPN連線,無客戶端SSL連線。其中,GRE為通用路由封裝(Generic Routing Encapsulation),DMVPN為動態多點虛擬私有網路(Dynamic Multipoint Virtual Private Network),VTI為虛擬隧道介面(Virtual Tunnel Interface)。

服務供應商VPN包含新式 第2層 或 第3層 MPLS VPN,及傳統 訊框中繼 或 ATM VPN。其中,MPLS 為多重協定標籤交換(MultiProtocol Label Switching),ATM 為非同步傳輸模式 (Asynchronous Transfer Mode)。

IPsec (Internet Protocol security) 屬於待評提案 (Recommendation For Comments, RFC) 之標準,定義了 VPN 如何認證和保護 IP 封包的安全。IPsec 可以保護從第 4 層到第 7 層的流量。IPsec 提供重要安全功能,包含使用加密技術保證外人看不懂,達到機密性;使用雜湊技術保證不遭篡改,達到完整性;使用 Diffie-Hellman 系列的網際網路金鑰交換 (Internet Key Exchange, IKE) 協定,以預先共用金鑰(密碼),數位憑證,或 RSA 憑證進行驗證,達到來源及去處之防偽

IPsec 透過封裝安全協定 (Encapsulation Security Protocol, ESP) 支援傳輸模式 (Transport Mode) VPN,供端點對端點 (end-to-end) 主機之間加密傳輸;以及隧道模式 (Tunnel Mode) VPN,供站點對站點 (site-to-site) 網段之間加密傳輸。

以上總結可將VPN技術分類如下:

  • A.企業自架VPN
    • 1.遠方存取VPN (即端點對端點VPN): 
      •  無客戶端 SSL/TLS VPN (第4層)
      •  客戶端 IPsec傳輸模式VPN (第3層)
    • 2.站點對站點VPN
      •  IPsec隧道模式VPN (第3層)
      • GRE over IPsec
      • 思科DMVPN
      • IPsec VTI
  • B.供應商維護VPN
    • 1.傳統:訊框中繼 或 ATM VPN
    • 2.新式:MPLS VPN

 參考: CCNA3: 08 VPN and IPsec Concepts

2023年7月18日 星期二

why so design the same origin policy and cors?

為了確保網頁不去攻擊別人,瀏覽器一般會實作同源政策(Same Origin Policy, SOP),限定網頁內嵌讀取,或腳本連線存取的對象,必須是相同網站的其他資源,例如圖片,影音,API服務等。

但是很多情況,網頁須要跨域存取資源,遂有伺服端提供跨源資源分享(Cross-Origin Resource Sharing, CORS)的設計。其原理是瀏覽器向伺服器提出請出時,送出來源(Origin)標頭,由伺服器依據組態,回應存取控制允許來源(Access-Control-Allow-Origin) 標頭,供瀏覽器決定是否存取資源。若請求無來源標頭,或來源不在組態設定的來源清單中,則不回應存取控制允許來源標頭

這種跨源分享的防弊設計建立在瀏覽器及伺服器雙方合作的基礎之上。瀏覽器須能自我克制,參考伺服器意見,才決定是否取用資源。因為如果使用wget或curl等客戶端命令列工具,其實也可忽視伺服器意見,直接取用資源。

又CORS機制的設計為何不是網頁宣告我可以取用誰,而是設計成網頁宣告誰可以取用我。其理由在若採取前者,網頁宣告我可以取用誰,則駭客只要攻入一個網站,就可以輕易偽造此宣告,達成跨域存取任意資源的後果。而現實採取後者,網頁宣告誰可以取用我,其理由在駭客須一一攻入資源提供方,顯然不符成本。

因此CORS機制,採用由伺服器回應瀏覽器,你是否在我的來源許可清單中的無老虎牙設計,顯然其防弊精神不在避免伺服器受攻擊,而是在瀏覽器主動SOP配合下,避免網頁成為攻擊工具,又能合理取用受控資源。

Reference: 1.Medium: 簡單弄懂同源政策 (Same Origin Policy) 與跨網域 (CORS)

2023年4月14日 星期五

apps for monitoring CPU and GPU temperatures and other resources on Linux

想隨時用手機 App 透過 ssh 連線監視 Linux 機器的資源使用硬體溫度可考慮如下作法。

(1)  set up a monitor account on Linux 建立監視用帳號

$sudo adduser  --home=/home/user_name  user_name

$grep user_name /etc/passwd

$sudo edquota -p  source_user   user_name

$sudo quota -u user_name

(2) set up .ssh store in the monitor account 建立帳號公鑰存放目錄

mkdir -p ~/.ssh && chmod 700 ~/.ssh 

cat >> ~/.ssh/authorized_keys

chmod 600 ~/.ssh/authorized_keys

(3a) set up DaRemote App for Android 設定Android App

            Configure Name, Host, User, Key/Password 設定主機

    Monitor CPU/Memory/Disk/Network including CPU temperature 監視資源

    Use Shell Terminal 使用終端機

(3b) set up ServerCat App for iOS 設定 iOS App

ServerCat

  Status: Temperature 監視主機資源

Monitor CPU/Load Memory/Swap Upload/Download Read/Write

including core usage, threads, memory,
                  GPU memory, CPU/GPU temperature, disk usage

  Configure Host/+/ 設定受監視主機

Name,  Host,  Port,  User,  Password, SSH Key

  Execute/Select host 執行上傳公鑰指令

        Copy the Shell Command from KeyChain Store to 

                    Send the Public Key to the remote host

                    cmd: echo 'ssh-ed25519 xxxxxxxxxxxx ' >> !/.ssh/authorized_keys

Execute 

  Mange KeyChain/+/ 管理登入金鑰

        Configure a new SSH Authentication Key Pair 建立登入金鑰

                    Generate 2048 bit RSA Key, or

    Generate ED25519 Key (Recommended)

Copy SSH Authentication Key to Server 產生上傳公鑰指令

  echo 'ssh-ed25519 xxxxxxxxxxxx ' >> !/.ssh/authorized_keys