CY's Blog

All work is preparing yourself for the accident waiting to happen.

前言

在開發嵌入式系統的時候,很常遇到需要在資源緊張的環境上進行開發,所謂的資源緊張大概不外乎memory不夠使用、flash不夠大,但是老闆或PM仍然希望RD在產品上面新增feature,這時候就只能針對code size進行優化了。我自己待的部門剛好就是遇到這種產品已經維護10年以上,可是又希望加新feature的狀況,因此開始尋找減少code size的方法,這邊分享一些我自己的心得。

Compile Optimization

首先我們可以看一下compiler是不是已經做過優化了,大家都知道gcc在編譯的時候可以選擇optimization的level,從0-3。0代表的是default,而隨著數字越高,對code size和execution time的優化就越高。

大部分的人都會建議使用-O2,在code size和execution time取平衡,但是如果真的對code size十分在意的話,其實也可以使用-Os,代表的是-O2但是不包含部分會影響code size的優化。

到底每個optimization的level是做了那些優化,可參考GCC的官方文件

strip

strip算是最基本的降低code size工具,他會移除debug資訊(可供gdb使用的資訊)以及symbol table,因此size會降低許多。

這邊簡單做個實驗:

  • 我們先寫個簡單程式:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
    void func() {
    printf("func\n");
    }

    int main() {
    func();
    return 0;
    }
  • 接著來編譯,為了凸顯strip的效果,我們加上-g來加上gdb debug訊息
    1
    gcc -g test.c -o test
  • 接著我們可以用nm -a test來看到他的symbol table
    1
    2
    3
    4
    5
    0000000000000000 a
    0000000000201030 b .bss
    0000000000201030 B __bss_start
    0000000000000000 n .comment
    ....
  • 以及用objdump -h test來看到有哪些section header,可以發現有許多debug資訊
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ...
    27 .debug_aranges 00000030 0000000000000000 0000000000000000 0000105d 2**0
    CONTENTS, READONLY, DEBUGGING
    28 .debug_info 0000033a 0000000000000000 0000000000000000 0000108d 2**0
    CONTENTS, READONLY, DEBUGGING
    29 .debug_abbrev 000000f6 0000000000000000 0000000000000000 000013c7 2**0
    CONTENTS, READONLY, DEBUGGING
    30 .debug_line 000000d4 0000000000000000 0000000000000000 000014bd 2**0
    CONTENTS, READONLY, DEBUGGING
    31 .debug_str 0000028a 0000000000000000 0000000000000000 00001591 2**0
    CONTENTS, READONLY, DEBUGGING
    ...
  • 接著執行strip test後,會發現symbol table已經消失了(無法使用nm),以及沒有debug的section header。兩者size有極大差異。
    1
    2
    3
    4
    5
    > ls -al
    -rwxrwxrwx 1 evshary evshary 11152 Aug 4 11:34 test
    > strip test
    > ls -al
    -rwxrwxrwx 1 evshary evshary 6304 Aug 4 11:39 test

objcopy

strip可以減少極大部分的code size,但是如果這樣還不夠的話,我們可以用objcopy把一些沒用到的section header移除掉,但是要提醒一下,這個移除幾乎不會影響太大,大概就幾百byte而已。

1
2
3
4
> objcopy -R .comment -R .note.ABI-tag -R .gnu.version test small_test
-> ls -al
-rwxrwxrwx 1 evshary evshary 6304 Aug 4 11:39 test
-rwxrwxrwx 1 evshary evshary 6024 Aug 4 11:45 small_test

這邊所謂的沒用到section header主要是一些環境的版本資訊,到底這些header代表什麼意思,可以參考Linux Standard Base PDA Specification 3.0RC1 - Chapter 5. Special Sections

利用 compile option 來移除沒用到的 symbol

我們知道程式裡面常常會有些程式碼(function/data)並沒有被人使用到,不論是因為長久maintain被修修改改,還是因為本身就有預留給未來使用。但是這些沒用到的功能如果都被編進去程式中其實是很浪費的,我們這邊可以用一些小手段來移除。

在gcc的編譯過程中我們可以加上特別的編譯參數-fdata-sections-ffunction-sections,這兩個的意思是把每個symbol(function或data)獨立成不同的section。為什麼要這樣做呢?當然是為了後面在link的時候我們可以直接移除沒用到的section,在link的時候多加上--gc-sections參數即可。

細節可以參考How to remove unused C/C++ symbols with GCC and ld?

觀察 map file

map file是我們在編譯過程中很重要的一個工具,他可以用來檢視目前symbol的size有多大,我們可以用nm來取得symbol table,甚至根據symbol的size大小來排序(指令是nm --size-sort -r -S [執行檔])。透過觀察map file,我們可以瞭解程式內部每個功能佔的大小為何,進一步思考有沒有優化的空間,甚至發現該功能根本是沒有在使用的。

我自己也曾經有遇過code size的問題,那時候我一樣是用nm來讀取map file,忽然發現某個變數大到不可思議,觀察了一下發現那個變數是直接用global的方式宣告,並不是要用的時候才malloc,導致在一般firmware運作的過程中那塊記憶體完全沒辦法被使用。更重要的是那個功能並不常被使用,而且還會隨著硬體平台有不一樣的大小,結果RD為了方便,直接保留可能會用到的最大值,造成空間的極度浪費。

Remove debug message

其實RD在開發的過程中,或多或少都會留一些debug訊息,雖然少少的,但是累積起來量也是很驚人,畢竟一個debug訊息就是一個字串。在code size緊張的情況下,應該可以審視一下,看能不能把debug訊息移除。

值得注意的是有些embedded的firmware確實是會有關閉debug資訊的方式,但是這個有可能只是不顯示(例如關閉console顯示),並不是真的移除,要仔細確認自己的狀況是哪種。

不過如果真的到了一定要移除debug訊息程式才能夠被使用的情況,這樣也挺危險的了,因為未來如果要maintain,必要的debug訊息還是逃不了。我會建議程式開發的過程中每個功能都可以自行決定要不要把debug的程式碼編進去,至少遇到bug還可以只開啟相關功能的debug訊息,而不是全部訊息都全開。

移除沒用到的功能(library)、檔案

在我們的embedded firmware裡面有些會需要使用SSL或SSH這種非常龐大的library,可能佔firmware的size超過1/3。像是這種library其實有很多功能是我們沒有用到的,以SSL、SSH來說,其實我們只會用到其中少部分的加密cipher,而不是全部。如果真要使用,建議要對library本身功能機制足夠熟悉,在編譯的時候只開用到的option即可。

除了library外,一個產品經過長時間的maintain,中間一定會有許多功能是後來沒用到,卻沒被移除的。如果只是程式碼倒還好,可以用前面提到的gc-section來排除,但是如果是file system的檔案,那就要靠自己來處理了。我個人的經驗是,有很多功能是過去產品有的,但是因為後來時代不符合被移除,結果相關檔案就都一直遺留下來,例如可在browser上面運作的java plugin等等,這些的size是也很可觀的。

Compression

壓縮也是減少code size的其中一個方法,除了啟動的程式外,我們可以把runtime過程才要load的東西進行壓縮。通常這類的角色可以是kernel啟動完成後另外加載的AP,或是filesystem。不過壓縮要考慮的點就是壓縮率、解壓的程式碼的大小以及速度,最好可以在這其中之間取得平衡。壓縮率對我們來說就是可以把程式縮小到什麼地步,如果縮小不大就沒有意義了,然後解壓的部分也很重要,要是有很高壓縮率,但是解壓程式很大,那整體來說並沒有得到多高的效益。而如果壓縮率高,但解壓速度過慢,也會影響到使用者體驗,這些都需要考慮到。

