顯示具有 Linux 標籤的文章。 顯示所有文章
顯示具有 Linux 標籤的文章。 顯示所有文章

4.05.2024

刪除Socket Programming的一些相關網誌。。。。。

大約在六年前 (2018)的時候,當時在學習一些Socket Programming的東西,寫了一些簡單的範例,把它們放在BloggerGoogle Drive

不過,現在已經有GitHubGitLab了!就不用這麼麻煩,把程式碼Copy-Paste下來再放上去就行了;網誌當然也沒有存在的必要,就把它刪除吧!

  • Linux C, getnameinfo()
  • Linux C, getaddrinfo(), gai_strerror(), freeaddrinfo()
  • Linux C, getprotobyname(), getprotobynumber, getprotoent(), endprotoent()
  • Linux C, gethostbyaddr(), gethostbyname()
  • Linux C, getservbyname(), getservbyport, getservent(), endservent()
  • Linux C, gethostname(), getpeername()
  • Linux C, htonl(), htons(), ntohl(), ntohs()
  • Linux C, inet_addr(), inet_aton(), inet_ntoa(), inet_pton(), inet_ntop()
  • Linux C, getifaddrs(), freeifaddrs()
  • Linux Socket Programming 初學筆記

  • 8.05.2023

    編譯器步驟

    1、語彙分析 (Lexical Analysis)
    • 分析程式中每一個字眼 (Word)的意義是什麼
    • 那些是註解 (Comment),註解在編譯過程會被編譯器忽略掉
    • 那些是關鍵字 (Keyword,如:int, for, while等)
    • 那些是常數 (Constant),如:1, 12, "Embedded"等等
    • 那些是運算子 (Operator),如;+、-、*、/等等
    2、語法分析 (Syntax Analysis)
    • 主要是將程式符號,轉換成階層式的語法樹 (Syntax Tree)符號表式。在這個語法樹中,在正常情況下,階層最高的節點 (Node)為 Assign 的符號,其餘的節點為其他的運算符號,而葉子 (Leaf)就都是變數的標記 (Token)
    3、語意分析 (Semantic Analysis)
    • 是藉由語法樹 (Syntax Tree)來分析程式的邏輯與語法是否符合規定。這個階段就是用來分析程式的「文法」是否正確,已經從文字符號的階段進入了程式語意的判別。
    4、中間碼的產生 (Intermediate Code Generate)
    • 就是從語法樹 (Syntax Tree)中,以一個節點 (Node)為基本單位,從最底層的節點依序往上,拆解成一個個最基本的運算式,而每一個節點也會賦予一個暫時性的符號。
    5、程式碼的最佳化 (Code Optimize)
    • 基本上就是化簡一些不必要的暫時性節點符號。
    • 當然,另外還有一些特別的最佳化演算法也會在這個階段使用,例如針對迴圈邏輯的最佳化有三種知名的演算法:Code Motion、Induction Variable、Strength Reduction;因為迴圈邏輯在語法上是最沒有執行效率的語法之一,因此需要特別的最佳化。或者,有時候編譯器會調整程式的前後順序,為了在下一個階段程式碼的產生過程中,暫存器的使用數目降低。
    6、程式碼的產生 (Code Generate)
    • 編譯器的最後一個步驟就是將程式碼產生出來。若以C語言為例,這裡就是將最佳化後的中間碼,搭配微處理器的暫存器,逐一轉換成組合語言。

    1.22.2022

    GIT基本指令筆記

    git也是一套Source Code Control System (源代碼管理系統),歷史的進化就是:RCSCVSSVNGIT。在2022的今天,會知道RCS的應該沒幾個,因為這是2000年以前的骨董。

    在這裡把自己之前所做的一些常用的指令記錄下來,方便日後查閱!

    # git init

    初始化一個Client端的Git基本設定。

    # git config --local user.name "(Your Name)"
    # git config --local user.email "(Your E-Mail)"
    # git config --local core.editor "/usr/bin/scite"

    core.editor是可以修改git commit時的文字編輯器指令;預設是vi或是vim。如果用不習慣可以使用此一指令來修改。

    除了"--local"之外,還有"--globle";兩者的差異在於前者是「此一目錄下的專案」,而後者是針對「所有的專案」。

    # git config --list

    可以查看自己git的相關設定;而設定檔則是在自己家目錄下的".gitconfig"它是一個隱藏檔。

    # git config --local alias.co "checkout"

    設定"git"開頭之後的指令別名 (即縮寫)。如上範例:"git checkout"可改寫成"git co";指令的全名建議用雙引號給括起來,尤其是中間含有空格的複合式指令。

    ◎ 若在"git add"之後又有修改檔案,需要再重新"git add"一次!

    # git commit -m "(Your Commit Log Message)"

    若在commit後面沒有接上"-m"參數,會以預設的編輯器跳出一個空白的檔案讓使用者輸入。

    # git commit -m --allow-empty

    "--allow-empty"意思是允許一個空白的commit。

    # git commit -a

    等價於:git add & git commit; 但是僅能用在"已存在"的檔案;"新增"或是"刪除"的檔案並不適用。

    # git log

    可以查看commit的紀錄,有一些參數可用:

    # git log --graph "(string...)":尋找commit訊息裡的某一特定字串。
    # git log --author "picasso"
    # git log --author "picasso | mike"

    尋找commit log的作者為"picasso"或是"mike"。

    # git log -S "picasso"

    commit的「檔案內容」有"picasso"這個單字。

    # git log -p (FileName)

    若有加上檔名,則是可以單獨查看那些檔案的commit log;而"-p"參數還可以查看每一次的差異在哪兒!

    # git log -g

    等價於git reflog,若使用了reset之後,用上述指令可以查看HEAD移動歷史紀錄。reset到過去的某一個版本也可以透過SHA1 ID。

    # git log --since="9am" --until="12am" --after="2017-01"

    尋找2017年一月以後,每日09:00~12:00的commit紀錄。

    # git rm (FileName) --cached

    它的意思是讓該檔案脫離git的控管,即該檔案由Tracked (追蹤中)變成Untracked (非追蹤中);但這並不會刪除檔案。

    # git rm (FileName)

    從git中移除該檔案;之後直接git commit即可。

    # git mv (FileNam1) (FileNam2)

    將檔案重新命名。

    # git commit --amend --no-edit

    "--amend"僅能用在最後一次的修改;而"--no-edit"則是我不需要修改到commit log。

    ◎ 若只「新增一個目錄或是資料夾」,git是無法判斷的,當然也無法add和commit;此時的作法是在該目錄下新增一個".gitkeep"隱藏檔讓git可以偵測到新增了一個檔案。

    ◎ 若在git專案中有一些檔案不需要被git控管可以在專案的根目錄下建立一個".gitignore"隱藏檔,把那些不需要被控管的檔案寫進去。如此,那些檔案在執行git status時就不會出現"Untracked"的狀態。

    ◎ 但是,".gitignore"只會忽略".gitignore"檔案建立時間之後的檔案。

    ◎ 若".gitignore"內的檔案忽然想要被git控管的話,則是執行:"git add -f (FileName); "即Force (強制)的意思。

    ◎ 若要讓.gitignore建立的時間點「之前」的檔案也適用此一規則,則是執行:git rm --cached

    ◎ 若.gitignore內的檔案,除了不被控管之外,也不想要了,則是執行:git rm -fX (FileName); 即強制執行刪除。

    # git blame (FileName) -L 5,10

    可以查看該檔案的每乙行commit紀錄;而"-L 5,10"參數則是指定修改該行的前5行到後10行。

    # git checkout (FileName) HEAD~2

    可以回覆被刪除或是被修改的檔案;而"HEAD~2"則是指定回覆到至今的前2個commit的版本。

    # git reset --soft | --hard | --mixed HEAD~X

    讓現在的專案狀態回覆到前X個commit的版本;但"--hard"參數是最常用的。HEAD是指目前所在的分支。

    # git branch

    可以查看目前有哪些分支。預設的分支名稱是"master"。

    # git checkout (Branch Name)

    切換分支。

    # git add -p (FileName)

    可以commit該檔案的"部分內容"。

    # git branch cat

    建立一個叫"cat"的分支。

    # git branch cat -m tiger

    把"cat"這個分支改名為"tiger"。

    # git branch -d tiger

    把"tiger"這個分支刪除;若"tiger"尚未合併回主分支 (master)則是無法刪除,此時必須改用"-D"參數。

    # git checkout -s dog

    若切換到一個不存在的分支,則會產生Error Message;此時可以加上"-s"參數,會建立該分支並同時切換

    # git checkout master & git merge dog

    合併分支必需要先切換到"被合併"的分支,再合併。如上例是:先切換回master主分支,然後再將dog分支合併過來。

    # git merge cat --no-ff

    合併分支時不要使用快轉模式 (Fast-Forward)

    ◎ 倘若砍掉了一個尚未合併的分支 (-D);假如有記下當時的SHA1 Code,還是可以救的回來:git branch (Branch Name) (SHA1 Code)

    # git checkout cat & git rebase dog;

    我現在切換到"cat"分支,但我要回到rebase到"dog"分支的基準點。

    沒事兒不要用rebase來合併分支,盡量使用merge。

    ◎ git的標籤分兩種:Lightweight TagAnnoteted Tag建議使用後者

    # git tag "0.0.0.1_release" (SHA1 Code)

    這是Lightweight Tag的語法;若無指定最後的SHA1 Code,則是以現行commit為Tag

    # git tag "0.0.0.1_release" -a -m "Hello!! World!! "

    這是Annoteted Tag的語法,"-a -m"的意思是要新增此一Tag的訊息。

    # git cherry-pick (SHA1 Code) (SHA1 Code)

    cherry-pick可以撿別的branch commit過來用;倘若撿過來但不要合併可以加上"--no-commit"參數。

    10.17.2020

    Linux的find和grep指令

    這一次自己要記下的,是在Linux環境下用到爛掉的findgrep指令;前者是用來找檔案,而後者則是在檔案中尋找特定的字串
    [root@localhost]# 
    [root@localhost]# find ./ -name "*.txt" -print^C
    [root@localhost]# 
    
    上述的指令是非常基本的用法;但還有一些不錯用的參數值得記一下:

    -name: 指定檔案名稱,這個檔案名稱也可以搭配正規表示法 (Regular Expression)

    -iname: 指定檔案名稱,但不區分大小寫字母

    -type: 可以蒐尋特定的檔案類型;所謂的檔案類型,就是當我們執行"ls -l"指令時,每1個檔案最前面的字母。如下:
    • -: 一般檔案 (File)
    • d: 目錄 (Directory)
    • b: 區塊設備 (Block)
    • c: 字元設備 (Character)
    • l: 符號連結軟連結檔案 (Symbol Link)
    • p: 管線檔案 (Pipe)
    • s: 網路套接檔案 (Socket)
    -xtype: 與"-type"類似,但只會查找Symbol Link型態的檔案。

    -perm: 查找特定權限 (Permission)的檔案,如"-perm 0777"。

    -empty: 尋找空的檔案或是空的目錄,如:
    [root@localhost]# 
    [root@localhost]# find ./ -type f -empty
    [root@localhost]# 
    [root@localhost]# find ./ -type d -empty
    [root@localhost]# 
    
    -user / -group: 指定尋找檔案特定的使用者或是群組,例如:-user "picasso"-group "chinese"

    -size: 指定尋找檔案特定的大小:
    • -size 50M // 剛好是 50 MB
    • -size +50M // 大於 50 MB
    • -size -100M // 小於 100 MB
    • -size +50M -size -100M // 大於 50 MB ,且小於 100 MB
    -print: 把尋找的結果列印出來。這裡的列印並不是指印表機的列印,而是指輸出到Console。

    -exec: 把查找出來的結果,執行 (Execution)某個命令指令。如:
    [root@localhost]# 
    [root@localhost]# find ./ -name ".git" -exec rm -rf
    [root@localhost]# 
    
    不過,並不建議使用"-exec"參數;可以改使用管線 (Pipe)搭配"xargs"指令:
    [root@localhost]# 
    [root@localhost]# find ./ -name ".git" | xargs -r rm -rf
    [root@localhost]# 
    
    grep (Globally search for Regular Expression and print it)的基本指令用法:
    [root@localhost]# 
    [root@localhost]# grep "Hello!! World!! " ./ -r
    [root@localhost]# 
    
    -i: 關鍵字不分大小寫字母。

    -n: 找出來的資料要顯示行號。

    -v: 反向匹配。即沒有關鍵字的那幾行。

    -r: 遞迴搜尋。即該目錄下的所有檔案。

    --include="(fine name)": 可以指定特定的檔案。

    有些檔案是無法讀取的,強制執行時會發生錯誤訊息;可以在執行grep指令的最後加上:2> /dev/null

    -A (n): 找到該筆資料的前一行 (After)

    -B (n): 找到該筆資料的後一行 (Before)

    -C (n): 找到該筆資料的前後各一行 (Current)

    --color=never / always / auto: 用顏色來明顯地標示出該行資量的所在位置。

    以下是grep指令常搭配的正規表示法應用。

    "^a": 以'a'為開頭的字串。

    "b$": 以'b'為結尾的字串。

    "^[ab]": 以'a''b'為開頭的字串。

    "[ab]$": 以'a''b'為結尾的字串。

    "^ab": 以"ab"為開頭的字串。

    "ab$": 以"ab"為結尾的字串。

    "ab | cd": 含有"ab""cd"的字串。

    "\bnet\b": 精準地找出只有"net"的字串;而像"network"這種的就會被排除在外。

    10.09.2020

    不負責任的I2C心得!!

    這是自己之前在學習I2C時的一些鳥不拉雞的心得,因為一直都都把它們記在筆記本裡,想想乾脆也把它們整理一下放在BLOG上好了。

    I2C,標準的全名叫作「Inter-Integrated Circuit」,念法叫作「I-squared-C」;"squared"的意思是指「數學裡的平方」,因為它的文字表達方式:I2C─就像I的2次方

    I2C主要是應用在晶片 (Chipset)與晶片之間的溝通,它只需要2根訊號線:SDA (Serial Data Line)CKL (Clock Line),在業界算是非常常用的通訊協定;現在上網Google I2C的規格書,應該會找到2個版本,一個是Philips Semiconductors時期的AN10216-01;另一個則是NXP Semiconductors時期的UM10204

    I2C總共分為6種模式:

    0. 低速模式 (Low-speed mode, Ls-mode)10 KHz

    1. 標準模式 (Standard-mode, Sm)100 KHz

    2. 快速模式 (Fast-mode, Fm)400 KHz

    3. 高速模式 (High-speed mode, Hs-mode)3.4 MHz

    4. 快速模式PLUS (Fast Mode plue, Fm+)1 MHz

    5. 超快速模式 (Ultra Fast-mode, UFm)5 MHz

    其實當初兩份工作都有接觸到I2C,而且都是使用Fm的400 KHz,當時一直都搞不清楚這400 KHz是什麼意思 (因為自己本身並不是電子電機相關科系),而規格書上又有提到:
    快速模式 (Fm)下,SCL的時脈訊號在低準位有效期間 (LOW period of the SCL clock),「至少 (minimum)」要1.3 μs;而在高準位有效期間 (HIGH period of the SCL clock),則是要「至少 (min)」0.6 μs

    我當時就是因為搞不懂,所以才有了這乙份無聊的筆記。原來,400 KHz的意思是─400K bit per secondHz (赫茲)是指頻率的單位,即1秒鐘所發生的次數

    400 KHz即400,000 bps,1秒鐘可以傳送40萬bits的資料!換句話說,每傳送1 bit的資料,僅需0.0000025秒 (2.5 μs)

    規格書提到的SCL高低有效準位期間分別是「至少」1.3 μs和0.6 μs,即SCL的1個週期是1.9 μs;既然如此,我們為了方便運算,把高準位的有效期間提高至1.5 μs,把低準位的有效期間提高至1.0 μs。如此,1.5 μs + 1.0 μs = 2.5 μs,就剛好是Fm 400 KHz所要求的時脈了。

    那假如工程師在程式設計上非常地精準,可以控制到高低準位的「至少」要求1.3 μs和0.6 μs,那傳輸速率會大於400 KHz嗎?我想應該是不會的,因為規範就是如此!

    10.05.2020

    Linux的hexdump指令

    這一次要記錄的是Linux下一個非常實用的指令:hexdump。Dump的意思是指「頃倒拋棄」;但在電腦的相關領域則是翻譯成「轉儲」。但自己覺得若是翻譯成「解譯解析」其實也挺貼切的 (還有另一個單字也翻譯做"解譯"、"解析":Parser,這個單字更貼切的翻譯是─語法分析程序)。在Linux的指令工具中,有許多這種類似的指令:hexdumpobjdumptcpdumpzdump等等。

    hexdump指令工具,可以把檔案以16進制的方式給呈現出來。這個工具對一些*.ko, *.o, *.bin, *.hex的檔案分析非常有用。因為並不是所有的檔案都可以用cat指令完美的呈現,因為檔案之中若是有一些控制字元文字編輯軟體是無法正確地顯示。當然,為了展現這個指令工具的用法,會先編輯一個簡單的文字檔來操作:
    [root@localhost ~]# 
    [root@localhost ~]# cat -n ./hexdump.TXT
         1  ABCDEFGHIJKLMNOP
         2  QRSTUVWXYZ012345
         3  6789abcdefghijkl
         4  mnopqrstuvwxyz~!
         5  @#$%^&*()_+{}[]
    [root@localhost ~]# 
    
    首先,要看得懂這種格式表示法,最左上方的"0000000"是指開始的位置每一行是以16 Bytes做為單位來表示,所以到了第2行,就是原本的"0000000"再加上16,即16進位的"0000010"。以此類推。

    1. "-b":把所要呈現的資料以8進制來表示。'A'的16進制值是0x41,轉換成8進制就是101。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -b ./hexdump.TXT
    0000000 101 102 103 104 105 106 107 110 111 112 113 114 115 116 117 120
    0000010 012 121 122 123 124 125 126 127 130 131 132 060 061 062 063 064
    0000020 065 012 066 067 070 071 141 142 143 144 145 146 147 150 151 152
    0000030 153 154 012 155 156 157 160 161 162 163 164 165 166 167 170 171
    0000040 172 176 041 012 100 043 044 045 136 046 052 050 051 137 053 173
    0000050 175 133 135 012
    0000054
    [root@localhost ~]# 
    
    2. "-c":把每1個Byte,以ASCII碼來顯示。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -c ./hexdump.TXT
    0000000   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P
    0000010  \n   Q   R   S   T   U   V   W   X   Y   Z   0   1   2   3   4
    0000020   5  \n   6   7   8   9   a   b   c   d   e   f   g   h   i   j
    0000030   k   l  \n   m   n   o   p   q   r   s   t   u   v   w   x   y
    0000040   z   ~   !  \n   @   #   $   %   ^   &   *   (   )   _   +   {
    0000050   }   [   ]  \n
    0000054
    [root@localhost ~]# 
    
    3. "-C":把每1個Byte,除了以16進制表示之外,還同時顯示相對應的ASCII碼
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -C ./hexdump.TXT
    00000000  41 42 43 44 45 46 47 48  49 4a 4b 4c 4d 4e 4f 50  |ABCDEFGHIJKLMNOP|
    00000010  0a 51 52 53 54 55 56 57  58 59 5a 30 31 32 33 34  |.QRSTUVWXYZ01234|
    00000020  35 0a 36 37 38 39 61 62  63 64 65 66 67 68 69 6a  |5.6789abcdefghij|
    00000030  6b 6c 0a 6d 6e 6f 70 71  72 73 74 75 76 77 78 79  |kl.mnopqrstuvwxy|
    00000040  7a 7e 21 0a 40 23 24 25  5e 26 2a 28 29 5f 2b 7b  |z~!.@#$%^&*()_+{|
    00000050  7d 5b 5d 0a                                       |}[].|
    00000054
    [root@localhost ~]# 
    
    4. "-d":以每2個Bytes為單位,顯示出相對應的10進制值。'A'是0x41,'B'是0x42,所以合起來就是0x4241,轉換成10進制就是16961。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -d ./hexdump.TXT
    0000000   16961   17475   17989   18503   19017   19531   20045   20559
    0000010   20746   21330   21844   22358   22872   12378   12849   13363
    0000020   02613   14134   14648   25185   25699   26213   26727   27241
    0000030   27755   27914   28526   29040   29554   30068   30582   31096
    0000040   32378   02593   09024   09508   09822   10282   24361   31531
    0000050   23421   02653
    0000054
    [root@localhost ~]# 
    
    5. "-o":以每2個Bytes為單位,顯示出相對應的8進制值。10進制的16961,轉換成8進制就是041101 (固定6碼,不足補0)。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -o ./hexdump.TXT
    0000000  041101  042103  043105  044107  045111  046113  047115  050117
    0000010  050412  051522  052524  053526  054530  030132  031061  032063
    0000020  005065  033466  034470  061141  062143  063145  064147  065151
    0000030  066153  066412  067556  070560  071562  072564  073566  074570
    0000040  077172  005041  021500  022444  023136  024052  057451  075453
    0000050  055575  005135
    0000054
    [root@localhost ~]# 
    
    6. "-v":嗯....這個參數的用途....仔細看,它並不是以0x41, 0x42, 0x43, 0x44,而是以每2 Bytes為單位,後面的排在前面; 這種排列方示,是跟系統的位元組順序 (Byte Order)有關。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -v ./hexdump.TXT
    0000000 4241 4443 4645 4847 4a49 4c4b 4e4d 504f
    0000010 510a 5352 5554 5756 5958 305a 3231 3433
    0000020 0a35 3736 3938 6261 6463 6665 6867 6a69
    0000030 6c6b 6d0a 6f6e 7170 7372 7574 7776 7978
    0000040 7e7a 0a21 2340 2524 265e 282a 5f29 7b2b
    0000050 5b7d 0a5d
    0000054
    [root@localhost ~]# 
    
    7. "-x":嗯....此一參數的功能和"-v"非常類似....
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -x ./hexdump.TXT
    0000000    4241    4443    4645    4847    4a49    4c4b    4e4d    504f
    0000010    510a    5352    5554    5756    5958    305a    3231    3433
    0000020    0a35    3736    3938    6261    6463    6665    6867    6a69
    0000030    6c6b    6d0a    6f6e    7170    7372    7574    7776    7978
    0000040    7e7a    0a21    2340    2524    265e    282a    5f29    7b2b
    0000050    5b7d    0a5d
    0000054
    [root@localhost ~]# 
    
    8. "-n":只顯示前n個Bytes的資料。
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -C -n 24 ./hexdump.TXT
    00000000  41 42 43 44 45 46 47 48  49 4a 4b 4c 4d 4e 4f 50  |ABCDEFGHIJKLMNOP|
    00000010  0a 51 52 53 54 55 56 57                           |.QRSTUVW|
    00000018
    [root@localhost ~]# 
    
    9. "-s":從偏移量 (Offset)開始輸出;例如:我要從第8個Byte開始輸出,只輸出24個Bytes的資料:
    [root@localhost ~]# 
    [root@localhost ~]# hexdump -C -s 8 -n 24 ./hexdump.TXT
    00000008  49 4a 4b 4c 4d 4e 4f 50  0a 51 52 53 54 55 56 57  |IJKLMNOP.QRSTUVW|
    00000018  58 59 5a 30 31 32 33 34                           |XYZ01234|
    00000020
    [root@localhost ~]# 
    
    以上,是hexdump指令工具的基礎用法;而[-e format_string][-f format_file]是比較高階的用法,這一部份我還沒研究,不過上述的9個參數,應該就可以滿足大部份程式員工作上的需求。

    8.30.2020

    Linux的seq指令

    今天要記錄的Linux指令筆記是─seq。我只能說這一條指令不但簡單,而且他媽的好用。

    seq的功能就是─列印出你想要的數字區間,以及其中的等差級數

    例如,最基本的列印出0 ~ 15的數值:
    [root@localhost ~]# 
    [root@localhost ~]# seq 0 15
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@localhost ~]# 
    
    若有3個參數的時後,其中第2個參數會被當成等差級數;若只有2個參數,就只有首尾二數,等差級數的預設值為1。例如:
    [root@localhost ~]# 
    [root@localhost ~]# seq 0 3 15
    0
    3
    6
    9
    12
    15
    [root@localhost ~]# 
    
    加上"-f / --format"參數,則可以讓輸出結果類似C語言當中的printf(); 函式:
    [root@localhost ~]# 
    [root@localhost ~]# seq -f "%g" 0 3 15
    0
    3
    6
    9
    12
    15
    [root@localhost ~]# 
    
    上面的寫法,有加和沒加的結果是一樣的!若無使用"-f / --format"參數,這就是預設的表示法。而接下來的範例則是展現了最少顯示3位的寫法
    [root@localhost ~]# 
    [root@localhost ~]# seq --format="%3g" 0 3 15
      0
      3
      6
      9
     12
     15
    [root@localhost ~]# 
    
    最少顯示4位,且靠左對齊:
    [root@localhost ~]# 
    [root@localhost ~]# seq --format="%-4g" 0 3 15
    0   
    3   
    6   
    9   
    12  
    15  
    [root@localhost ~]# 
    
    上的例子很爛,因為看不出來到底有沒有4位數。那換一個好了:最少顯示4位,不足4位者前面補0
    [root@localhost ~]# 
    [root@localhost ~]# seq --format="%04g" 0 3 15
    0000
    0003
    0006
    0009
    0012
    0015
    [root@localhost ~]# 
    
    參數"-w / --equal-width",設定字寬。此一功能無法和"-f / --format"共用。以下的範例因為後面的數值都是2位數,所以前面的數值雖然只有個位數,它會自動補零 ("-f / --format"比較靈活)。
    [root@localhost ~]# 
    [root@localhost ~]# seq -w 0 3 15
    00
    03
    06
    09
    12
    15
    [root@localhost ~]# 
    [root@localhost ~]# seq --equal-width 0 3 15
    00
    03
    06
    09
    12
    15
    [root@localhost ~]# 
    
    "-s / --separator"參數則是可以定義每乙個數值之間的分隔符號 (Separator);若無此一參數,預設值是'\r',即回車 (CR, Carrier Return, 0x0D)
    [root@localhost ~]# 
    [root@localhost ~]# seq -s ";" 0 3 15
    0;3;6;9;12;15
    [root@localhost ~]# 
    [root@localhost ~]# seq --separator=", " 0 3 15
    0, 3, 6, 9, 12, 15
    [root@localhost ~]# 
    

    8.18.2020

    Linux的wpa_passphrase指令

    Errr....這乙次自己要記錄的心得筆記是Linux下的wpa_passphrase指令。其實這一個指令是和Wireless (無線網路)有關,指令很簡單,但它背後所牽扯到的相關Know-How錯綜複雜,但在這兒也沒打算贅述 (寫下去就沒完沒了了)。在Linux下,wpa_*開頭的相關指令除了wpa_passphrase之外,還有wpa_cliwpa_supplicant

    WPA指的是無線網路的加密技術,總共計有WEP (Wired Equivalent Privacy,有線等效加密協定)WPA (Wi-Fi Protected Access,無線保護存取)WPA2WPA3等等;WPA只不過是個通稱而已。而wpa_passphrase指令的功能也很簡單,就是把ESSID (Extended Service Set Identifier)PSK (Pre-Shared Key,預先共享金鑰) 2個東西一起做一個加密的動作 (或是行為)。用法如下:
      wpa_passphrase "(Your ESSID)" (Your PSK)
    範例如下:
    [root@localhost ~]# 
    [root@localhost ~]# wpa_passphrase "PICASSO_1234" 0987654321
    network={
            ssid="PICASSO_1234"
            #psk="0987654321"
            psk=17b53e93e75d3199be569fedc6d18ad54e9682d5df4f37325870cad6a4111529
    }
    [root@localhost ~]# 
    
    "PICASSO_1234"就是我們的ESSID,而0987654321就是PSK。指令輸出的結果,有註解符號 (#)的那一 行,就是PSK未加密過的明碼;而下面那一行落落長長的PSK才是被加密過的!假如我們只需要已加密過的PSK,則可 善用管線 (Pipe)把我們所需的資料過濾出來:
    [root@localhost ~]# 
    [root@localhost ~]# wpa_passphrase "PICASSO_1234" 0987654321 | grep "psk" | grep -v "#" | cut -d '=' -f 2
    17b53e93e75d3199be569fedc6d18ad54e9682d5df4f37325870cad6a4111529
    [root@localhost ~]# 
    

    8.16.2020

    DNS Server List

  • Google Public DNS
    • 8.8.8.8
    • 8.8.4.4

  • CloudFlare
    • 1.1.1.1
    • 1.0.0.1

  • OpenDNS (CISCO provider)
    • 208.67.220.123
    • 208.67.220.220
    • 208.67.222.123
    • 208.67.222.222

  • Norton DNS
    • 198.153.192.1
    • 198.153.194.1

  • Comodo Secure DNS
    • 8.26.56.26
    • 8.20.247.20
    • 156.154.70.22
    • 156.154.71.22

  • Quad9 DNS
    • 9.9.9.9
    • 149.112.112.112

  • 中華電信研究院 (HiNet)
    • 168.95.1.1
    • 168.95.192.1
    • 168.95.192.2

  • 財團法人網路資訊中心TWNIC
    • 192.83.166.11
    • 192.83.166.11

  • Quad 101 (TWNIC provider)
    • 101.101.101.101
    • 101.102.103.104

  • 遠傳電信FET (SeedNet)
    • 139.175.1.1
    • 139.175.55.244

  • 台灣碩網 (So-Net)
    • 61.64.127.1
    • 61.64.127.2

  • 台灣固網TFN
    • 211.78.215.137
    • 211.78.215.200

  • 和信超媒體 (GigaMedia)
    • 203.133.1.6
    • 203.133.1.8

  • 亞太電信APOL
    • 203.79.224.10
    • 203.79.224.30

  • 8.11.2020

    NTP Server List

  • 中華電信國家時間與頻率標準實驗室
    • time.stdtime.gov.tw
    • clock.stdtime.gov.tw
    • watch.stdtime.gov.tw
    • tick.stdtime.gov.tw
    • tock.stdtime.gov.tw

  • FACEBOOK
    • time1.facebook.com
    • time2.facebook.com
    • time3.facebook.com
    • time4.facebook.com
    • time5.facebook.com

  • APPLE
    • time.asia.apple.com (Asia)
    • time.apple.com (America)
    • time.euro.apple.com (Europe)

  • CloudFlare
    • time.cloudflare.com

  • Google
    • time.google.com
    • time1.google.com
    • time2.google.com
    • time3.google.com
    • time4.google.com

  • NTP Pool Project (for Taiwan, R. O. C. )
    • 0.tw.pool.ntp.org
    • 1.tw.pool.ntp.org
    • 2.tw.pool.ntp.org
    • 3.tw.pool.ntp.org

  • OpenWRT Project
    • 0.openwrt.pool.ntp.org
    • 1.openwrt.pool.ntp.org
    • 2.openwrt.pool.ntp.org
    • 3.openwrt.pool.ntp.org

  • ubuntu Project
    • 0.ubuntu.pool.ntp.org
    • 1.ubuntu.pool.ntp.org
    • 2.ubuntu.pool.ntp.org
    • 3.ubuntu.pool.ntp.org

  • Refer: Setup NTP Client and Server

    11.08.2018

    Linux C, fstat(), stat(), lstat()

    這一次要做的筆記是fstat()stat()lstat()這3個函式的差異和比較。fstat()在之前的文章就已經記錄過了,不過當時並沒有注意到還有stat()和lstat()這2個類似的函式。

    跟這3個函式相關的,有1個很重要的struct stat結構,如下:
    #include <bits/stat.h>
    
    struct stat {
      dev_t st_dev;
      // ID of device containing file (unsigned long int)
      ino_t st_ino;          // inode 數量 (unsigned long int)
      mode_t st_mode;        // 保護 (unsigned int)
      nlink_t st_nlink;      // hard links 數量 (unsigned int)
      uid_t st_uid;          // user ID 擁有者 (unsigned int)
      gid_t st_gid;          // group ID 擁有者 (unsigned int)
      dev_t st_rdev;         // device ID (unsigned long int)
      off_t st_size;         // 以byte為單位,計算大小 (long int)
      blksize_t st_blksize;  // 系統I/O 區塊大小 (long int)
      blkcnt_t st_blocks;    // 區塊取得數量 (long int)
      time_t st_atime;       // 最後一次存取時間
      time_t st_mtime;       // 最後一次修改時間
      time_t st_ctime;       // 狀態修改時間
    };
    
    #include <sys/stat.h>
    
    int fstat(int filedes, struct stat *buf);
    
    int stat(const char *path, struct stat *buf);
    
    int lstat(const char *path, struct stat *buf);
    
    這乙些資料型態的大小都不是絕對的,有些還有32-bit/64-bit的差異,可以參考/usr/include/bits/types.h/usr/include/bits/typesizes.h的定義。

    這3個函式執行成功會回傳0;執行失敗則是回傳-1。範例程式如下:
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <time.h>
    #include <stdlib.h>
    #include <strings.h>
    
    #define FILENAME    "./TEXT.TXT"
    #define STAT_FLAG   0 // 0: fstat; 1: stat; 2: lstat
    
    int main(int argc, char **argv, char *envp[])
    {
      struct stat stStatBuf;
      int fd = -1, nRet = -1;
    
      bzero((void *)&stStatBuf, sizeof(struct stat));
    #if (STAT_FLAG == 0)
      fd = open(FILENAME, O_RDONLY);
      if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
      }
    
      nRet = fstat(fd, &stStatBuf);
      if (nRet == -1) {
        perror("fstat");
        exit(EXIT_FAILURE);
      }
    #elif (STAT_FLAG == 1)
      nRet = stat(FILENAME, &stStatBuf);
      if (nRet == -1) {
        perror("stat");
        exit(EXIT_FAILURE);
      }
    #elif (STAT_FLAG == 2)
      nRet = lstat(FILENAME, &stStatBuf);
      if (nRet == -1) {
        perror("lstat");
        exit(EXIT_FAILURE);
      }
    #endif
      printf("st_dev = %u \n", stStatBuf.st_dev);
      printf("st_ino = %u \n", stStatBuf.st_ino);
      printf("st_mode = %u \n", stStatBuf.st_mode);
      printf("st_nlink = %u \n", stStatBuf.st_nlink);
      printf("st_uid = %u \n", stStatBuf.st_uid);
      printf("st_gid = %u \n", stStatBuf.st_gid);
      printf("st_rdev = %d \n", stStatBuf.st_rdev);
      printf("st_size = %ld \n", stStatBuf.st_size);
      printf("st_blksize = %ld \n", stStatBuf.st_blksize);
      printf("st_blocks = %ld \n", stStatBuf.st_blocks);
      printf("st_atime= %s", ctime(&stStatBuf.st_atime));
      printf("st_mtime = %s", ctime(&stStatBuf.st_mtime));
      printf("st_ctime = %s", ctime(&stStatBuf.st_ctime));
    #if (STAT_FLAG == 0)
      close(fd);
    #endif
      return 0;
    }
    
    這3個函式的差異:

    1. fstat()函式的第1個參數所傳進去的是一個檔案描述子 (File Descriptor),所以必須先用open()函式來開啟檔案產生描述子。而不能用fopen()來產生檔案指標 (FILE *)。所以在程式運作上若是沒有read(), write()或是seek()等行為,大可不需要使用fstat()函式。

    2. stat()和lstat()的運作完全一樣,差別在於若是檔案來源是一個軟連結 (Symbolic Link)的話,lstat()函式只會帶出該軟連結的檔案資訊,並不會指到原始檔案;而stat()函式則是會直接帶出原始檔案的資訊。

    參考文章:Linux的fstat()函式

    10.17.2018

    Linux C, IPCs, Semaphore, Share Memery and Message Queue

    好久沒有記錄一下學習心得了!這一次來記一下Linux系統中的IPC (Inter-Process Communication,內部行程通訊)

    在一個作業系統中有許許多多的行程,程序彼此之間要如何溝通?於是就有了IPC的概念。IPC的方法教課書上有3種:Semaphore (號誌)Share Memory (共享記憶體)Message Queue (訊息佇列)。至於Sample Code (程式碼範例)這裡就不寫了,因為參考書上都有,把書上的東西抄過來並沒有什麼意義。這裡只記下一些簡單的學習心得。

    1). Semaphore的相關函式為:semctl(); (Semaphore Control), semget(); (Semaphore Get)semop(); (Semaphore Operation)

    2). Share Memory的相關函式為:shmget(); (Share Memory Get), shmat(); (Share Memory Attach), shmdt(); (Share Memory Delete), shmctl(); (Share Memory Control)

    3). Message Queue的相關函式為:msgctl(); (Message Queue Control), msgget(); (Message Queue Get), msgrcv(); (Message Queue Receive), msgsnd(); (Message Queue Send)

    4). 我們也可以使用一些Linux Command Line來得知這些IPCs的訊息或是刪除它們:ipcs -s; ipcs -m; ipcs -q;
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -s
    
    ------ Semaphore Arrays --------
    key                semid            owner            perms            nsems
    
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -m
                                                                  
    ------ Shared Memory Segments --------
    key        shmid   owner   perms  bytes   nattch   status
    0x00000000 131072  root    600    393216   2       dest
    0x00000000 163841  root    600    393216   2       dest
    0x00000000 65538   gdm     600    393216   2       dest
    0x00000000 196611  root    600    393216   2       dest
    0x00000000 229380  root    600    393216   2       dest
    0x00000000 262149  root    600    393216   2       dest
    0x00000000 294918  root    600    393216   2       dest
    0x00000000 327687  root    600    393216   2       dest
    0x00000000 360456  root    600    393216   2       dest
    0x00000000 393225  root    600    393216   2       dest
    0x00000000 425994  root    600    393216   2       dest
    0x00000000 458763  root    600    393216   2       dest
    0x00000000 491532  root    600    393216   2       dest
    0x00000000 524301  root    600    393216   2       dest
    0x00000000 557070  root    600    393216   2       dest
    0x00000000 589839  root    600    393216   2       dest
    0x00000000 622608  root    600    393216   2       dest
    0x00000000 786451  root    600    393216   2       dest
    0x00000000 851989  root    600    393216   2       dest
    
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -q
    
    ------ Message Queues --------
    key              msqid          owner          perms          used-bytes    messages 
    
    [root@localhost ~]# 
    [root@localhost ~]# 
    
    或者是,我們執行:cat -n /proc/sysvipc/sem, shm, msg; 也可以查詢到更詳盡的資訊:


    如果我們想要手動刪除IPCs的話,指令則是:
    ipcsrm -s < sem_id> ; ipcsrm -m < shm_id> ; ipcsrm -q < msg_id> ;

    5). 這3種IPCs有何差異 (或是優缺點)?

    Semaphore (號誌)
    a). 相同的程式要執行2次以上時,可以解決Race Condition (競走現象)的問題。
    b). 有提供同步化的保護機制。

    Share Memory (共享記憶體)
    a). 效率較好,因為是直接對記憶體做存取。
    b). 應用於不同的程式 (Process)之間存取相同的記憶體。
    c). 缺點為並沒有同步化的機制,需由開發者自行設計。
    d). 要避免兩支以上的程序 (Process)同時存取Share Memory的競走現象,可和Semaphore搭配做出保護機制。

    Message Queue (訊息佇列)
    a). 兩支程式之間可以各自執行,不需同時執行。
    b). 承a). 所以並沒有同步化的問題。
    c). 缺點每乙則訊息的大小及佇列數量的上限,均有其系統上的限制。

    關於IPCs,系統又提供了那些核心參數可供參考?它們的意義為何?

    6). Semaphore (號誌)
    a). cat -n /proc/sys/kernel/sem
    b). sysctl -a | grep --color "kernel.sem"
    c). ipcs -s -l
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/sem 
            1    250  32000      32    128
    [root@localhost ~]# 
    [root@localhost ~]# sysctl -a | grep --color "kernel.sem"
    kernel.sem = 250        32000      32    128
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -s -l
    
    ------ Semaphore Limits --------
    max number of arrays = 128
    max semaphores per array = 250
    max semaphores system wide = 32000
    max ops per semop call = 32
    semaphore max value = 32767
    
    [root@localhost ~]# 
    [root@localhost ~]#
    
    這些指令的執行結果都是一樣地,在/usr/include/linux/sem.h裡都可以找到相對應的值;那這些值代表什意思?

    SEMMSL (250): 每一個號誌集合 (陣列)的最大號誌數量 (The maximum semaphores per semaphore set.)

    SEMMNS (32000): 在系統範圍限制上所有號誌集合的總數 (A system-wide limit on the number of semaphores in all semaphore sets.)

    SEMOPM (32): 每一個號誌發生時最大的系統操作數量 (The maximum number of operations that may be specified in a semop(2) call.)

    SEMMNI (128): 在系統範圍限制上最大的號誌集合總數 (A system-wide limit on the maximum number of semaphore identifiers.)

    7). Share Memory (共享記憶體)
    a). cat -n /proc/sys/kernel/shmall, shmmax, shmmni
    b). sysctl -a | grep --color "kernel.shm"
    c). ipcs -m -l
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/shmall 
            1    2097152
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/shmmax 
            1    33554432
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/shmmni
            1    4096
    [root@localhost ~]# 
    [root@localhost ~]# sysctl -a | grep --color "kernel.shm"
    kernel.shmmax = 33554432
    kernel.shmall = 2097152
    kernel.shmmni = 4096
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -m -l
    
    ------ Shared Memory Limits --------
    max number of segments = 4096
    max seg size (kbytes) = 32768
    max total shared memory (kbytes) = 8388608
    min seg size (bytes) = 1
    
    [root@localhost ~]# 
    
    這些指令的執行結果都是一樣地,在/usr/include/linux/shm.h裡都可以找到相對應的值;那這些值代表什意思?

    SHMALL (2097152): 系統範圍限制上的共享記憶體區塊的大小 (This file contains the system-wide limit on the total number of pages of System V shared memory.)

    SHMMAX (33554432): 共享記憶體的最大單位 (bytes) (This file can be used to query and set the run-time limit on the maximum (System V IPC) shared memory segment size that can be created. Shared memory segments up to 1GB are now supported in the kernel. This value defaults to SHMMAX.) (PS: 33,554,432 = 0x02000000)。

    SHMMNI (4096): 共享記憶體的最大分割數量 (This file specifies the system-wide maximum number of System V shared memory segments that can be created.)

    SHMMIN (1): 共享記憶體的最小單位 (bytes)。

    8). Message Queue (訊息佇列)
    a). cat -n /proc/sys/kernel/msgmax, msgmnb, msgmni
    b). sysctl -a | grep --color "kernel.msg"
    c). ipcs -q -l
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/msgmax
            1      8192
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/msgmnb
            1      16384
    [root@localhost ~]# 
    [root@localhost ~]# cat -n /proc/sys/kernel/msgmni
            1      1700
    [root@localhost ~]# 
    [root@localhost ~]# sysctl -a | grep --color "kernel.msg"
    kernel.msgmax = 8192
    kernel.msgmni = 1700
    kernel.msgmnb = 16384
    [root@localhost ~]# 
    [root@localhost ~]# ipcs -q -l
    
    ------ Messages: Limits --------
    max queues system wide = 1700
    max size of message (bytes) = 8192
    default max size of queue (bytes) = 16384
    
    [root@localhost ~]# 
    
    這些指令的執行結果都是一樣地,在/usr/include/linux/msg.h裡都可以找到相對應的值;那這些值代表什意思?

    MSGMAX (8192): 每乙則訊息 (message)的大小 (byte)。

    MSGMNI (1700): 在系統範圍限制上的最大佇列 (queue)數目。

    MSGMNB (16384): 在系統上所有佇列 (queue)上的訊息 (message)的最大值。

    好!就先記到這兒!

    12.15.2017

    getopt_long()測試方法-B

    基於上一篇getopt_long()測試方法-A的文章,自己是建立在使用者的角度,來分析這樣的一個函式應該要如何設計。

    雖然沒有辦法做到很完美,例如像"ps aux""rpm -ivh"這種指令的選項解譯,但是還是有符合大部份Linux系統下的指令需求。

    getopt_long()函式所傳進去的第4個引數const struct option *longopts,它是一個option的結構陣列,option結構的第4個成員int val; 的用法,和第1個成員const char *name; 相呼應。換句話說,就是當使用者輸入長選項"--help"時,就等同於短選項的"-H"

    問題來了,option結構的第2個成員int has_arg; 是決定這個長選項是否要帶參數。而getopt_long()函式所傳進去的第3個引數const char *optstring; 是定義短選項的字元,若字元後面有接冒號 (:)就代表這個短選項後面需要接參數;沒有接冒號就代表不需要接參數。

    Question: 「如果第3個引數const char *optstring; 和第4個引數const struct option *longopts的設定『發生衝突』,會發生什麼事?

    長選項的option結構的第2個成員int has_arg; 有3種設定方式:沒有參數 (no_argument)必有參數 (required_argument)可有參數 (optional_argument)短選項就只有2種設定方式:沒有參數 ()必有參數 (:)

    所以,如此又多了3 * 2 = 6種測試方式!我們先來看最理想的狀況:

    Step VIII: 長選項設定為沒有參數 (no_argument)短選項也設定為沒有參數 ()
    57. ./main --help
    58. ./main --help 1234
    59. ./main -H
    60. ./main -H 1234
    [root@localhost]# 
    [root@localhost]# ./main --help
    You got a short option argv[1]: "-H"
    
    [root@localhost]# 
    [root@localhost]# ./main --help 1234 
    You got a short option argv[1]: "-H", but DOESN'T need argument argv[2]: 1234 
    
    [root@localhost]# 
    [root@localhost]# ./main -H
    You got a short option argv[1]: "-H"
    
    [root@localhost]# 
    [root@localhost]# ./main -H 1234 
    You got a short option argv[1]: "-H", but DOESN'T need argument argv[2]: 1234 
    
    [root@localhost]# 
    [root@localhost]# 
    
    這一部份的測試結果,若長選項"--help"有找到相對應的短選項"-H",執行結果會以短選項為優先。

    Step IX: 長選項設定為必有參數 (required_argument)短選項也設定為必有參數 (:)
    61. ./main --delete
    62. ./main --delete 1234
    63. ./main -D
    64. ./main -D 1234
    [root@localhost]# 
    [root@localhost]# ./main --delete 
    ./main: option '--delete' requires an argument
    The long option argv[1]: "--delete" need an argument!! 
    
    [root@localhost]# 
    [root@localhost]# ./main --delete 1234 
    You got a short option argv[1]: "-D" with an argument: argv[2]: "1234" 
    
    [root@localhost]# 
    [root@localhost]# ./main -D 
    ./main: option requires an argument -- 'D'
    The short option argv[1]: "-D" need an argument!! 
    
    [root@localhost]# 
    [root@localhost]# ./main -D 1234 
    You got a short option argv[1]: "-D" with an argument: argv[2]: "1234" 
    
    [root@localhost]# 
    [root@localhost]# 
    
    不論是長選項或是短選項,應帶參數而未帶參數,getopt_long()函式就直接回傳問號'?'字元;若有帶參數,就直接對應到短選項的設定。

    Step X: 長選項設定為沒有參數 (no_argument)短選項設定為必有參數 (:)
    65. ./main --count
    66. ./main --count 1234
    67. ./main -c
    68. ./main -c 1234
    [root@localhost]# 
    [root@localhost]# ./main --count 
    程式記憶體區段錯誤 (core dumped)
    [root@localhost]# 
    [root@localhost]# ./main --count 1234 
    程式記憶體區段錯誤 (core dumped)
    [root@localhost]# 
    [root@localhost]# ./main -c 
    ./main: option requires an argument -- 'c'
    The short option argv[1]: "-c" need an argument!! 
    
    [root@localhost]# 
    [root@localhost]# ./main -c 1234 
    You got a short option argv[1]: "-c" with an argument: argv[2]: "1234" 
    
    [root@localhost]# 
    [root@localhost]# 
    
    當執行長選項時,就會發生「程式記憶體區段錯誤 (core dumped)」的錯誤訊息;若執行短選項時,則是正常執行。

    Step XI: 長選項設定為必有參數 (required_argument)短選項設定為沒有參數 ()
    69. ./main --create
    70. ./main --create 1234
    71. ./main -C
    72. ./main -C 1234
    [root@localhost]# 
    [root@localhost]# ./main --create
    ./main: option '--create' requires an argument
    The long option argv[1]: "--create" need an argument!! 
    
    [root@localhost]# 
    [root@localhost]# ./main --create 1234 
    You got a short option argv[2]: "-C"
    
    [root@localhost]# 
    [root@localhost]# ./main -C 
    You got a short option argv[1]: "-C"
    
    [root@localhost]# 
    [root@localhost]# ./main -C 1234 
    You got a short option argv[1]: "-C", but DOESN'T need argument argv[2]: 1234 
    
    [root@localhost]# 
    [root@localhost]# 
    
    這個執行結果就有意思了!

    a). 當長選項設定為必有參數而未輸入參數時,getopt_long()函式會回傳0,因為它有找到相對應的長選項;

    b). 當長選項設定為必有參數而輸入參數時,getopt_long()函式會回傳相對應的短選項字元;但短選項的字元設定為沒有參數,所以就不會抓取輸入長選項時所帶進來的參數。

    c). 當短選項設定為沒有參數時,完全不影響長選項的設定。

    Step XII: 長選項設定為可有參數 (optional_argument)短選項設定為沒有參數 ()
    73. ./main --interface
    74. ./main --interface 1234
    75. ./main -I
    76. ./main -I 1234
    [root@localhost]# 
    [root@localhost]# ./main --interface 
    You got a short option argv[1]: "-I"
    
    [root@localhost]# 
    [root@localhost]# ./main --interface 1234 
    You got a short option argv[1]: "-I", but DOESN'T need argument argv[2]: 1234 
    
    [root@localhost]# 
    [root@localhost]# ./main -I 
    You got a short option argv[1]: "-I"
    
    [root@localhost]# 
    [root@localhost]# ./main -I 1234 
    You got a short option argv[1]: "-I", but DOESN'T need argument argv[2]: 1234 
    
    [root@localhost]# 
    [root@localhost]# 
    
    輸入短選項時,並沒有什麼問題;但輸入長選項時,則是直接對應到短選項。因為短選項設定為沒有參數,所以不管長選項有無帶參數,都不會抓取。

    Step XIII: 長選項設定為可有參數 (optional_argument)短選項設定為必有參數 (:)
    73. ./main --list
    74. ./main --list 1234
    75. ./main -L
    76. ./main -L 1234
    [root@localhost]# 
    [root@localhost]# ./main --list 
    程式記憶體區段錯誤 (core dumped)
    [root@localhost]# 
    [root@localhost]# ./main --list 1234 
    程式記憶體區段錯誤 (core dumped)
    [root@localhost]# 
    [root@localhost]# ./main -L 
    ./main: option requires an argument -- 'L'
    The short option argv[1]: "-L" need an argument!! 
    
    [root@localhost]# 
    [root@localhost]# ./main -L 1234 
    You got a short option argv[1]: "-L" with an argument: argv[2]: "1234" 
    
    [root@localhost]# 
    [root@localhost]# 
    
    因為短選項設定為必有參數,雖然長選項設定為可有參數,但是當短選項對應到長選項時,依然會發生「程式記憶體區段錯誤 (core dumped)」的問題。

    綜合以上Step VIII ~ XIII的實驗結果可得知:

    a). 長選項如果有相對應的短選項時,短選項的設定優先權會大於長選項

    b). 例如:當短選項設定為沒有參數 ()時,無論長選項是設定為必有參數 (required_argument)或是可有參數 (optional_argument)時,短選項都不會去抓取長選項的參數。

    c). 反之,當短選項設定為必有參數 (:)時,無論長選項是設定為沒有參數 (no_argument)或是可有參數 (optional_argument)而沒帶參數時,都會發生「程式記憶體區段錯誤 (core dumped)」的問題。

    Answer: 簡而言之,當長選項和短選項的參數設定發生衝突時,getopt_long()函式並沒有解決這樣的問題;反而是程式設計師們應自行注意,不應讓這樣的衝突發生。

    在一開始的範例程式碼最後,還多加了3個函式:pid_t writePID(const char *PATH); pid_t readPID(const char *PATH); 和int killPID(const char *PATH); 這3個函式也是在Linux Programming下常看到的。當我們執行一支常註程式時,通常都會記下自己在系統中的PID (Process ID),所以會有一個writePID()函式;倘若有別支程序需要送一個訊號 (Signal)和這一支程式溝通時,就需要用readPID()函式;當我們的常註程式要結束時,也要記得刪除自己的PID,就用killPID()函式。

    相關文章:Linux C getopt_long()

    12.13.2017

    getopt_long()測試方法-A

    在上一篇文章當中─Linux C getopt_long(),只有初步地提到為何需要這樣的函式?這個函式是如何地宣告?然後再加上自己寫的一個簡單範例。

    在程式碼的撰寫過程當中,不可能一開始就寫得很複雜,一定是從簡單地判斷開始去做,發現有問題,就慢慢的一步一步堆疊上去。

    1. 長選項,和短選項,先分開來測;
    2. 每一個選項,我都挑2個選項來測;所以會有2種組合;
    3. 因為在實務上,不太可能只接1個選項,可能是2個選項、3個選項,所以測試時,就以1個選項2個選項來做為測試方法,所以也會有2種組合;
    4. 因為選項後面,又可以再區分為有帶參數沒帶參數,也是2種組合。
    5. 綜合2~5點,我們會有2 * 2 * 2 = 8種組合。為了方便起見,所有選項後面的參數,就用12345678來表示。

    getopt_long()函式所傳進去的第4個引數const struct option *longopts,是一個option的結構陣列;option結構的第2個成員int has_arg; 是決定這一個長選項是否要帶參數。從這一個差異點來著手,自己的測試命令列指令是:

    Step I: 長選項的has_arg為沒有參數 (0: no_argument)時:
    01. ./main --author
    02. ./main --force
    03. ./main --author 1234
    04. ./main --force 5678
    05. ./main --author --force
    06. ./main --author --force 5678
    07. ./main --author 1234 --force
    08. ./main --author 1234 --force 5678

    Step II: 長選項的has_arg為必有參數 (1: required_argument)時:
    09. ./main --add
    10. ./main --append
    11. ./main --add 1234
    12. ./main --append 5678
    13. ./main --add --append
    14. ./main --add --append 5678
    15. ./main --add 1234 --append
    16. ./main --add 1234 --append 5678

    Step III: 長選項的has_arg為可有參數 (2: optional_argument)時:
    17. ./main --all
    18. ./main --file
    19. ./main --all 1234
    20. ./main --file 5678
    21. ./main --all --file
    22. ./main --all --file 5678
    23. ./main --all 1234 --file
    24. ./main --all 1234 --file 5678

    長選項的部份完成之後,再來看短選項的部份,也就是getopt_long()的第3個引數const char *optstring

    在這個引數當中,若選項後面沒有接冒號表沒有參數;反之則為必有參數。

    Step IV: 短選項沒有參數 ()時:
    25. ./main -X
    26. ./main -Y
    27. ./main -X 1234
    28. ./main -Y 5678
    29. ./main -X -Y
    30. ./main -X -Y 5678
    31. ./main -X 1234 -Y
    32. ./main -X 1234 -Y 5678

    Step V: 短選項必有參數 (:)時:
    33. ./main -x
    34. ./main -y
    35. ./main -x 1234
    36. ./main -y 5678
    37. ./main -x -y
    38. ./main -x -y 5678
    39. ./main -x 1234 -y
    40. ./main -x 1234 -y 5678

    以上的5大項,都是在理想化的狀態之下做的實驗和測試;倘若使用者輸入不存在的選項呢?是否應該也要有相對應的防呆機制?

    Step VI: 輸入一個不存在的長選項時:
    41. ./main --ask
    42. ./main --oops
    43. ./main --ask 1234
    44. ./main --oops 5678
    45. ./main --ask --oops
    46. ./main --ask --oops 5678
    47. ./main --ask 1234 --oops
    48. ./main --ask 1234 --oops 5678

    Step VII: 輸入一個不存在的短選項時:
    49. ./main -Q
    50. ./main -r
    51. ./main -Q 1234
    52. ./main -r 5678
    53. ./main -Q -r
    54. ./main -Q -r 5678
    55. ./main -Q 1234 -r
    56. ./main -Q 1234 -r 5678

    從Step I ~ VII的測試項目當中,不難發現:

    a). getopt_long()函式在判斷命令列參數時,是以每一個參數是否為"--"或是'-'開頭。

    b). 若為"--"開頭,表示長選項;有找到相對應的長選項,getopt_long()會回傳0。

    c). 若為'-'開頭,表示短選項;有找到相對應的短選項,getopt_long()會回傳該字元。

    d). 若連續2個選項寫在一起,如:"--author --force", "--author -Y", "-X --force", "-X -Y"等等,getopt_long()函式並不會判斷這是2個不同的選項;而是會判斷成"--force"是"--author"的參數。所以這一部份必須由程式設計師們去做一個防呆的判斷。

    e). 無論長短選項,選項後面需要接上參數,而沒有接時,getopt_long()會回傳'?'字元。

    f). 若是輸入不存在選項時,getopt_long()也會回傳'?'字元。

    g). 綜合e). 和f). ,至於是何種原因導致getopt_long()回傳'?'字元,這也是由程式設計師來判斷。

    理解以上a). ~ g). 的getopt_long()設計原則之後,我們就可以執行一個較為複雜的指令。就會得到一個很漂亮,完全符合我們預期的結果!
    [root@localhost ~]# 
    [root@localhost ~]# ./main --author --force 1234 --add 5678 --append --all 9012 --file -X 3456 -Y -x -y 7890 --ask 1234 --oops 5678 -Q 9012 -r 3456
    You got a long option argv[1]: "--author". 
    
    You got a long option argv[2]: "--force", but DOESN'T need argument argv[3]: "1234". 
    
    You got a long option argv[4]: "--add" with an argument: argv[5]: "5678" 
    
    The long option argv[6]: "--append" need an argument!! 
    
    You got a long option argv[7]: "--all" with an argument: argv[8]: "9012". 
    
    You got a long option argv[9]: "--file". 
    
    You got a short option argv[10]: "-X", but DOESN'T need argument argv[11]: 3456 
    
    You got a short option argv[12]: "-Y". 
    
    The short option argv[13]: "-x" need an argument!! 
    
    You got a short option argv[14]: "-y" with an argument: argv[15]: "7890" 
    
    ./main: unrecognized option '--ask'
    Unknown long option argv[16]: --ask 
    // usage()函式內容省略!
    ./main: unrecognized option '--oops'
    Unknown long option argv[18]: --oops 
    // usage()函式內容省略!
    ./main: invalid option -- 'Q'
    Unknown short option argv[20]: -Q 
    // usage()函式內容省略!
    ./main: invalid option -- 'r'
    Unknown short option argv[22]: -r 
    // usage()函式內容省略!
    [root@localhost ~]# 
    [root@localhost ~]# 
    
    (To be continue .... )

    12.06.2017

    Linux C getopt_long()

    這一次,自己想要記錄下來的學習心得,是Linux C中的getopt_long()函式。為什麼會有這樣的一個函式?

    當我們在輸入一個Linux Shell Script的指令時,例如ICMP (Internet Control Message Protocol,網路控制訊息協定)的ping指令時:

    [root@localhost]# ping -I eth0 -c 4 www.google.com.tw -v
    
    以上這乙行的指令和後面的參數是代表什麼意思?

    -I」的'I'指的是Interface (介面);后面接了一個參數「eth0」─我要使用的是eth0這個網絡卡的介面;

    -c」的'c'指的是count (次數);後面也接了一個參數'4',表示要執行4次;

    www.google.com.tw指的就是我要執行ping的網址;

    最後的「-v」指的是view (檢視),我要把執行的結果給呈現出來。

    類似這種在執行時需要帶參數的指令非常的多,所以C99有一個getopt()這樣的一個函式,來解決程式設計師們在設計軟體上時的問題。

    還有另外一種指令的格式是像這個樣子的:
    [root@localhost]# ps lx | grep "bash" --color
    
    --color」指的就是我們要將grep出來的結果特別用顏色給標示出來。

    像"--color"這種長選項,C99也提供了一個getopt_long()這樣的一個函式;但是─getopt_long()也可以向下相容於getopt()函式!

    或許有人會覺得:我也可以善用主程式當中的int argcchar *argv[]來做處理呀!當然也是可以,只不過要在程式碼中做很多的防呆判斷機制,比較麻煩。

    所以,就是這一篇文章的重點─我想要記錄一個getopt_long()函式的範例,爾後在工作上就可以直接地Copy-Paste了!先來看一下這個函式的基本介紹:

    功能說明:分析命令列參數。

    標頭檔:#include <getopt.h>

    函式宣告:int getopt_long(int argc, const char *argv[], const char *optstring, const struct option *longopts, int *longindex);

    函式說明:
    1. int argc; argc就是執行程序時所帶的參數個數 (Argument Count)。例如指令:
       ./a.out; argc就為1;
       ./a.out -c -d; argc就為3;
       ./a.out -a -b 1234; argc就為4。

    2. const char *argv[]; argv則是指執行程序時帶的參數變數 (Argument Variable)。例如指令:
       ./a.out; argv[0]為"./a.out";
       ./a.out -c -d; argv[0]為"./a.out",argv[1]為"-c",argv[2]為"-d";
       ./a.out -a -b 1234; argv[0]為"./a.out",argv[1]為"-a",argv[2]為"-b",argv[3]為"1234"。

    3. const char *optstring; 代表欲處理的短選項字串。getopt_long()函式會回傳argv中下一個選項的字母。例如:"abc:d:"代表程序後面可以接上"-a", "-b", "-c"和"-d"等參數;'c'和'd'後面有冒號 (:)表這個短選項後面必須接參數值。所以,完整的指令應該像是這樣:
    [root@localhost]# ./a.out -a -b -c 123456 -d "Hello!! World!! "
    
    4. const struct option *longopts; 引數longopts是一個結構指標,指向定義好、分析用的命令列參數資料。此一結構定義在:/usr/include/getopt.h (OS: Fedora Core 13)

    結構內容如下:

    struct option {
        const char *name;
        int has_arg;
        int *flag;
        int val;
    };

    a). const char *name; 命令列參數的長選項名稱;
    b). int has_arg; 可有3個數值,其值與意義如下:
        0: 參數後面沒有參數內容 (no_argument)
        1: 參數後面必有參數內容 (required_argument)
        2: 參數後面可有參數內容 (optional_argument)
    c). int *flag; 指向一個整數指標,如果此指標非NULL值,則會將函式傳回值存到指標指的位置。
    d). int val; 函式的傳回值。

    5. int *longindex; getopt_long()函式,在longopts結構陣列中,所尋找到的Index位置。

    除此之外,getopt_long()函式還有用到一些全域變數 (Globle Variable)

    6. extern char *optarg; 在getopt_long()函式之中的第3個引數const char *optstring,指的是所要搜尋的短選項;若短選項後面還有接上冒號 (:),則代表還必須接上一個參數值,而全域變數optarg就是指到這一個參數值。

    7. extern int optind; 全域變數optind會指向在命令列當中第一個非'-'或是"--"開頭的參數。

    範例:https://drive.google.com/open?id=1KxuBUXRs7_DLwxVKqZ4c71KY6uWD8X5Q

    因為範例的程式碼有點大且複雜,所以我把它放在Google Driver上!

    原本以為getopt_long()這個函式的範例應該還蠻簡單的,但沒想到卻足足花了我3、4天的時間在研究這個函式。getopt_long()就某種程度上而言,並沒想像中的那麼聰明,還是有一些地方必須靠程式設計師來自行判斷。例如,當我們在檢視程序 (Process)時的指令:
    [root@localhost]# ps aux
    
    它等同於:
    [root@localhost]# ps -a -u -x
    
    或者是當我們在安裝RPM (RedHat Package Management)管理套件時的指令:
    [root@localhost]# rpm -ivh
    
    也等同於:
    [root@localhost]# rpm -i -v -h
    
    以上兩種用法,getopt_long()函式都沒有幫我們判斷,要由程式設計師們自行另外增加判斷。所以,自己所撰寫的程式碼當中,也沒有以上兩種方式的判斷。

    至於getopt_long()函式是如何測試的,測試時又發生了那些狀況?我想另外再寫兩篇測試的方法和心得。

    (To be continue .... )

    3.17.2017

    檔案鎖機制

    這一次,自己想要記錄下來的學習心得,是Linux系統下的檔案鎖的機制。什麼是檔案鎖呢?

    我們先假設有一個檔案叫Resource.TXT,它會被兩個程序 (Process 0, Process 1)去存取。可是,如果當這兩個行程,同時要寫資料到Resource.TXT這個檔案,那麼一定會有問題,會發生Race Condition (競走現象)。為了要解決這樣的問題,我們可以先用一個很笨的方法:

    1. 當Process 0要去存取Resource.TXT時,先建立一個檔案鎖 (Lock File)
    2. 而Process 1要去存取Resource.TXT時,先去判斷檔案鎖是否存在,如果存在,就表示有其它的行程正在存取當中,所以程序先行等待;
    3. 當Process 0存取結束時,就把檔案鎖給刪除;
    4. 而Process 1再去確認檔案鎖是否存在,若不存在,表示沒有行程在存取,換Process 1自行存取。

    很簡單吧!其實,這種避免許多行程去存取某一個資源的競走現象,在作業系統的觀念中還蠻重要的;在更深入的Linux Kernel Space也會用到。

    所以,基於以上4點,用幾行簡單的程式碼,具體實踐。
    #include "proc.h"
    
    #define SECONDS   30
    
    int main(void)
    {
        FILE *fp = (FILE *)NULL;
        char *pStr0 = (char *)NULL;
    
        creat(LOCK_FILE, S_IROTH);
        fp = fopen(RESOURCE_FILE, "r");
        if (fp == (FILE *)NULL) {
            fprintf(stderr, "Open the Resource File: %s ERROR!! \n", RESOURCE_FILE);
            exit(EXIT_FAILURE);
        }
    
        pStr0 = (char *)calloc(sizeof(char), 16);
        fread((void *)pStr0, sizeof(char), 16, fp);
        fprintf(stdout, "pStr0: %s", pStr0);
        fprintf(stdout, "Waiting for %d seconds.... \n", SECONDS);
        sleep(SECONDS);
        fprintf(stdout, "Done!! \n");
        free((void *)pStr0);
        fclose(fp);
        remove(LOCK_FILE);
    
        return 0;
    }
    
    首先,我們在Process 0程式一開始執行時,就先用creat()函式來建立一個檔案鎖 (/var/tmp/Resource.lck),

    接著,我們開始用fopen(), fread(), fprintf()等函式來讀取Resource.TXT檔案,並且將之顯示出來;

    再來,為了讓Process 1有時間去存取和驗證,用sleep()函式讓程序等待30秒之後再結束,

    最後,在程序結束前,要記得把檔案鎖給刪除。

    再看Process 1的程式碼:
    #include "proc.h"
    
    #define SECONDS   3
    
    int main(void)
    {
        struct stat stTemp;
        int fd = 1, nRet = 1;
        FILE *fp = (FILE *)NULL;
        char *pStr1 = (char *)NULL;
    
        bzero((void *)&stTemp, sizeof(struct stat));
    
        do {
            fd = open(LOCK_FILE, O_RDONLY);
            nRet = fstat(fd, &stTemp);
            fprintf(stdout, "Warning: The file was LOCKed!! Please wait.... \n");
            sleep(SECONDS);
        } while (nRet == 0);
     close(fd);
    
        pStr1 = (char *)malloc(sizeof(char) * 16);
        fp = fopen(RESOURCE_FILE, "r");
        if (fp == (FILE *)NULL){
            fprintf(stderr, "Open the Resource File: %s ERROR!! \n", RESOURCE_FILE);
            exit(EXIT_FAILURE);
        }
    
        pStr1 = (char *)malloc(sizeof(char) * 16);
        fread((void *)pStr1, sizeof(char), 16, fp);
        fprintf(stdout, "pStr1: %s", pStr1);
        fclose(fp);
        free((void *)pStr1);
    
        return 0;
    }
    
    程序一開始就先判斷檔案鎖是否存在,這裡判斷的方法是,用低階I/O的open()函式開啟檔案之後,用fstat()函式去判斷檔案鎖是否存在;

    因為fstat()函式執行成功會回傳0,所以在while-loop當中,fstat()一直回傳0就表示檔案鎖存在,我們就顯示一小段警告訊息,並且用sleep()函式等待3秒鐘,直到檔案鎖被Process 0給移除之後,才會跳出迴圈。

    跳出回圈之後,就可以讀取Resource.TXT了,把內容讀出來。

    再把其它的相關檔案程式碼補齊:
    proc.h:
    #ifndef __PROC_H__
    #define __PROC_H__
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    #include <stdlib.h>
    
    #define RESOURCE_FILE    "./Resource.TXT"
    #define LOCK_FILE        "/var/tmp/Resource.lck"
    
    #endif
    
    Makefile:
    GCC := gcc
    CFLAGS := -o
    OBJS := proc0.o proc1.o
    EXEC := proc0.out proc1.out
    RM := rm -rf
    
    .PHONY: all
    all: $(EXEC)
    
    $(EXEC): $(OBJS)
        @-$(GCC) $(CFLAGS) proc0.out proc0.o
        @-$(GCC) $(CFLAGS) proc1.out proc1.o
    
    $(OBJS): %.o: %.c
        @-$(GCC) -c $< $(CFLAGS) $@
    
    .PHONY: clean cleanall install print tar dist TAGS check test
    clean:
        @-$(RM) *.o
    
    cleanall: clean
        @-$(RM) $(EXEC)
    
    install:
    
    print:
    
    tar:
    
    dist:
    
    TAGS:
    
    check: 
    
    test:
    
    
    Resource.TXT:
    Hello!! World!!
    
    
    執行結果如下圖所示:


    這樣的作法,相當的笨拙,且不夠聰明。好在Linux的User Space下還提供了另外一種更好的方式─fcntl()函式 (File Control);這一篇文章只不過是個引言,接下就會記錄如何使用fcntl()函式來避免競走現象。

    3.15.2017

    MSB, LSB, 位元運算

    原本這一篇想要記下來的C語言中「位元運算」;可是,我印象中好像有寫過類似心得的記錄,剛剛又翻了BLOG中"Linux"和"C & C++"的標籤,發現還真的沒寫過!再翻自己的筆記本,居然是寫在筆記本上面?因為自己有個習慣,工作上的一些心得或是技巧,都會先記在私人的筆記本上面,等到有空時再整理到BLOG。既然我沒有整理過的話,那就順便把更早之前記下的MSB和LSB的觀念也記一下。

    MSB (the Most Significant Byte)LSB (the Least Significant Byte)中的"Significant",中文解釋為「有意義的、具有權重的、意味深長的」;而前面再加上個副詞"Most / Least"來修飾後面的Significant,就變成「最有意義、具有權重的位元組」或是「最沒意義、舉無輕重的位元組」。

    那麼何謂「最有意義、最沒意義的位元組」。這裡以一組印度阿拉伯數字─12,345為例,中文念成「一萬兩千三百四十五」,把萬位數的'1'刪除,就會變成2,345─兩千三百四十五,相差了10,000,所以'1'就是MSB;而把個位數的'5'刪除,就會變成12,340─一萬兩千三百四十,相差了5,所以'5'就是LSB

    要如何知道自己所應用的CPU (Chipset)是屬於MSB還是LSB?可以寫一個類似下面的程式碼來驗證一下:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int 
    main(void)
    {
        unsigned char ucTemp[4];
        int a = 0xDEADBEEF;
    
        memset(ucTemp, 0x00, sizeof(unsigned char));
        memcpy(ucTemp, &a, sizeof(int));
        printf("%02X %02X %02X %02X \n", ucTemp[0], ucTemp[1], \
            ucTemp[2], ucTemp[3]);
    
        return 0;
    }
    
    如果程序碼的執行結果是「DE AD BE EF」,表MSB;若為「EF BE AD DE」,則是LSB。

    咦?不對呀!16進制的0xDEADBEEF,等於10進制的3,735,928,559;「DE」就是MSB,「EF」就是LSB。那為何會有兩種不一樣的結果?因為這又牽扯到Big EndianLittle Endian的問題。

    Big Endian就是「記憶體位址中,較"低"的位址,存放MSB」,ucTemp[0]的記憶體位址,必定要比ucTemp[3]來的低;ucTemp[0]存放MSB (DE),ucTemp[3]存放LSB (EF),,這是Big Endian的概念。

    Little Endian就是「記憶體位址中,較"高"的位址,存放MSB」,ucTemp[3]的記憶體位址,必定要比ucTemp[0]來的高;ucTemp[3]存放MSB (DE),ucTemp[0]存放LSB (EF),這是Little Endian的觀念。

    寫得文謅謅的,好像很難懂?不懂沒關係,觀念通了就行了。由此可見,Big Endian / MSB,比較符合人類的思考習慣和視覺閱讀上的方式。

    簡而言之,Big / Little Endian和MSB / LSB的想法就先記到這,爾后的Driver Programming (驅動程式)Socket Programming (網路程式)都還會再用到。接下來,要記錄的是「位元運算」的一些技巧。為了方便講解,先宣告一個值:char C = 0xA5; 16進制的0xA5,等於10進制的165,等於2進制的「1010 0101」。我們把取出來的值,儲存在char cRet = 0x00; 至於應用的部份,請看以下的範例會比較快一些:
    #include <stdio.h>
    
    int 
    main(void)
    {
        char C = 0xA5, cRet = 0x00, X;
    
        cRet = (C >> X) & 0x01; // 取第X位元的值
        cRet = C & (0x01 << X); // 取第X位元的值
    
        cRet = C & 0x0F;        // 取最後4位元的值
        cRet = C & 0xF0;        // 取最前4位元的值
    
        C &= ~(0x01 << X);      // 把C的第X位元設為0
        C &= ~0x0F;             // 把C的最後4個位元設為0
        C &= ~0xF0;             // 把C的最前4個位元設為0
        // 把C的中間4個位元 (2, 3, 4, 5 Bit)均設成0
        C &= ~0x3C;
    
        C |= (1 << X);          // 把C的第X位元設為1
        C |= 0x0F;              // 把C的最後4個位元設為1
        C |= 0xF0;              // 把C的最前4個位元設為1
        // 把C的中間4個位元 (2, 3, 4, 5 Bit)均設成1
        C |= 0x3C;
    
        // 把C的中間4個位元 (2, 3, 4, 5 Bit)設成1010
        C &= ~0x3C;             // 先把中間4個位元都設成0
        C |= (0x0A << 2);       /* 把0x0A (0000 1010),往左移2個位
                                   元,變成0010 1000,再做OR運算 */
    
        // 把C的第X位元做Toggle (0變成1, 1變成0)
        C ^= (1 << X);
    
        return 0;
    }
    

    3.02.2017

    Linux DBM DataBase

    這一次,自己要記錄下來的學習心得是Linux系統下的DBM (Data Base Management)資料庫。就自己的工作經驗上,這個DBM資料庫根本就沒用過,不過也就是因為沒用過,所以才想要刻意地記錄下來。

    DBM資料庫,比較像是一個非常簡單的「索引式檔案儲存系統」,它並沒有像微軟的MS-SQL Server或是Linux平臺下的MySQL這麼的完善,或是功能強大。

    先來瞭解一下,跟DBM資料庫有相關的函式有那些:

    1. DBM *dbm_open(const char *file, int open_flags, mode_t file_mode);
    dbm_open()函式和dbm_close()這2個函式是一對的,就好像fopen()-fclose()open()-close()一樣。dbm_open()函式會傳進3個參數:
    第1個參數file,指的是資料庫的路徑。dbm_open()會在路徑下開啟2個檔案:(FileName).dir和(FileName).pag。
    第2個參數open_flags和第3個參數file_mode的用法,等同於open()函式,在此不再贅述。
    若dbm_open()函式開啟資料庫成功,會回傳一個指到DBM資料結構的指標;反之則是回傳(DBM *)NULL。

    2. int dbm_store(DBM *db, datum key, datum content, int store_mode);
    dbm_store()函式的主要功能就是儲存一筆資料
    第1個參數db,就是我們在之前dbm_open()所回傳的指標。
    第2個參數key,鑰匙!它的概念就是假設有一筆資料,有10個欄位,我把第3個欄位的值設成key,換句話說,我必須透過這第3個欄位的key值才能夠找到這一筆資料。
    第3個參數content,就是指整筆資料的內容。
    第4個參數store_mode,只有2種用法:DBM_INSERTDBM_REPLACE
    當要儲存一筆資料時,這一筆資料的key值跟之前儲存過的資料有重覆,且store_mode設定成DBM_REPLACE,那麼,這一筆新的資料就會取代掉原本舊的資料。
    當要儲存一筆資料時,這一筆資料的key值跟之前儲存過的資料有重覆,但store_mode卻設成DBM_INSERT,那麼,舊有的資料保持不變,而新的一筆資料會被忽略。
    當要儲存一筆資料時,這一筆資料的key值跟之前儲存過的資料完全沒有重覆,無論store_mode設定成DBM_INSERT或DBM_REPLACE,這一筆資料都會被插入資料庫。
    dbm_store()執行成功時,會回傳0,執行失敗時會回傳一個負數;假如第3個參數設成DBM_INSERT且dbm_store()有找到一筆已存在的資料,就會回傳1。

    3. datum dbm_fetch(DBM *db, datum key);
    dbm_fetch()函式的功能就是從資料庫中讀取一筆資料
    第1個參數db,就是我們在之前dbm_open()所回傳的指標。
    第2個參數key,就是當初我們儲存這一筆資料時,所設定的key值;現在dbm_fetch()透過這一個key值來找回該筆資料。
    若dbm_fetch()執行成功有找到該筆資料,則回傳該筆資料,而該筆資料儲存在datum結構當中;執行失敗時,datum結構中的char *dptr成員則是被設成(char *)NULL

    4. int dbm_delete(DBM *db, datum key);
    dbm_delete()的用法跟dbm_fetch()類似,傳進去的2個參數也一樣;dbm_fetch()是讀取一筆資料,dbm_delete()則是刪除一筆資料
    函式執行成功回傳0,執行失敗則是回傳一個負數。

    5. datum dbm_firstkey(DBM *db);
    6. datum dbm_nextkey(DBM *db);
    這2個函式必須搭配使用,它們會掃描資料庫中所有的key值。它們的用法必須如下所示:
    for (key = dbm_firstkey(db_ptr); key.dptr != (char *)NULL; key = dbm_nextkey(db_ptr)) { /* .... */ }
    
    若dbm_firstkey()和dbm_nextkey()函式執行成功,則回傳該筆資料,且該筆資料儲存在datum結構中;執行失敗時,datum結構中的char *dptr成員則是被設成(char *)NULL。

    7. int dbm_error(DBM *db);
    dbm_error()函式會檢查一個資料庫是否有錯誤,沒有錯誤就會回傳0,反之則回傳一個非0的值。

    8. int dbm_clearerr(DBM *db);
    dbm_clearerr()會清除任何錯誤狀態的旗標,這是設定在資料庫中。而它的回傳值則沒有被指定。

    9. void dbm_close(DBM *db);
    請參閱dbm_open()函式。

    基於以上這些DBM相關的函式,自己寫了一個很簡單的範例。不過,因為程序碼有點大,不太方便貼在部落格,所以我把它放在Google Drive,如下:https://drive.google.com/open?id=0B4hMTvUWgd_gRlNTSExueksyVnc

    假如在編譯時沒有過,有可能是缺少了DBM的函式庫,自己用的版本是:gdbm-1.11-7.fc24.i686.rpmgdbm-devel-1.11-7.fc24.i686.rpm,把這兩個套件安裝起來即可!
    [root@localhost]# rpm -ivh gdbm-1.11-7.fc24.i686.rpm
    [root@localhost]# rpm -ivh gdbm-devel-1.11-7.fc24.i686.rpm
    

    6.03.2016

    RE (Regular Expression), "awk" command

    在上一篇的筆記之中,記下了可以搭配RE的Linux指令"sed"的用法─在不修改原始檔案的狀況下,執行動態編輯。

    這一次要記的是另一個指令:"awk"!這個工具善於處理已整理過、格式化的資料,如ps aux、/etc/passwd等等。它的基本格式如下:awk '樣式命令{動作}' "檔案"

    a) awk預設的欄位分隔符號是空白 (Space)或是TAB鍵

    b) $1, $2, $3代表該行的第1欄位、第2欄位、第3欄位;$0則是代表整行資料

    c) awk的內建變數:
    NF:每一行 ($0)所擁有的欄位總數。
    NR:目前是處理第幾行的資料。
    FS:目前的分隔字元,預設值是空白 (Space)鍵。

    [root@localhost ~] awk "/root/" /etc/passwd
    [root@localhost ~] awk '/root/' /etc/passwd
    
    在檔案中尋找含有"root"字串的行。

    [root@localhost ~] awk '{print $1,$4}' /proc/net/arp
    
    僅顯示檔案中每一行的第1欄位和第4欄位。

    [root@localhost ~] awk '/eth/{print $2,$10}' /proc/net/dev
    
    在檔案中僅顯示含有"eth"字串的那一行的第2欄位和第10欄位。

    [root@localhost ~] awk -F ':' '/root/{print $6,$7}' /etc/passwd
    
    在檔案中尋找含有"root"字串的行,且欄位分隔符號為':',並秀出第6欄位和第7欄位。

    [root@localhost ~] awk -F ':' 'BEGIN{OFS="+++"}/root/{print $6,$7}' /etc/passwd
    [root@localhost ~] awk -F ':' '/root/{print $6 "+++" $7}' /etc/passwd
    
    輸出結果同上,但把輸出結果的欄位分隔符號變為"+++"。

    [root@localhost ~] awk -F ':' '/eth.|ppp.|wlan./{print $1}' /proc/net/dev
    
    在檔案中,尋找含有"eth."、或"ppp."、或"wlan."字串的行,且欄位分隔符號為':',僅顯示第1欄位。
    此一指令的功能在尋找網路設備的名稱。

    [root@localhost ~] awk -F ':' '/root/{print NR "," NF}' /etc/passwd
    
    在/etc/passwd檔案中,欄位分隔符號為':';尋找含有"root"字串的行,秀出「含有"root"的字串是在第幾行」和該行有幾個欄位,分隔符號為','。

    [root@localhost ~] awk '{FS=":"}$3<10{print $0}' /etc/passwd
    
    在檔案中,欄位的分隔符號為':',第3欄位的值小於10,把整行秀出來!

    6.02.2016

    RE (Regular Expression), "sed" command

    Regular Expression (RE, 正規表示法)是在Linux環境之下粉重要的一個工具,剛開始會覺得很難學,不易上手;但學會了之後,就會覺得很好用!

    但自己偏偏就是屬於前者─不較不用心的那一種人,所以就乾脆把學過的指令和用法記錄下來....

    a) 一個'.'就是代表一個字元;若要代表'.',則是在前面多加一個跳脫字元'\',變成'\.'

    b) ^Picasso:代表Picasso出現在行首
    c) Picasso$:代表Picasso出現在行尾

    d)
    [A-Z]:一個大寫字母;
    [a-z]:一個小寫字母;
    [0-9]:一個數字;
    [^A-Z]:一個非大寫字母;
    [^a-zA-Z]:一個非字母;
    [^a-z0-9A-Z]:一個非數字、字母。

    e) aA*C:在'a'和'C'之間,字元'A'出現0或0個以上。

    f) [a-z]\{3,5\}:以3~5個小寫字母所組成的字串。

    會跟RE搭配的指令之一就是"sed"指令。sed指令工具的特色,就是可以在不修改原始檔案的狀況下,執行動態編輯。基本格式:sed "樣式命令" "檔案"

    [root@localhost ~] sed "2,25d" /etc/passwd
    
    把第2~25行刪除,其餘顯示。

    [root@localhost ~] sed "/root/d" /etc/passwd
    
    搜尋含有"root"的字串,並把它刪除 (Delete)

    [root@localhost ~] sed "/[0-9]\{3\}/d" /etc/passwd
    
    把含有3位數的字串刪除。

    [root@localhost ~] sed "/^$/d" /etc/passwd
    
    把空白列刪除。

    [root@localhost ~] sed '/root/!d' /etc/passwd
    
    搜尋含有"root"的字串,不要刪除。等同於:把不含有"root"的字串刪除。

    [root@localhost ~] sed -n "/root/p" /etc/passwd
    [root@localhost ~] sed '/root/!d' /etc/passwd
    
    把含有"root"的字串列印 (Print)出來。

    [root@localhost ~] sed -n "s/root/picasso/pg" /etc/passwd
    
    在/etc/passwd檔案中,搜尋含有"root"的字串,並全部 (Globle)取代為picasso,且列印出來。

    [root@localhost ~] sed -n "s/root//pg" /etc/passwd
    
    在檔案中,搜尋含有"root"的字串,並全部取代為空字串,且列印出來。

    [root@localhost ~] sed -n "s/^...//p" /etc/passwd
    
    把檔案中每一行的前3個字元刪除,並列印出來。

    [root@localhost ~] sed -n "s/...$//p" /etc/passwd
    
    把檔案中每一行的末3個字元刪除,並列印出來。

    [root@localhost ~] set -n "s/\(root\)/\1picasso/pg" /etc/passwd
    
    在檔案中尋找"root"字串,並儲存成檔案\1,再取代成rootpicasso,且列印出來。

    [root@localhost ~] sed -n '/root/s/bin/PPG/pg' /etc/passwd
    [root@localhost ~] sed -n "/root/s/bin/PPG/pg" /etc/passwd
    
    在檔案中先尋找含有"root"字串的行數;在該行中再尋找含有"bin"的字串,並全部 (Globle)取代為"PPG",且列印 (Print)出來!

    [root@localhost ~] sed -n "/root/,/operator/s/bin/fuck/pg" /etc/passwd
    
    在檔案中尋找有"root"字串開使出現的行數,一直到有"operator"字串的行數為止;在這之中再尋找"bin"字串,並全部取代為"fuck",且列印出來!

    [root@localhost ~] sed -n "12,24s/bin/dick/pg" /etc/passwd
    
    在檔案中從12行到24行,尋找"bin"字串,並全部取代為"dick"且列印出來!

    ◎ 補充 (28th Jul, 2020)

    前面的用法都是直接把sed指令拿來用;假如我要把某個Shell Script的執行結果,用sed來執行取代或是替換呢?
    例如:我要把MAC Address: 00:14:02:03:09:60,把這中間的冒號 (:)給取消掉,那我可以這樣子來呈現:
    [root@localhost ~]# 
    [root@localhost ~]# ifconfig eth0
    eth0      Link encap:Ethernet  HWaddr 00:14:02:03:09:60
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:8513 errors:0 dropped:13 overruns:0 frame:0
              TX packets:221 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:2048
              RX bytes:1021746 (997.7 KiB)  TX bytes:32296 (31.5 KiB)
    
    [root@localhost ~]# 
    [root@localhost ~]# ifconfig eth0 | grep "HWaddr" | awk '{print $5}' | sed 's/://g'
    001402030960
    [root@localhost ~]# 
    

    第2個例子,我要把時區 (Time Zone)裡面一些有加上底線 (UnderLine)的城市,因為正常的情況下,沒有人會寫成"New_York",所以我要把底線給取消掉,置換成空白 (Space)字元:
    [root@localhost ~]# 
    [root@localhost ~]# ls -l /usr/share/zoneinfo/America/ | grep "New_York"
    -rw-r--r--    1 root     root          3545 Jul  9 06:29 New_York
    [root@localhost ~]# 
    [root@localhost ~]# ls -l /usr/share/zoneinfo/America/ | grep "New_York" | awk '{print $9}' | sed 's/_/\ /g'
    New York
    [root@localhost ~]#