filesystem的部分有點可以稍微注意一下,大部分的應用都是web居多,而web其實是有壓縮的空間,且不需另外解壓的。我們知道一般web都是由html、CSS、javascript所組成,而這些內容丟給browser的時候並不需要是人眼比較好閱讀的方式,例如說不需要換行、縮排等等。這麼一來我們就有可以動手腳的空間,可以在編譯過程中,把原始的檔案做壓縮,最後才變成file system,這樣的壓縮率是很可觀的。除了減少size外,這還帶來另外一個很大的好處就是減少網路流量的傳輸,特別在embedded system中系統效能其實都不快。提醒一下,記得開發過程使用git追蹤的web檔案最好是原始檔案(人眼好讀的),編譯過程才壓縮,不然這只是給自己帶來開發的困擾而已。

web壓縮的方式網路上有很多,有些甚至提供online的服務,例如HTMLCompressor或是textfixer等等,可以自己尋找適合的工具。

結語

上面分享了許多方法,但最後我要先澄清一下,自己需要搞清楚到底不夠的是flash還是memory,上面的方法並不是做了兩個都一定會減少。舉個例子來說,移除沒有必要用到的大變數通常只會影響memory的使用率,因為compile出來firmware的size並沒有包括大變數(因為是bss section,未初始化區段),而file system的壓縮通常也只會影響flash的使用率,除非firmware有把檔案預先從flash讀出來放在memory中。我想強調的是使用這些方法時,還是要有必備的系統觀以及對你的系統有一定熟悉程度。

老實說軟體開發者最討厭的大概就是被各種硬體條件所限制,然而這些在embedded的世界中還是有很大的機會會遇到,特別是考量到成本的時候。雖然很討厭這類的問題,但是解決後其實還是蠻有成就感的。以上分享希望能夠幫助大家解決code size issue。

參考

前言

工作到現在已經三年半了,雖然有許多成長,但也看見自己很多的不足,可以再進步的地方。個人覺得如果要能夠更快速地成長,需要定期總結自己的經驗以及所學習的東西,並且思考未來可以再怎麼做會更好。平常我有做日記的習慣,但是還沒有為自己統整工作上面的心得,這邊除了當作紀錄以外,也是可以提供給跟我差不多狀況的人參考。不過老實說,這些分享並不一定正確,但是至少是此時此刻的我最真實的想法,也歡迎大家多多指教。

經歷

畢業前

我是從研替開始軟體工程師的生涯,以前在大學、研究所時,雖然有寫程式,但是畢竟都還是學生等級的程式,個人是覺得不夠深入與成熟。不過另一方面其實也累積了不少基礎,有許多重要的知識都是那時紮根的,包括Linux的使用、git的使用、Network的概念、Security的概念,這些對後來的自己都有很多幫助。雖然有時會想要是現在的自己回到過去的話,肯定可以學得更快更好、更有效率,畢竟產業和學術上還是有多少落差,但這也都有點後見之明了,而且正是因為曾經經歷過,才會有不同看事情的角度。會覺得過去要是怎樣怎樣現在就會更好,大概是人免不了的通病吧!

第一年

進到職場後,最不習慣的是每件事都要很清楚是在做什麼,過去在學校基本上只要code能work就行了,所以最常做的就是上stack overflow找找,然後copy & paste,不太會去理清背後的原理,或是思考怎麼做才會更有效率。然而在職場上,如果只是剪剪貼貼的話,遲早會出問題的,被同事問回答不出來還是小事,更慘的是自己做完了導致其他的bug出現。另外一點是動作需要很迅速,我記得剛過試用期後就被交付了要porting BSP上的ICMP的功能到FreeRTOS,時間給我3天,結果最後花費的時間還是超過,這跟學校其實差異很大,我在學校光是改一個memory leak的bug就改了2個月(雖然是因為還要兼顧課業)。最後,關於品質的部分,也是那時的我需要克服的坎,我不喜歡做測試,可是當時的主管就很強調要做自我驗證,才不會提供的程式上面有很多顯而易見的問題,給出去的程式也是代表自己的品牌,需要細心驗證才行。

其實整體來說,我在職場上的第一年大概是以調整心態並且適應工作為主,而工作內容大概都是偏向AP層的修改,或是開發些應用程式。最常見的case大概是做客製化的firmware以及處理客戶回報的問題,改的內容不外乎是web上的修改,或是AP層上面的邏輯的問題。不過也不是沒有要寫新feature,包括在JAVA應用程式上用JNI連結到C library、Linux command line tool、帳號管理API等等,或多或少訓練了怎麼設計程式架構。

第二年

第二年開始我已經有能力解決比較困難的問題,那時團隊遇到SDRAM不足的問題,後來我透過分析map檔找到一個沒有必要使用的巨大global variable,克服這個難關。不過老實說,我現在回想起來這個問題也不是太難,只是要對firmware compile過程有比較深一點的認知而已。另外當時遇到了過熱當機的問題,這個老實說是幫助我成長最大的問題,我把NXP的MCU spec讀了好幾次,了解了ARM的exception架構,另外也深入理解FreeRTOS以及lwip等NXP提供的BSP是怎麼與MCU互動的。除此之外,我基於資深同事提出的proprietary protocol架構上做改進,並且設計提供給AP使用的API,其實也理解了設計protocol的原則,包括易用、相容性、功能獨立性等等。

這一年對我來說是技術成長最快的一年,開始練習解決問題需要有系統的思維,除了AP層以外,更加理解軟體與硬體的相依性,BSP的概念等等。那時候我常常都是在ARM的exception handler中加上dump stack的功能,然後反推造成當機的原因。這些經驗對後來看待問題時有很大的幫助,會思考這個OS的運作性質是什麼,怎麼去分配記憶體的,比較能夠有系統觀去解決問題。

第三年

第三年因為要做經驗傳承,開始跟資深同事學習公司既有的proprietary OS,除了AP以外,更重要的是底層kernel的運作,類似UNIX上面STREAM的架構。由於FreeRTOS的架構相對來說比較簡單(有些人甚至認為比較像library),所以proprietary OS也是強化了我對OS的理解,而且多了可以比較的參照物。除此之外,因緣巧合下我開始有了機會可以當project leader,雖然團隊加上我只有3個人,不過也算是個不錯的經驗。我主要負責的是與PM、測試溝通,然後分配feature & bug給團隊,並且確保merge的code沒有任何問題。同時間,我也開始和其他同事合作導入CI/CD、code review的概念,剛好就在我的project上進行實驗。

這個時間點對我來說除了技術以外,開始有了與人相關的任務,不論是領導還是溝通。另外因為自己有比較多一點的權力,所以也嘗試導入自己所喜歡的文化,如CI/CD。比較有趣的是,我發現以前的同學也差不多在這個時間擔任leader的角色,也許大家走的路都差不多吧!

現在

延續前一年,我開始擔任既有產品的maintain窗口,學習分配工作給團隊的其他人,並且思考要怎麼進行有效率的溝通,不論是團隊內還團隊外。技術方面則是開始接觸eCos這個系統,雖然外界已經沒人在用了,但是這個產品仍然需要有人maintain。發現挺有趣的是我差不多一年接觸一種OS,也加深了以前學OS的一些概念,例如synchronization、scheduler等等。

其實到了現在我覺得已經進入了一個坎,技術開始進步緩慢,而且因為既有產品線眾多,有許多maintain的effort,沒有心力去學新技術。目前,不得不好好反思自己的職涯規劃,以往我都是認為只要有學習成長就可以了,沒有想太多自己未來的走向,包括什麼是自己的強項,我接下來的時間要學什麼,可以成長多少等等。如果再宏觀一點,從人生角度來說,我規劃學習這些技術對工作有什麼幫助?在我的生命中是什麼樣的地位?我的人生究竟是想要什麼?成就感嗎?還是只要賺夠多錢就行了?這些都是以前有想過,但是沒有仔細思量的,現在因爲工作上面遇到了些障礙,所以會開始思考什麼才是自己想要的。

雖然我是因為感覺沒有成長所以思考是不是要改變跑道,但是另一方面來說,我覺得也是因為這個契機才開始會反省工作在自己人生中的意義是什麼,這倒也不是什麼壞事。人本來就應該要很清楚自己想要什麼,留在某個地方就是要承擔自己失去的其他機會成本,離開某個地方就是要能捨棄當前環境的舒適以及穩定,沒有好或壞,就只是要自己承擔相對應的責任。

總結

這職場三年多下來,其實我也學習了很多事情,比起技術而言,我覺得最重要的是觀念的改變。從一個人的觀念和面對事情的態度,大概就可以推估他的未來發展。技術要學其實是可以很快,但是個人特質是很難短時間內改變,因此我覺得這是影響個人成就非常深遠的關鍵。以下就我的觀點分享認為重要的事情。

  1. 發問前一定要再三思考
    • 在問別人問題的時候,要先思考想從對方那邊得到什麼樣的答案。簡單來說就是不要無腦發問,同事都是很忙的,所以相對應來說可以問問題的次數是有限制。因此每次發問都先思考:我問的問題是否其實查一查資料或做做實驗就可以解決?我想問的問題到底是什麼?有時候整理一下自己的想法後,就赫然發現其實問題已經得到了解答,根本不需要問人,這也是常聽到的Rubber Duck Debugging
    • 一般來說我們遇到不確定的問題都會去請教主管,看要怎麼做會比較好,這個做法在剛進公司時是OK的,畢竟對工作內容還不熟,但是隨著自己慢慢熟悉,要做的應該是自己能夠做主並且決策。以公司的角度來說,多請人就是要減少大家的工作量,如果事事都要去問人的話,那就不需要多請人了。當然有些問題可能是一定要主管做決策的,那可以提出自己的想法以及建議的解決方式,主管只要確認我們的解法沒有什麼大問題就可以了。
  2. 定期反省,找出更有效率的做法
    • 可以的話,建議每天做日記,思考今天的工作內容有什麼可以改進之處。不過老實說這樣確實是蠻累的,而且會變成無腦的慣性,所以也可以改成每週一次。一個人在工作上的表現,大概就是解決問題的能力和效率,如果這個能力能不斷提升,能創造的價值就越高。
    • 舉例來說,在我的公司就是要處理很多文件流程,每次要跑大家就都要到處問人,後來我受不了了,乾脆自己整理一份跑流程的SOP,每次跑我就參考SOP執行,如果有錯再修改SOP,大大減少我在工作上的煩躁感。而且後來有新人進來要跑流程時,我也可以請他們看SOP,而不需要手把手教學,減少時間的浪費。
  3. 用目的論思考,了解自己做的事情是為了什麼,要解決什麼問題
    • 我發現有時候自己做的事情和別人對我的期待其實是有落差,我認為重要的東西,別人卻覺得那個不是重點。因此面對任何任務,都要確定做這件事的目的是什麼,例如是要給客戶滿意的解釋,還是要百分之百肯定問題的根因,然後是不是值得投入相對應的資源。
    • 常常做事情都會不小心過度鑽研在細節上面,大家各自提出自己的意見,而無法做出正確的決策,這時候最好都是要回到最初的問題:我們到底是要解決什麼,怎麼決策才能符合我們最一開始的目的。不會有完美的解答,但是只要能達到設定的目標,那就是可以接受的答案。
  4. 溝通的時候,站在對方的立場思考
    • 工作上基本上一定會有cowork的機會,而很多的爭論幾乎都是來自於溝通不夠完全。回應他人的問題時,可以站在他的角度思考,他想得到什麼答案,我能提供什麼答案,怎麼在中間取得平衡,不要變成事情都是某一方去承擔,另一方面也是提高溝通效率,不會信件來回很多次都得不到共識。
    • 有時候不要太依賴信件,有些情況直接面對面溝通會來得有效率,且也不會太過生硬,讓人有距離感,有時候都是因為雙方文字上彼此誤會而吵起來。
  5. 要定期盤點自己的能力
    • 我會強烈建議要隨時maintain一份A4的履歷,並不是說要騎驢找馬,而是為了把自己重點且精華的能力精簡成一份履歷,其實也是認識自我的一種方式。整理完後會發現原來自己的強項是什麼、還欠缺什麼,工作的時候就比較不會得過且過,而是會用宏觀的視野思考為什麼要做,還能怎麼做會更好,因為這些都將變成未來與人談判的籌碼。當然如果有好的機會的話,已經有一份履歷在手,就可以隨時把握。
  6. 負起責任
    • 負起責任有幾個方面,首先就是對自己做的事情,任務交到自己的手上,那就是要把它做好,如果發現有任何問題就是要隨時反應,不要到了最後一刻才說。另外這也隱含了一點:我不是機器人,別人說什麼就做什麼,而是要有能力自己判斷、決策並且承擔最後的結果。能做到這點,周圍的人就可以放心與自己合作了。
    • 另外一方面是對自己負責,永遠要記得不管做什麼決定,承擔結果的是自己。不管是想要加班做事情、針對某個問題作深入鑽研、隱藏遇到的問題裝做表面一片和諧,其實都無妨,只要能夠接受最終結果就好。既然都已經是成年人了,就不要想著拿盡所有好處而且可以逃避責任。以職業生涯來說,選擇自己的去留也是同樣的道理,如果放棄了現在的位置,那就別未來才後悔失去了許多福利,如果選擇留著,那也別再抱怨沒有新的成長,一切都是在於自己的選擇。

不過老實說,上面的分享也只適用於想不斷提升自己的情況,但是人生是有很多面向的,我也曾經看過有人工作只出五分力,剩下的時間都是專注在自己的生活品質上面。這並沒有什麼不好,甚至如果以長遠來看,他說不定活得還比認真努力打拼的人更快樂。反正,最重要的還是要記得人生是自己的,做什麼決定都沒關係,只要肯承擔後果就好

簡介

Jupyter Notebook,過去被稱為ipython notebook,是ipython內的強大工具。

Jupyter最常用在學習資料處理上面,因為輸入指令後就可以產生相對應的圖形結果,做到資料視覺化的功能。而且更重要的是我們可以將自己的結果輸出成html或上傳Github,分享給其他人進行討論。

安裝

MAC

如果有安裝python-pip了,可以直接用如下指令安裝。要是遇到權限問題可以再加上sudo。

1
pip install "ipython[notebook]"

使用

基本操作

  • 創造一個資料夾,然後在裡面開啟jupyter notebook
    1
    2
    3
    mkdir ipython_notebook && cd ipython_notebook
    jupyter notebook
    # 原本是可以用ipython notebook,但是未來可能會被捨棄
  • 如果要在別的port開啟
    1
    jupyter notebook --port 8080
    接下來在web上應該可以直接連線Jupyter。

選擇New->python3後就可以在web新創一個notebook,值得注意的是這個notebook的副檔名是.ipynb,存放位置就是在我們當前的目錄,也就是ipython_notebook

登入機制

jupyter notebook其實是有登出機制的,在右上角logout後,就要用密碼或token才能登入。

這時候其實可以直接重啟server,或是輸入jupyter notebook list來查看token,就可以再次登入了。

編輯方式

在Jupyter中,進入notebook後會看到一個可以輸入值的空間,這個叫做cell。cell上面輸入python語法後,按下shift+enter就會產生執行結果。而我們可以增加或減少這些cell。

特別注意原本cell是藍色的,代表在command mode,但是如果點選cell後就會變成綠色,代表進入edit mode。從edit mode跳回command mode只要按下ESC即可。

另外可以注意每個cell可以選擇不同屬性,最常用的還是Code和Markdown。Code就是python的部分,而Markdown則是可以寫上相關的文字敘述。

常用快捷鍵

主要可以點選Help->Keyboard Shortcuts來看目前快速鍵怎麼使用(或是按ESC+h更快)

常用快速鍵如下所示:

  • c:複製當前的cell
  • x:剪下當前的cell
  • v:貼上剪貼簿的cell
  • dd:刪除當前cell
  • a:在上方插入新的cell
  • b:在下方插入新的cell
  • shift+enter:執行當前cell並跳到下一個cell
  • ctrl+enter:執行當前cell
  • shift+tab:可以顯示當前函式的使用方法

分享

我們除了可以把當前notebook下載成html外,也可以push到Github上並且利用nbviewer這個網站來分享。

舉個例子,A gallery of interesting Jupyter Notebooks就收集了不少有趣的Juypter Notebook範例。

只要有ipynb上傳到Github,我們就可以看到輸出結果,就像這個GitHub的結果可以被nbviewer顯示出來。

參考

簡介

最近我在研究怎麼在ARM Cortex M3/4上面跑一個自己寫的OS,最主要是參考jserv的mini-arm-ospikoRT,相關程式碼放在arm-os-4fun
最近發現自己遇到了些問題,想說再研究怎麼解決的過程中順便把細節紀錄下,供自己未來可以參考。

這邊首先要探討的是Cortex M3/4上面有的權限模式,以及它們是怎麼進行切換等細節。
原本我是在qemu上面跑STM32虛擬機,但是後來發現好像跟真實硬體有點不一致,所以後來我都在STM32F429的硬體上面來測試了。

Cortex M權限設計

首先我們先了解Cortex上面有哪些權限模式,處理器上面有兩種Operation Modes:Thread mode和Handler mode。

  • Thread Mode:一般程式運行的狀態。
  • Handler Mode:處理exception的狀態。

然而除了這個以外,還有不同的Privilege Levels,避免一般使用者可以存取敏感資源。

  • Privileged:可以存取所有資源,在CPU reset之後就是privileged。
  • Unprivileged:通常是讓OS中userspace的程式運行用的,在幾個方面存取資源是受限的。
    • MSR、MRS指令存取上會有限制。
    • 無法存取system timer、NVIC。
    • 有些memory無法存取。

Operation Modes和Privilege Levels的關係如下所示,Unprivileged不能進入Handler Mode的。

- Privileged Level Unprivileged Level
Handler Mode O(state1) X
Thread Mode O(state2) O(state3)
  • 上面標註的state 1-3是為了方便我們後面講解而標的。

如何切換權限與模式

關於切換的部分可參考下圖,圖片來源A tour of the Cortex-M3 Core

模式切換

下面我們先看怎麼樣從state2,state3進入state1,也就是發生exception,然後再從state1回來。

Exception Entry

進入exception有兩種情況:

  1. 目前我們在thread mode
  2. preempts:發生的exception比目前我們所在的exception權限還高

發生exception時,ARM會自動把當前的register的資訊存起來,順序為xPSR, PC, LR, R12, R3, R2, R1, R0。儲存的方式就是push到當前的stack中,可能是main stack(SP=MSP),也可能是process stack(SP=PSP)。

address register
SP+00 R0 <- SP after exception
SP+0x04 R1
SP+0x08 R2
SP+0x0C R3
SP+0x10 R12
SP+0x14 LR
SP+0x18 PC
SP+0x1C xPSR
SP+0x20 xxx <- SP before exception

完成後接著會開始執行exception handler,並且把EXC_RETURN寫入LR。

Exception Return

要從exception跳還必須要符合兩個條件:

  1. 目前正在Handler Mode。
  2. PC的值是合法的EXC_RETURN。

關於EXC_RETURN的值,其實代表了ARM從handler mode回去的路徑,有三種可能:

  1. 目前是nested exception,回去上層還是handler mode。
  2. 是由privileged thread mode呼叫的,也就是要回到state2。
  3. 是由unprivileged thread mode呼叫的,也就是要回到state3。

因此EXC_RETURN有三個可能的值

EXC_RETURN Description
0xFFFFFFF1 Return to Handler mode.
Exception return gets state from the main stack.
Execution uses MSP after return.
0xFFFFFFF9 Return to Thread mode.
Exception Return get state from the main stack.
Execution uses MSP after return.
0xFFFFFFFD Return to Thread mode.
Exception return gets state from the process stack.
Execution uses PSP after return.

Privileged to Unprivileged

接著我們要來探討怎麼從Privileged進入Unprivileged,也就是state2進入state3的部分。

如果要進入Unprivileged,那必須使用到特殊register - control。

bit Description
CONTROL[1] 0:Use MSP, 1: Use PSP
CONTROL[0] 0:Privileged thread mode, 1:Unprivileged thread mode

要特別注意操作control register一定要用MRS和MSR register

1
2
3
4
# CONTROL值搬到R0
MRS R0, CONTROL
# R0的值放入CONTROL
MSR CONTROL, R0

進入Unprivileged Thread Mode的操作

1
2
MOV R0, 3
MSR CONTROL, R0

ARM在切換上面的設計

ARM在處理nested exception上有自己的一套做法來加快速度,確保高優先權的exception能更快被執行到,達到更高的即時性(real-time)。

下面介紹兩種在Cortex M上面的機制:

  • tail-chained:
    • 情況:如果發生exception1的時候又發生exception2,但是exception2的優先權沒有高於exception1,必須等待。
    • 原本:一般來說exception1結束的時候會先pop stack,然後再push stack進入處理exception2。
    • 改進:exception1到exception2中間的pop&push其實是沒意義的,所以ARM Cortex M會在exception1結束後直接執行exception2,減少了中間的浪費。
  • late-arriving
    • 情況:如果發生exception1並且執行state saving(上面說的push register),這時候有更高優先權的exception2進來,發生preempts。
    • 原本:會中斷exception1的state saving,優先讓給exception2。
    • 改進:exception2其實也是需要state saving,所以繼續維持state saving,然後直接執行exception2。當exception2結束後,就又可以使用tail-chained的模式來執行exception1。

參考

關於Cortex M相關的資料非常推薦下面兩本書籍,都有中文的翻譯。JosephYiu有參與ARM Cortex M的設計,比較有權威性。

可參考jserv老師和學生撰寫的rtenv+簡介,裡面也有提到ARM CM3權限的部分。

簡介

當我們要製作報告或論文的圖表時,除了excel以外,其實也可以使用gnuplot這套工具。gnuplot非常的強大,除了可以畫各種圖表以外,還可以跨不同平台使用。

我們這邊簡單紀錄一些常用圖表怎麼繪畫。

安裝

MAC

如果我們要正常顯示圖表的話需要有x11,這部分可以安裝APPLE的XQuartz即可,這樣啟動gnuplot的時候就會自動啟動XQuartz了,可參考Can’t plot with gnuplot on my Mac

接下來安裝gnuplot的時候要特別注意,如果沒有加上--with-x11的話,可能會造成Terminal type set to 'unknown'的warning,可參考Can’t find x11 terminal in gnuplot Octave on Mac OS

1
brew install gnuplot --with-x11

使用

基本操作

1
2
3
4
5
6
7
8
9
10
# 啟動
gnuplot
# 畫出sin(x)的圖
plot sin(x)
# 設定範圍,x軸是-10到10,y軸是0到2的cos(x)
plot [x=-10:10] [0:2] cos(x)
# 清空之前的設定
reset
# 結束
exit

讀取檔案

我們可以把多筆資料先存成檔案,然後再讓gnuplot來讀

我們先存資料到data.txt,中間用空格隔開

1
2
3
4
5
1 5
2 10
3 15
4 10
5 5

執行gnuplot就會看到有許多一點一點資料散佈在plot上

1
2
gnuplot
plot "data.txt"

如果要開啟多個檔案

1
plot "data1.txt", "data2.txt", "data3.txt"

存成程式

每次都要自己一個個輸入指令說實在太麻煩了,我們可以存成.plt檔,以下面為例存成plot.plt

1
plot "data.txt"

進入gnuplot後輸入如下指令即可

1
load "plot.plt"

圖表上的文字

圖表上面總是要有些文字說明,可參考如下設定

1
2
3
4
5
6
7
8
9
10
11
12
# 設定標題
set title "pic_title"
# x軸說明
set xlabel "x(unit)"
# y軸說明
set ylabel "y(unit)"
# 設定線條說明外框
set key box
# 不要線條說明
set nokey
# 如果要修改線條說明
plot "data1.txt" title "title 1", "data2.txt" title "title 2"

圖表的顯示

也許我們會想改變圖表上面的顯示

1
2
3
4
5
6
7
8
9
10
# 增加格線
set grid
# 數據連成一條線
set style data lines
# x軸的範圍
set xrange [-10:10]
# y軸的範圍
set yrange [-10:10]
# X軸的單位
set xtics x: 每次x軸都增加x

plot上其實也可以做一些操作

1
2
3
4
5
6
7
8
# 使用data.txt,並且畫成線,linestyle為1,linewidth也為1
plot "data.txt" with lines linestyle 1 linewidth 1
# 使用data.txt,pointtype為1,pointsize也為1
plot "data.txt" with point pointtype 1 pointsize 1
# 如果線和點都要的話
plot "data.txt" with linespoints
# 如果要變成長條圖的話
plot "data.txt" with boxes

儲存成圖片

1
2
3
4
5
6
7
8
9
10
# 要存成png檔案
set terminal png
# 可以加上size資訊
set terminal png size 1200,800
# 輸出圖片,這個指令會等待後續的plot
set output "output.png"
# 輸出圖片
plot .....
# 記得要再改回x11
set terminal x11

常用

  • 折線圖
    • 先產生出data.txt
    • 使用在gnuplot中load如下plt檔
      1
      2
      3
      4
      5
      6
      7
      reset
      set title "pic_title"
      set xlabel "x(unit)"
      set ylabel "y(unit)"
      set terminal png
      set output "output.png"
      plot "data.txt" with linespoints title "title 1"
  • 長條圖
    • 先產生出data.txt
    • 使用在gnuplot中load如下plt檔
      1
      2
      3
      4
      5
      6
      7
      8
      9
      reset
      set title "pic_title"
      set xlabel "x(unit)"
      set ylabel "y(unit)"
      set terminal png
      set output "output.png"
      # 設定長條圖的size
      set boxwidth 0.3
      plot "data.txt" with boxes title "title 1"

參考

簡介

有時候需要對影片、音樂做各種處理,例如轉檔、切割等等,這時候可以使用很強大的影音處理神器ffmpeg來做這些操作。

這邊不會細談調整編碼等細節,只是記錄日常常用到的操作指令而已。

安裝

MAC

1
brew install ffmpeg

使用

轉檔

-f代表format

1
ffmpeg -i [要轉的檔案] -f [目標格式] [輸出檔名]

有哪些格式可選可用如下指令

1
ffmpeg -formats

裁減影片

-ss代表從何時開始,-t代表維持時間,-to代表停止的時間

1
2
3
4
# 從5秒開始後的30秒
ffmpeg -i [要轉的檔案] -ss 00:00:05 -t 00:00:30 [輸出檔名]
# 從5秒到25秒
ffmpeg -i [要轉的檔案] -ss 00:00:05 -to 00:00:25 [輸出檔名]

顛倒影像

-vf代表vedio filter,可以讓影片經過處理,轉換影片角度有下面三種常用

  • hflip:水平翻轉
  • vflip:垂直翻轉
  • transpose=1:順時針轉90度
    1
    2
    3
    4
    5
    6
    7
    8
    # 水平翻轉
    ffmpeg -i [要轉的檔案] -vf hflip [輸出檔名]
    # 垂直翻轉
    ffmpeg -i [要轉的檔案] -vf vflip [輸出檔名]
    # 順時針轉90度
    ffmpeg -i [要轉的檔案] -vf transpose=1 [輸出檔名]
    # 逆時針轉90度
    ffmpeg -i [要轉的檔案] -vf transpose=2 [輸出檔名]

影片截圖

-an代表不需要聲音,-vframes代表要抓幾張圖,-r代表每秒抓幾張圖

1
2
3
4
5
ffmpeg -i [要轉的檔案] -an -ss [抓取時間] -vframes [幾張圖] -r [幾張圖] [輸出圖檔]
# 在開始的時間抓一張圖
ffmpeg -i [要轉的檔案] -an -ss 00:00:00 -vframes 1 cover.jpg
# 從頭開始,每10秒抓一張圖
ffmpeg -i [要轉的檔案] -an -ss 00:00:00 -vframes 1 -r 0.1 tmp-%d.jpg

調整音量大小

-vol代表聲音大小,256是正常

1
ffmpeg -i [要轉的檔案] -n [聲音大小] [輸出檔名]

播放影音

在ffmpeg內有一個tool是ffplay,可以簡單用來播放影音

雖然沒有進度條,但是如果按著右鍵左右移動也會有進度條的效果

1
ffplay [影片名稱]
  • 如果只想要播放音樂
    1
    ffplay -vn [影片名稱]
  • 如果只想要播放影片
    1
    ffplay -an [影片名稱]
  • 重複循環,0代表無限次
    1
    ffplay -loop [次數] [影片名稱]

常用

  • 影片轉音樂
    1
    ffmpeg -i [要轉的檔案] -f mp3 [輸出檔名]
  • 轉換成mp4
    1
    ffmpeg -i [要轉的檔案] -f mp4 [輸出檔名]
  • 裁減影片
    1
    ffmpeg -i [要轉的檔案] -ss [開始時間] -to [結束時間] [輸出檔名]
  • 抓截圖
    1
    ffmpeg -i [要轉的檔案] -an -ss 00:00:00 -vframes 1 cover.jpg
  • 聲音調整
    1
    2
    3
    4
    # 調大聲音
    ffmpeg -i [要轉的檔案] -vol 300 [輸出檔名]
    # 調小聲音
    ffmpeg -i [要轉的檔案] -vol 200 [輸出檔名]
  • 手機拍攝如果是反的情況
    1
    2
    3
    4
    # 順時針
    ffmpeg -i [要轉的檔案] -vf transpose=1 [輸出檔名]
    # 逆時針
    ffmpeg -i [要轉的檔案] -vf transpose=2 [輸出檔名]

參考

簡介

dd全名叫做data duplicator,這個工具最主要的功能是對資料作複製、修改、備份,是一個很方便的小工具。通常Linux中預設都會有,不需要額外安裝。

使用教學

基本

  • 輸入輸出參數
    • if=FILE:輸入名稱
    • of=FILE:輸出名稱
1
dd if=[input_file] of=[output_file]

轉換

  • 做相對應的轉換conv=CONVS
    • lcase:大寫字母換小寫
    • ucase:小寫字母換大寫
    • nocreat:不要建立輸出檔案
    • notrunc:input小於output時,仍維持output大小
    • fdatasync:讓資料同步寫入硬碟
1
2
# 轉為小寫
dd if=[input_file] of=[output_file] conv=lcase

區塊

  • bs=[bytes]:等同於同時設定ibs和obs,一次讀或寫的block size。
    • ibs=[bytes]:指定每次讀取的block size(default 512 bytes)
    • obs=[bytes]:指定每次寫入的block size(default 512 bytes)
  • count=[number]:只處理前[number]輸入區塊,block size要參考ibs。
  • seek=[number]:輸出檔案跳過前[number]個區塊,block size要參考obs。
  • skip=[number]:輸入檔案跳過前[number]個區塊,block size要參考ibs。

常用指令

  • 大小寫轉換
    1
    2
    3
    4
    # 換大寫
    dd if=[input] of=[output] conv=ucase
    # 換小寫
    dd if=[input] of=[output] conv=lcase
  • 產生一個特定大小的檔案
    1
    2
    3
    4
    # 內容為空的1KB檔案
    dd if=/dev/zero of=[output] bs=1024 count=1
    # 內容為亂數的1MB檔案
    dd if=/dev/urandom of=[output] bs=1m count=1
  • 把特定檔案的開頭512 byte清空
    1
    dd if=/dev/zero of=[output] bs=512 count=1 conv=notrunc
  • 備份硬碟
    1
    2
    3
    dd if=[來源] of=[目標]
    # 例如從/dev/sda備份到/dev/sdb
    dd if=/dev/sda of=/dev/sdb
  • 備份光碟,可參考Create an ISO Image from a source CD or DVD under Linux
    1. 先觀察/dev/cdrom
      1
      isoinfo -d -i /dev/cdrom | grep -i -E 'block size|volume size'
    2. 然後應該會出現類似如下內容
      1
      2
      Logical block size is: 2048
      Volume size is: 327867
    3. 接著參考上面的數字使用dd指令(bs大部分都是2048,而count其實有加沒加都沒差)
      1
      2
      3
      dd if=/dev/cdrom of=test.iso bs=<block size from above> count=<volume size from above>
      # 以上述例子
      dd if=/dev/cdrom of=outputCD.iso bs=2048 count=327867
  • 拆分&合併檔案,可參考Splitting and Merging files using dd
    • 拆分檔案,例如把檔案切成好幾個1G
      1
      2
      3
      dd if=[大檔案] of=[part1] bs=1m count=1024
      dd if=[大檔案] of=[part2] bs=1m count=1024
      ....
    • 合併檔案,例如好幾個1G合併起來
      1
      2
      3
      dd if=[part1] of=大檔案 bs=1m count=1024
      dd if=[part2] of=大檔案 bs=1m count=1024 seek=1024
      ....

參考

簡介

當我們要下載網路影片時,通常會去使用browser上的套件來下載,其實除了browser套件外,我們也可以使用command-line的方式,也就是這篇要介紹的youtube-dl。

youtube-dl功能十分強大,也有很多參數可以調整,能下載的網站不只是youtube,也可以是其他熱門網站,例如Facebook等等,更重要的是這個工具有多個平台可以使用(Windows、Mac、Linux)。

除了指令youtube-dl以外,我們也可以用GUI的介面的工具youtube-DLG,使用上更為方便,詳請可參考最強的網路影片下載器 Youtube-dl-gui 只要有網址就能幫你搞定

安裝

MAC

1
2
3
brew install youtube-dl
# 如果有需要後續轉檔的話
brew install ffmpeg

Ubuntu

1
2
3
sudo apt-get install youtube-dl
# 如果需要後續轉檔的話
sudo apt-get install ffmpeg

Python

其實更好的方法是使用Python的pip來安裝,因為youtube-dl本身就是使用Python所寫成的,而由於影片的網站更新很快,所以可能要隨時更新到最新版的youtube-dl才行,OS distribution不一定會出的那麼快。

1
2
3
pip install --upgrade youtube_dl
# 如果使用python3的話
pip3 install --upgrade youtube_dl

使用

這邊介紹一些常用的指令

支援

  • 確定有支援下載哪些影片網站,相關列表也可以從官網查詢
    1
    youtube-dl --extractor-descriptions

格式

如果我們沒有指定格式的話,通常youtube-dl會幫我們挑最好的

  • 指定下載的影片格式
    1
    2
    3
    4
    5
    6
    # 先查詢有哪些格式可下載
    youtube-dl -F [URL]
    # 指定下載格式
    youtube-dl -f mp4 [URL]
    # 或是用format code
    youtube-dl -f [列表中的format code] [URL]

輸出格式

由於官方的輸出格式預設有帶ID(%(title)s-%(id)s.%(ext)s),我們可以將其去除

1
youtube-dl -o '%(title)s.%(ext)s' [URL]

字幕

  • 選擇嵌入特定字幕
    • --write-sub代表下載字幕
    • --embed-sub代表嵌入字幕
    • --sub-lang代表要選擇的字幕
      1
      2
      3
      4
      # 先列出可下載的字幕列表
      youtube-dl --list-subs [URL]
      # 嵌入想要的字幕
      youtube-dl --write-sub --embed-sub --sub-lang [字幕] [URL]
  • 直接嵌入所有字幕
    • --all-subs選擇所有字幕
      1
      youtube-dl --write-sub --embed-sub --all-subs [URL]

轉為音樂格式

如果我們要下載音樂格式的話,基本上需要有ffmpeg的輔助

  • 選擇要下載的音樂格式,例如mp3、m4a、flac等等
    1
    youtube-dl -x --audio-format [音樂格式] [URL]
  • 可以用--audio-quality強迫ffmpeg轉換較高品質的音樂,0是最好,9是最差
    1
    youtube-dl -x --audio-format [音樂格式] --audio-quality [音樂品質] [URL]
  • 下載時附上封面(使用youtube截圖)和音樂資訊(作曲者等等)
    1
    youtube-dl -x --audio-format [音樂格式] --embed-thumbnail --add-metadata [URL]

下載播放清單

  • 其實只要把[URL]換成播放清單的網址即可,不過我們也可以指定開始和結束位址
    • --playlist-start:開始
    • --playlist-end:結束,也就是倒數第幾個影片
      1
      youtube-dl --playlist-start [開始位置] --playlist-end [結束位置] [URL]

常用

我這邊直接列出常用的指令,如果要使用可以直接copy比較快

  • 下載mp4影片並加上字幕
    1
    youtube-dl -f mp4 --write-sub --embed-sub --all-subs -o '%(title)s.%(ext)s' [URL]
  • 下載mp3音樂,並加上封面
    1
    youtube-dl -x --audio-format mp3 --audio-quality 0 --embed-thumbnail --add-metadata [URL]

參考

簡介

這篇想要介紹的是「被討厭的勇氣」這本書,書中用哲學家和年輕人用對話方式來闡述阿德勒心理學。由於阿德勒心理學跟較廣為人知的佛洛伊德心理學相比更冷門一點,所以很多想法都給現代人帶來啟發,也因此廣受歡迎。
這系列書分為兩集,上集是對阿德勒心理學有個綜觀認識,而下集則是因為讀者有回饋不少意見而針對這些疑問的回答。個人認為下集比較偏向怎麼去實踐方面,其中幾乎有八成是在講教育部分。雖然我覺得一般人如何去實踐部分較少有點可惜,但是對諮商師、教師來說可能極具價值吧!
其實書中討論的阿德勒心理學範圍還蠻廣的,原本我想嘗試用整體架構來分析,但是發現可能以目前所掌握的相關知識不足以做這樣的評論,所以我改成會針對幾點比較有收穫的概念來介紹。

目的論

目的論其實是這本書很重要的精華,反對了宿命論,強調了人類可以自我改變的能力。我們常常太過注重因果關係了,「因為過去發生怎樣怎樣,所以現在的我才變成這樣」或是「都是因為他先怎樣怎樣,我才會這樣」。雖然某種層面上可能是對的,但是更可能的是這些都只是藉口,因為我們並非受制過去原因而行動,而是朝向自己決定好的目的而行動。換句話說,自己之所以不幸,完全是自己親手所選擇的
這個概念很重要,如果強調因果論,那代表的是現在的我是無法改變的,因為過去是無法改變的,這是宿命。然而將其想成是我們為了什麼目的才變成現在的自己,那隱含的意思是只要改變了目的,現在的自己也會跟著改變。不是因為過去的經驗形塑了現在的我,而是我去賦予過去經驗什麼意義來解釋自己的人生。由此可知,重要的不是經歷了什麼,而是如何去運用它
我們在生活中常常會對現狀不滿,這時候就會去找理由,都是因為環境、過去等等什麼原因導致自己這樣的。但是如果用目的理論來看,我們可能是「不想去改變」而去找理由。雖然現狀令人不滿,但是改變更加痛苦,為了不去改變,我們必須要有可以歸罪的理由。說直白一點,我們是因為缺乏「改變的勇氣」,所以寧可選擇了不幸的現狀。
在目的論下,一切的理由都是藉口,當前現況完全是自己所選擇的。在下集有提到一個三角柱的概念,我們跟親人、朋友常常會抱怨誰誰誰很可惡,自己很可憐,這也就是三角柱其中兩面,「可惡的他」和「可憐的我」,但是更為重要的是第三面「今後該怎麼辦」。不斷地找理由不會改變現況,要專注於自己能改變的事情上面。
老實說,目的論真的蠻殘酷的,我們為了讓自己好過一點,感性上會想逃避自己的責任,去抱怨外在的環境,但是就現實與邏輯而言,如果真的要改變現狀,那就得承擔起自己應負的責任,並找出改變的方法。「責任」與「勇氣」說起來容易,但是當想到要用在自己身上時,才真正感受到其重量。

逃離競爭關係

阿德勒説:「一切的煩惱都是人際關係的煩惱」。雖然有點極端,不過確實人類的煩惱大多數是來自人際關係。其中對我而言,最常見的煩惱是怕自己輸給別人。人類與身俱來就有「追求卓越」的慾望,想奮發向上,但是一旦理想無法達成時,就會有產生自己低劣無能的「自卑感」。
在當前社會上,其實都會有「競爭」的關係,在學校時是同學,在公司時是同事,會害怕自己輸給別人,擔心自己可能會輸,因此要不斷贏下去。儘管這種方式可以不斷促進自己進步,但也導致看到別人成功幸福時,會無法發自內心地去祝福,甚至更惡劣的,看到別人不幸會覺得可以證明自己的成功。
其實我從求學階段就或多或少發現自己也有這樣的心態,不斷去比較自己與同儕間。然而競爭是一個無限循環,我打敗了一群人,還有更厲害的人在等著,就算我打敗所有人站在巔峰,還是會時時擔心自己會輸,不斷去維持自己的地位,這也意味著永遠得不到幸福。如果我們真的想獲得幸福,必須要脫離競爭模式,放棄比較。
目前的我會把人生當做擁有一筆錢可以隨意去買自己想要的東西,如果去比較我買的東西跟別人比起來有沒有比較大、比較好,那並不會得到快樂。不如專心在選擇自己要買哪些東西,我想要買的東西組合肯定跟別人不一樣,也無從比較起,重點是買到的東西能不能帶給我快樂,而不是比別人還要好。

課題的切割

其實從上面提到的目的論可以看出,阿德勒心理學很重視可控制這件事,因為可控這件事對人類而言十分重要,甚至有研究指出會影響到健康。如果用目的論來看,那代表人生是可控制的,我們能去改變。然而事實上人生還是有很多層面是不能控制的,特別是人與人關係,我沒辦法掌握別人怎麼想,如果不能控制,那就會造成自己的不安,對幸福人生來說是種破壞。
關於這點,阿德勒提出了「課題切割」,劃清界線,這件事從哪邊開始是自己的課題,哪邊是別人的,最簡單的區分方式是思考「因為這個決定而帶來的結果,最後會由誰來承受?」。切割課題後,不去介入他人課題,也不讓他人介入自己的課題。介入他人的課題會背負他人的人生,而讓他人介入自己的課題則會讓自己左右為難,就像父子騎驢的故事,不管自己怎麼做,都有人會不滿意的。
課題切割最高明的點在於確認了什麼是自己可控,什麼不是。我們只要專注在自己能改變的事情上,其他則不用太在意。按照書中的說法,這個就是人際關係的王牌,因為主動權在我手上,不用在意他人想法。
也許會有人認為這樣會很招人厭惡,不太能夠做到。然而不這麼做,將會讓自己的人生變得很被動,因為要不斷去迎合別人。這邊也帶出了本書的書名,為了行使自由,讓自己依照自己的生活方針過日子,必須要能接受別人的討厭,因此需要擁有被討厭的勇氣

建立橫向關係

人際關係有分為橫向和縱向,阿德勒心理學否定一切的「縱向關係」,提倡所有的人際關係都應該是「橫向關係」。縱向關係帶來的是稱讚、責罵,然而橫向關係帶來的是尊敬、感謝。
我會特別把這點拿出來提的原因是書中有舉一個很有趣的例子:假設你遵從上司的指示,結果卻因此面臨工作上的挫敗,這應該是誰的責任?如果是縱向關係的人就會認為是上司的責任,因為是上司的指示,然而這個卻是隱含著自己逃避了責任、閃避複雜的人際關係,因為不用思考太過困難的事、不必為失敗負責,所以仰賴他人的指示過日子。對於橫向關係的人來說,應該要拒絕並提出更好的方案,工作成敗是自己要負責的,沒有任何理由推託。其實在工作上還蠻常遇到為了避免麻煩,把問題交給別人決定的情況,讓別人決定別人負責,但是真正負責任的做法也許是自己也提出想法,然後再與別人共同討論並共識出較好的做法。
除了對上關係外,對下也是「縱向關係」的一種,像是對孩童的讚賞或責罵。阿德勒很尖銳地指出,大人說對孩子好而去責罵通常不是真正為了小孩好,而是因為小孩犯錯隱含著大人有教育失敗的責任,而為了自己不要被批評,所以想要控制他照著自己的想法走。然而教育是為了讓孩子能夠自立,我們只能從旁提供協助,就算是失敗也是要由自己去負起責任。有點像是學習騎腳踏車,真正騎車的人是小孩自己,跌倒也必須是自己去承擔,但我們可以提供他建議和方法,如果怕小孩受傷而不放手,那他就永遠學不會騎車,只能不斷依賴我們。這跟教育有關的部分目前我大概只有這樣的了解,也許未來有小孩之後可能會有更多的體悟吧!

活在當下

「活在當下」這句話大部分的人都聽膩了,幾乎都變成是一個政治正確的口號,不過這邊我想提提阿德勒對活在當下的看法。本書用旅遊來比喻人生,出去旅遊絕對不是到達目的才叫旅行,如果是這樣我們就用最快的方式飛到目的地,然後再快速返回不就達成目標了?從自己踏出家門的那一刻起,就已經稱作旅行,旅行的概念是在到達目的前的每個瞬間,認真享受每個瞬間才是旅行的目的。
同理人生也一樣,人生就像是登山攻頂的活動,有大半時間都在半路上,如果不能認真對待這個過程,只期待登頂的那個瞬間,那不就代表人生有一大半是沒有價值的?對我而言,常常也會期待未來會變得更好,希望「當下」趕快過去,只要我做完什麼事情人生就會不一樣了,像是學生時期要準備大考、工作時要面對大案子等等。可是再回過頭來想,面對困難挑戰永遠只想說熬過去人生會更好,好像完全沒有真正更好過,因為我只有完成挑戰那一瞬間獲得解脫的快樂,但很快又會有下一個挑戰。與其自己只能在這樣的痛苦中不斷輪迴,不如專心面對當下每個瞬間。
關於活在當下,其實也是呼應了前面目的論的看法。「人生是一連串的剎那,過去和未來都不存在,別想要藉著回顧過去、預見未來,給自己一個免除責任的藉口。」由於自己真正擁有的是現在,如果用過去當做藉口,就意味著放棄了現在所擁有的選擇權。當自己想把現在的不如意推給過去,或是期盼未來會更好時,都要注意是不是根本沒有活在現在,應該要認真做當下自己所能做改變的事情。

評價

以書寫手法來說,兩本書的結尾基本上是年輕人被哲學家說服,受到感動想去嘗試阿德勒心理學,說實在這有點讓人覺得矯情,我覺得其實不用用這種傳教意味濃厚的結尾也不會讓書的價值打折扣,不太清楚為什麼用這種想煽動人心的結尾。
另外,很多觀念在對話中並沒辦法越辯越明,個人認為講不夠詳細、邏輯不夠清楚的地方還蠻多的。舉個例子,書中有提到要無條件相信別人,但下集又提到相信不是照單全收,對於對方的思想信念要持懷疑態度,如果我都對人產生懷疑了,還稱得上是信任嗎?這裡作者沒有做更詳細的解釋。當然這可能也是對話方式的書籍所受限之處,畢竟對話類型的書比起理論類型更能讓一般人接受,但對話勢必要省略比較細節的內容了。
看完上下兩集後,我認為如果是要把阿德勒心理學當作是自己的人生觀,還有太多需要釐清之處了,而且這也會有洗腦之嫌。然而不可否認,他在百年前提出的這些思想確實有其高明之處,簡而有力地戳破我們不敢面對的一面,這些部分可以再好好理解並且善用在生活中。我們可以利用前人的思想來形塑自己的人生觀,但是不要只是照本宣科去執行,這樣就跟下集一開始的年輕人一樣會陷入苦惱之中了。
如果認為完完全全照著某個理論做就可以得到幸福,那其實不也是逃避負責任,缺乏真正去面對自己人生的勇氣呢?

簡介

GPG全名為Gnu Privacy Guard(GnuPG) ,最初的目的是為了加密通訊的加密軟體,是為了替代PGP並符合GPL而產生的。目前很多自由軟體社群要驗證身份也都會需要用到這套工具。

使用

安裝

  • 指令
    1
    2
    3
    4
    # Ubuntu
    sudo apt-get install gnupg
    # MAX
    brew install gnupg
  • GUI
    • 其實現在GUI介面都做得很好看了,而且也很容易上手,建議可以用GUI的tools。
    • MAC的GUI tools可從gpgtools.org安裝

建立key

  1. 先產生key
    • 可選擇”RSA & RSA”,key長度為4096
    • 真實姓名就填自己的英文名字,備註可填中文
    • 產生的key會放在~/.gnupg這個目錄下
    • 記得要輸入密碼,防止別人入侵系統時可以直接拿到私鑰
    • 最後會產生出user ID的hash(UID)
      1
      2
      3
      gpg --full-generate-key
      # 如果gen key發生問題,可用如下指令後再一次
      gpgconf --kill gpg-agent
  2. 接下來就是產生撤銷憑證,未來忘記密碼可以用來撤銷,因此要小心保管
    • 注意如果key有填utf-8,這步在MAC可能會出問題,不過如果是用GUI卻沒問題,原因並不清楚。
      1
      2
      3
      gpg -o revocation.crt --gen-revoke [UID]
      # 也可以直接放到.gnupg內
      gpg --gen-revoke [UID] > ~/.gnupg/revocation-[UID].crt
  3. 釋出公鑰,這個公鑰可以傳給朋友,或是上傳到server
    • -a:代表匯出明碼
    • -o:代表輸出檔名
      1
      gpg -ao mypublic.asc --export [UID]
  4. 如果是要把朋友的公鑰放入已知道人的清單
    1
    gpg --import friends.asc
  5. 可以用fingerprint顯示自已的公鑰後,弄到pdf上印出
    1
    gpg -v --fingerprint [UID]

管理key

查看、編輯與刪除key

  1. 查看目前的鑰匙
    1
    2
    3
    4
    5
    6
    # 列出所有公鑰
    gpg --list-keys
    # 同時看簽名
    gpg --list-sigs
    # 列出所有私鑰
    gpg --list-secret-keys
  2. 編輯key(對key簽名也是用同樣的方法)
    1
    gpg --edit-key [UID]
  3. 刪除已存入key的方式,如果有私鑰要先刪除
    1
    2
    3
    4
    # 先刪除私鑰
    gpg --delete-secret-key [UID]
    # 刪除公鑰
    gpg --delete-key [UID]

搜尋

  1. 首先先搜尋對象的public key
    • 這裡指定的key server是用MIT的,可以找其他也有公信力的Server,可參考wiki
      1
      gpg --keyserver hkp://pgp.mit.edu --search-keys 'Linus Torvalds'
  2. 得到對方的public key後,將其存入~/.gnupg/pubring.gpg
    1
    gpg --keyserver hkp://pgp.mit.edu --recv-keys 79BE3E4300411886
  3. 可查看與更新朋友的public key
    1
    2
    gpg --list-keys
    gpg --refresh-keys

import/export

  1. 除了搜尋以外,也可以用import/export的方式管理朋友的公鑰
    1
    2
    gpg --import public_keys_list.txt
    gpg --export -ao public_keys_list.txt
  2. import/export自己的金鑰
    1
    2
    3
    4
    5
    6
    # export 公鑰
    gpg --armor --output public-key.asc --export [UID]
    # export 私鑰
    gpg --armor --output private-key.asc --export-secret-keys [UID]
    # import
    gpg --import [金鑰]

用key傳送接收信件

  1. 假設我們要傳送secret.tgz給朋友,可以先進行加密
    1
    gpg -ear 朋友 < secret.tgz > secret.tgz.asc 
  2. 朋友收到secret.tgz.asc後可用如下指令變回secret.tgz
    1
    gpg -d < secret.tgz.asc > secret.tgz
  3. 如果要確認發信人身份
    1
    2
    3
    4
    # 先對檔案簽名
    gpg --clearsign file.txt
    # 驗證檔案身份
    gpg --verify < file.txt.asc

參考

0%