Ubuntu Firefox 無法播放 Youtube 影片

問題

  • 作業系統: Ubuntu 18.04 LTS
  • Firefox version: 75.0

只有在看直播影片時會有以下錯誤,但聊天室正常運作:

由於這是目前最新版的 Firefox,所以是支援所有格式的。因此在 https://www.youtube.com/html5 測試得到成功訊息:

解決

剛開始沒加上 Ubuntu 這個關鍵字,因此一直繞在設定 about:config 的內容或者是安裝 flash 外掛程式,但兩者都無法解決問題。

加上 Ubuntu 搜尋後就有答案啦 QQ

安裝 ubuntu-restricted-extras 套件,此套件含有:

  • Support for MP3 and unencrypted DVD playback
  • Microsoft TrueType core fonts
  • Flash plugin
  • codecs for common audio and video files

$ sudo apt-get install ubuntu-restricted-extras

安裝時需要閱讀並同意 License,都確認後就等待安裝。

最後再重啟 Firefox,就可以看直播啦!

References:

GitHub - Sign Commits With Your GPG Key

Why? Security!

GitHub 是知名的版本控制平台,也是開發者們的大寶庫,可以在上面找到各式各樣的開源專案,學習應用兩相宜。當然也有很多社群甚至公司也採用 GitHub 進行專案開發,但除了基本的 Git 操作,其中的安全議題是常常為人所忽略的。

我也是在聽完公司同仁分享才開始重視這個議題,於是終於啟用 GPG key 簽署 commit,也啟動 2FA 的驗證。

Enable 2FA

GitHub 的登入就如一般的服務,只要輸入帳密即可操作該帳號的專案,然而一般人總懶的時時更換密碼,也因此容易成為安全漏洞,若取得帳戶便能肆意修改專案。因此啟動 2FA 兩階段驗證便是一道重要的防火牆,然而這也只是最基本的第一步。

Enable SSH key

透過 SSH key 可以有效的管理並限制哪些授權裝置能使用自己的帳號發布、修改、刪除專案等操作。

Enable GPG key

有了上述兩個設定,為什麼需要 GPG key 進行 commit 簽署呢?原因也很簡單,就是要確保此 commit 真為本人所提交,讓其他使用者確信這項修改或 release 來自可信賴的源頭。在同事的簡報中,他特別點出若是專案中有牽涉到與加密相關的操作就更應該使用 GPG key 簽署。簽署後的 commit 在 Github 上會顯示 Verified,如附圖:

接下來就引導讀者們如何使用 GPG key 簽署 commit! 我的環境是 Ubuntu 16.04 & Ubuntu 18.04

依照以下步驟:

  1. 安裝 gnupg2
  2. 產生 GPG key
  3. 登錄 GPG key
  4. 本機端的 Git 設定
  5. 簽署 commit

Install gnupg2

gnupg 是用來生產 GPG key 的套件。在 Ubuntu 16.04 中已預設裝有 gnupg 1.4.20,但 GitHub 的官方教學建議我們使用 v2 以上,因此需要安裝 gnupg2,版本為 v2.1.11 :

sudo apt install gnupg2

Ubuntu 18.04 的預設 gpg 已是 v2 以上,可以跳過這步驟

Generate a GPG key

Ubuntu 16.04:

$ gpg2 --full-gen-key

Ubuntu 18.04:

$ gpg --full-generate-key
  1. 接著會跳出 cli prompt 詢問要產生哪種金鑰,可以直接按 Enter 鍵選擇 RSA and RSA (default)
  2. 金鑰長度,必須要是 4096 bits 以上,因此輸入 4096
  3. 選擇金鑰的有效期限,輸入 Enter 以選擇 key does not expire 
  4. 確認選擇,輸入 y
  5. 輸入真實姓名
  6. 輸入 email,此 email 需在 GitHub 網站上驗證過
  7. 輸入 comment,可以留空,直接按 Enter 進行下一步
  8. 確認已輸入的個人資訊,輸入 O 以確認
  9. 輸入 secure passphrase,進行簽署時需要輸入這組密碼
  10. cli prompt 會請使用者隨意按鍵會移動滑鼠以幫助取得 entropy,這是產生隨機數的重要元件。可能會遇到電腦在這關卡太久,因為產生的 entropy 太少,可以透過指令查看 entropy 的數目:
$ watch -n1 cat /proc/sys/kernel/random/entropy_avail

並透過安裝 rng-tools 加速亂數產生。我一裝完也就產生完了 XD

$ sudo apt install rng-tools
  1. 簽章測試,測試是否能用以產生的 GPG key 簽名
$ echo "test" | gpg --clearsign
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

test
-----BEGIN PGP SIGNATURE-----
....
-----END PGP SIGNATURE-----

若沒辦法進行簽章可能要先升級套件:

$ sudo apt upgrade gpg

Register GPG key on GitHub

  1. 先透過指令查看金鑰資訊:
$ gpg --list-secret-keys --keyid-format LONG
/home/userID/.gnupg/pubring.kbx
------------------------------------
sec   rsa4096/3AA5C34371567BD2 2016-03-10 [SC]
uid                          Hubot 
ssb   rsa4096/42B317FD4BA89E7A 2016-03-10
  1. 複製 keyID, 上面的例子 ID 為 3AA5C34371567BD2
$ gpg --armor --export 3AA5C34371567BD2
# Prints the GPG key ID, in ASCII armor format
  1. 產生 key block,整段複製(包含 -----BEGIN PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----)
  2. 進入 Github 的頁面 settings -> SSh and GPG keys
  3. 點選 New GPG key,貼上 3. 所複製的 key block
  4. 輸入 GitHub 密碼確認
  5. 完成!!

GPG key settings first

  1. 之後提交 commit 時使用某把 key 進行簽署:
$ git config --global user.signingkey <keyID>
$ git config --global commit.gpgsign true   // optional, 每次 commit 都要簽名
$ git config --global gpg.program gpg2       // Ubuntu 16.04 only, 使用 gpg2

 Sign your commit!

一行指令,非常簡單! -S 就是簽名 commit

$ git commit -S -m "success!"
$ git log --show-signature -1    // 查看剛簽章的 commit

References:

IOTA Foundation Research Intern (Remote) Interview

動機


近兩年跟著 jserv 老師還有一群 成大分散式帳本實驗室BiiLabs 優秀的夥伴研讀 IOTA Tangle 技術,並做了幾個 side project。尤其現在研究所論文也圍繞在 IOTA 之上,因此非常希望能有進入 IOTA Foundation 直接與 research team 共事的機會,或許也能搶先一步知道許多還未對外明說的技術細節 XD

歡迎關注我們:

履歷準備

履歷 link

  • page 1: 摘要
  • page 2: Appendix, 專案細節

 

比賽經驗

  • 2018 金矽獎應用組優勝

projects

  • 著重寫所有跟 IOTA 有關的 projects
  • 當然成為 entangled project (IOTA Foundation 底下的專案) 的 contributor 也要寫進去的呀!

面試準備

由於是全英面試,特別緊張,感謝我一直以來的優秀戰友 王贊鈞 跟我相互練習了兩次。相互練習真的收穫良多,除了確認自己的回答對方是否聽的懂外,也能就自己的履歷挑出可能的問題,再接著擬定對答策略

 

面試時程

投履歷

  • 2 月初得知實習職缺
  • 2/20 投遞履歷

在 application form 中有一欄是填寫可以面試時間,投遞後過了兩週遲遲沒消息就死心了,5 月底收到面試通知嚇一大跳

 

一面

  • 5/24 收到一面通知
  • 5/28 一面

一面是與在德國的 HR 面試,整個過程約 20 分鐘,氣氛非常輕鬆。當她注意到我是女性時非常感動,高興了 30 秒後開使用中文跟我對談

OAO!!!!!!

聊了 3 分鐘才切回正題並切回英文模式。

問題:

  • 對這個職缺的看法,想要做什麼?學什麼?
  • 薪資的要求?
  • 任期的規劃?

接著簡單說明台灣如何聘顧,然後合約內容等。
最後就是我提問了 remote 工作模式、這個職缺會招募多少人,也詢問什麼時候能得知這輪面試結果,結果 HR 直接給了我肯定答覆 XD

 

二面

  • 6/10 收到二面通知
  • 6/28 二面

二面由在英國的 research team director 進行面試,原先他指定在 6/14 下午進行面試,但因為時間不能配合,我回信提出要更改時間,並註明平日可以進行面試的時段。

結果遲遲未收到回信,這中間我又多回了一封信表明真的非常希望有機會能跟面試官進行面試,最終在 6/19 收到回信 QAQ

問題:

  •  請稍微自我介紹或說明你的背景
  • 目前碩士的學程?
  • 英國的碩士有分研究導向和選修課程導向的
  • 選修過哪些課程?
  • DLTCollab 都做些什麼?
  • 你熟悉的語言是 C/C++,那有接觸過 python 嗎?
  • 關於 Tangle 你的了解是?
  • 稍微說明什麼是 Coordicide
  • 薪資要求?

這些問題都不難,但過程中面試官有提到一兩個名詞我不了解,還好他很有耐心的再解釋一次,我才接著回答。除此之外,對話的時候也會卡住,不通順 QQ
英文還是要好好精進啊~~

最後一樣是我提問,關於 remote 工作模式如何追蹤工作進度、實習生會負責哪些專案、mentor 制度

最後他提到再下一週末會收到 HR 通知信,會再召集所有 candidate 有一次咪聽,然後就結束了~

心得

回顧面試經過,有些意外的是沒有問比較深入的 Tangle 技術細節,也沒有考資結或 coding 題。
但英文面試令人很緊張,最重要的是聽懂問題,最忌答非所問,其實慢慢回答,盡力完整表達的內容就好。當然還是期許自己能通順對話,用字精準,因為未來要用英文溝通的機會還很多啊 !


最後,非常感謝 jserv 老師幫我修改履歷,且一直以來推坑我做這做那的。在老師的眾多優秀學生中,我是一個沒什麼想法、不突出、不太認真、打雜還可以的超普通學生,就憑一股莫名其妙的衝勁,硬著頭皮做老師提到的比賽、專案。透過這次機會才發覺原來已經不知不覺累積一些成果及經驗,更結識了很多優秀的大神們。

Ubuntu 18.04 安裝 Visual Studio Code

在 18.04 上安裝 VS Code 並不如先前版本一樣一行指令順利安裝,而是會再需要安裝相依性的套件,其實跟著提示的建議指令就能順利裝起來了。所以不需要緊張 XD

1. 下載 VS Code 安裝包
官網載點選擇 .deb 下載
2. 安裝指令
打開終端機後進到 .deb 的目錄下,執行

$sudo dpkg -i [vs code 檔名].deb

3. 相依性套件
接著就會出現許多相依性套件為安裝的訊息,根據提示指令下載相依性套件:

$sudo apt install [欠缺的套件]

也有可能再出現相依問題,因此再接著依提示輸入

sudo apt --fix-broken install

最後重行裝回 vs code 所欠缺的套件

$sudo apt install [欠缺的套件]

4. 成功安裝 VS Code
解決相依性套件的問後,進行 vs code 安裝就完成囉!

$sudo dpkg -i [vs code 檔名].deb

雙系統 -- Windows 7 & Ubuntu 18.04 LTS

這幾天拿到了朝思暮想的 ThinkPad X220,最吸引我的是它的鍵盤跟招牌小紅點,其次是大小及重量,當然 ThinkPad 還有一些機制是我想試的。

本來就習慣了雙系統的環境,就選用的最近出來的 Ubuntu 18.04 LTS 安裝!順利安裝後卻遇到了 grub 始終抓不到 windows 7 的開機選項,以下提供解法。

Ubuntu 18.04 LTS 系統安裝流程

安裝流程其實十分簡單,簡單歸納以下:
  1. 製作 LiveUSB 或燒錄開機碟
    1. 先至 Ubuntu ISO 下載頁面 下載 ISO 檔
    2. 右鍵點選 ISO 檔燒至光碟 or
    3. 利用 Rufus 等工具製作 LiveUSB
  2. 磁區分割
    • 先在 windows 中分割安裝 Ubuntu 的磁區 or
    • 直接利用製作好的 LiveUSB 或光碟選擇 Try Ubuntu,利用 GParted 切割
  3. 關閉 fastboot
    • 這部在我原本的筆電 (ASUS) 才需要做,否則無法進入 BIOS
  4. 進入 BIOS
    • X220: 將 UEFI 的開機選項調為優先(預設是 legacy 優先),儲存離開
    • ASUS: 將 CMS 調整為 enable ,並在 boot 分頁中選擇 UEFI 光碟或 LiveUSB 開機
  5. Ubuntu installer
    1. 按照指示選擇語系、鍵盤、時區等
    2. 最後一個安裝選項選擇「其他」以進行進一步的磁區分割 /, /home 等
  6. 等待

GRUB menu 沒有顯示

  1. 進入 Ubuntu 並打開終端機
  2. 指令:`sudo gedit /etc/default/grub`
  3. 修改成以下:
    #GRUB_HIDDEN_TIMEOUT=0
    #GRUB_HIDDEN_TIMEOUT_QUIET=true
    GRUB_TIMEOUT_STYLE=menu
  4. 存檔離開,下指令 `sudo update-grub` 更新設定
  5. 重開機測試

GRUB 找不到 Win7

由於我使用的 Win7 pro 沒有支援 UEFI 開機,而 Ubuntu 18.04 LTS 與 GRUB 的預設都是使用 UEFI,因此才造成 GRUB 遲遲抓不到。但這個情形在新的  Win7/8/10 就不會出現,因為他們兩種開機模式都支援。

因此我們切換開機模式為 legacy 就解決問題了!

步驟如下

  1. 依照下列指令安裝 boot-repair 工具,進行修正:
1
2
3
4
5
sudo apt-add-repository ppa:yannubuntu/boot-repair

sudo apt-get update

sudo apt-get install -y boot-repair
  1. 下指令 boot-repair ,開啟 GUI 介面後點選 >進階選項
  2. 在 「GRUB 選項」分頁中,勾選 「使用 legacy GRUB」的選項
  3. 重新開機後進入 Ubuntu 打開終端機 (這時 Win 7 還未出現,別緊張

輸入指令:sudo update-grub

列出的選項中就會有 Win 7 囉!

  1. 重新開機就選擇 Win 7 試試囉!

參考資料:

噢!我的編譯器 – Lexical Analysis Detail

大致上了解 lexical analyzer 做了什麼之後,我們可以再更進一步看細節:

  • 字串怎麼讀?
  • 字串怎麼切?
  • token class 怎麼定義?

字串怎麼讀?

當專案很大,程式碼很多時,讀取速度當然就愈慢 。因此,讀程式碼時就不是一個字元一個字元慢慢讀了,而是一次抓取多個字元放進緩存的 buffer 中。畢竟和 disk 拿取資料, 系統呼叫的成本是很高的呢!於是每次拿取的資料的單位就與 disk block 大小一致,如果檔案沒那麼大的話,就會在最後面加一個特殊的結尾字符,一般稱之為 eof (end of file)。

所以當遇到 eof 時,就知道要更新 buffer,拿下一段的程式碼了。

字串怎麼切?

有了 input stream 後,要切。但怎麼知道這一刀要下在哪裡?怎麼記住每一段的範圍呢?

龍書中提到使用兩個標記標住起點與終點:lexemeBegin, forward。

想法很簡單:

由 forward 不斷前進,找到符合規則的 token 後就記錄下來,更新 lexemeBegin,然後再重複直到結束。

token class 怎麼定義?

這個部份就是利用 lex 或 bison 實做 scanner 的重頭戲。如何訂一條規則,描述出這個 token 的 pattern 呢?

這就需要 RE (regular expression) 出場了!所謂的 regular expression 其實就是透過定義一些符號試圖精簡的表示以個語句的規則,這個的應用場合非常廣泛,如在很多「搜尋」的場景都能夠使用 RE 過濾出目標。

詳細的 RE 語法可以參照 正規表達式 Regular Expression

在龍書中還提到了在建構 lexical analyzer 時,會先把 regular expression 轉換成狀態轉移圖表示 (transition diagram),為的是能建構一連串的判斷動作,如圖:

unsigned number transition diagram
unsigned number transition diagram

每一個原圈代表一個狀態,當走到雙圈時就代表最終狀態且是被接受的!而每一條箭頭稱為 edge,代表在狀態 a 且遇到 edge 上的字元時前進到箭頭指向的狀態 b。例如:在 state13 時,遇到 E 即前進到 state16。

也就是說每一個 token 都會轉成不同的 transition diagram,我們便可以把這張圖轉換成以 switch case 的程式碼,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TOKEN getRelop()                        // TOKEN has two components
TOKEN retToken = new(RELOP); // First component set here
while (true)
switch(state)
case 0: c = nextChar();
if (c == '&lt;') state = 1;
else if (c == '=') state = 5;
else if (c == '&gt;') state = 6;
else fail();
break;
case 1: ...
...
case 8: retract(); // an accepting state with a star
retToken.attribute = GT; // second component
return(retToken);

 

沒走到雙圈的最終狀態 == fail ?
這答案是不夠全面的,沒有走到雙圈並不代表這個字串是有誤,而是他不屬於這一個 token class 的。

因此會繼續以走下一個 token class 的 transition diagram,直到最終狀態。如果某個字串它能多個匹配不同 token class 的話,便會歸類給最先走成功的 token class。

真正的 fail 是在於當所有 transition diagram 都走完且沒有任一匹配的時候,這時候 lexer 就會直接噴錯囉!

代表我的兩個 Double

前些天嵌入式課程中的考題十分有趣,利用了 double 轉成有意義的字串。

這在某些應用場景十分有用,可以單純使用數值表示字串,甚至透過一些數值運算可以更改字串內容。

題目出處:week5 隨堂測驗(中)

以下是能夠印出我 ID 的 double,是隨堂測驗中的延伸題。

output: jkrvivian

1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
double m[] = { 1.7899031678214814931707136432e161,
5.43472210425371198594225672155e-322 };
puts((char *)m);
}

噢!我的編譯器 - Lexical Analysis Overview

前一篇 Introduction II 中我們提到編譯器主要的工作就是進行 分析生成,而又可以再把工作細分為以下,本篇要討論的就是分析的第一步驟: 詞彙分析器 lexical analyzer

分析 (analysis):

  • 詞彙分析器 (lexical analyzer),亦稱 scanner,建立 symbol table
  • 語法分析器 (syntax analyzer),亦稱 parser
  • 語意分析器 (semantic analyzer)
  • 中間碼產生器 (intermediate code generator)
  • 程式碼最佳化 (code optimizer) (optional & machine-independent)

Lexical analyzer 做了什麼?

lexical analyzer 是第一個拿到原始碼的,那它要做什麼呢?

就是判斷詞性囉!除了斷詞還需要做分類,那怎麼樣叫做斷詞呢?

舉個簡單的例子,a = b + 2;,我們可以拆成以下,也就是進行斷詞

  • a
  • =
  • b
  • +
  • 2
  • ;
除了會除去空白,還有 Tab 鍵、註解、換行符號等等。另外,也同時根據換行符號得知行號,以做錯誤訊息與程式碼的對應。那分類呢?請見以下:
  • a : identifier
  • = : assign
  • b : identifier
  • + : plus
  • 2 : number
  • ; : semicolon
而遇到 identifier 時,就會把相關資訊插入 symbol table,所以現在 symbol table 會有 a, b, c 的資訊。

以上就是 lexical analyzer 所做的事情,接下來便會把整理好的資訊與 symbol table 提供給下一個 syntax analyzer 使用。

token, pattern 和 lexeme

有了基本概念後,先介紹三個名詞:
  • token : 類別及其屬性,以 <token name,  optional attribute> 表示,如 a 就是 <id,pointer to symbol-table entry for a>
  • pattern : 每個類別的格式,如被歸類到 number 這個類別底下的都是數值格式,而 literal 就是被 "" 所環繞的字串
  • lexeme : 類別底下的斷詞,如上述的 id 底下有 a, b, c 三個 lexeme
token 跟 lexeme 的解釋很相像,可以用 class 跟 instance 的關係更清楚的說明。token 就像是 class,token name 就是 class 的名稱。而 lexeme 就像是 instance,同 class 可以創造出許多不同的 instances ,同理,同一個 token 可以有很多 lexeme,而這些 lexeme 的不同之處就是 optional attribute,但他們都符合這個 token 的 pattern。當然這種種資訊都會被紀錄在 symbol table 中,以便後面的人接續使用。

看看下表更清楚了:

token pattern sample lexeme
comparison < or > or <= or >= or == or != <=, ==
id 字母後面跟著字母或數字 pi, abc, tmp1
number 數值常數 3.14159, 123
semicolon ; ;

Lexical errors

在 lexical 階段除錯是困難的,因為只單純的讀進程式並進行分類。因此,這裡除的錯是 拼字的錯,也就是沒有任何一個 token 的 pattern 符合這筆輸入時就是錯誤。那 lexical analyzer 如何處理這個錯誤呢?

龍書裡頭提到幾種修正辦法:

  1. 刪掉已讀進的字元,直到剩餘字符合某 token 的 pattern
  2. 僅刪掉一個字元
  3. 插入缺少的字元
  4. 替代某個字元
  5. 交換某組相鄰字元的順序

當然,最單純的方法就是以 一種 修正辦法改正目前讀入的字串,以得到符合規範的 lexeme。但最常見的方法其實是依據整份原始碼進行修正,使得全部都是合法的 lexemes,這個實作上也就複雜的多囉!

噢!我的編譯器 - Introduction II

在上一篇 Introduction to Compiler - I 中簡述了編譯器的工作以及編譯系統的流程,接著我們將更深入的打開 compiler 的盒子,看看它到底是經過了哪些處理完成編譯的!

The Structure of a Compiler

龍書裡面提到,我們可以將編譯器做的事情分為兩階段:

  1. 分析 (analysis): 又稱為 compiler 的 **前端處理 (front-end)**,分析與解構原始碼,並將資訊整理成 中介表示 (intermediate representation)符號表 (symbol table) 傳給下一階段,當中如果發現任何錯誤就會噴錯誤訊息
  2. 生成 (synthesis): 又稱為 compiler 的 **後端處理 (back-end)**,根據符號表與中介表示產出目標程式碼

套入翻譯的時候大腦快速運轉的流程也是說的通的,總是得對 A 語言的句子進行分析才能夠生成 B 語言的句子啊!例如,這幾學期學得很痛苦的法文 XD

中文的「我起床了」是一句多麼直覺且精簡的表達一個動作,句型就是「主詞 + 動詞 + 語末助詞」,其實翻成英文也十分簡單,”I got up.”,一樣的句型構成,但有了過去式,畢竟起床這事兒已經發生了,反觀法文…….

法文文法書有一課提到「反身」,相當於中文的「我自己」、「你自己」還有英文的 yourself、myself 。但法文不太一樣,有些動詞必須要用反身的句型,例如起床、洗臉、梳頭,因為是「我自己起的床」、「我自己梳的頭」。所以同樣的一句話在法文是 “Je me lève.” 而不是 “Je lève.”,因此看到這句絕對不要翻成「我自己起床」,而是「我起床了」,否則其他人一陣嘲笑就是你的錯誤訊息囉!
所以,勢必得對句子進行單字及文法 分析,我們才能 生成 準確的翻譯囉!學了他國語言總是覺得中文最簡單,但法文特別虐腦倒是真的 QAQ

當然,我們還可以再把編譯器的工作從這兩大點再細分出來:

  1. 分析 (analysis):
    • 詞彙分析器 (lexical analyzer),亦稱 scanner,建立 symbol table
    • 語法分析器 (syntax analyzer),亦稱 parser
    • 語意分析器 (semantic analyzer)
    • 中間碼產生器 (intermediate code generator
    • 程式碼最佳化 (code optimizer) (optional & machine-independent)
  2. 生成 (synthesis):
    • 程式碼產生器 (code generator)
    • 機械碼最佳化 (machine-independent code optimizer) (optional & machine-independent)

流程圖如下:

因為 symbol table 在各個步驟都會使用到,因此把它獨立畫出來

compile 的步驟

先有個概觀,接下來我們就要把焦點移到各個步驟囉!

噢!我的編譯器 - Introduction I

前言

趁著這個寒假再趕緊複習近一年前選修過的 compiler,除了釐清整個架構流程與觀念,也期望能有對應的實做練習。其實只是下學期要當助教,得把這些東西撿回來 XD

之後的整理皆基於:

  • 龍書 (Compilers principles, techniques, and tools)
  • Stanford CS143 的開放式課程
  • 自己的想法

希望能夠闡述的更輕鬆且容易理解,大致上就這樣,我們開始吧!

Introduction I

其實編譯器的角色套用在現實生活中能夠特別清楚的解釋他的角色及功用 – 翻譯人員。不論是即時口譯還是各節目或動畫的專業字幕組都是,反正只要始能把 A 語言流暢的翻成 B 語言的都算。但翻譯的工作並不是那麼簡單,需要理解某語言的生字、語法才能夠進行,當然更專業的人員還能夠使用精簡的句子傳達意境,甚至即使他文法錯了,翻譯大大也能理解並精準的譯出正確的意思。總之,翻譯其實不僅僅是「翻譯」,還要再經過「編輯」,這也就是 “compile” 「編譯」的意思。

編譯器 Compiler

了解之後,我們再把視角拉回 compiler 上,compiler 就是程式語言間的翻譯員,不同的 compiler 會編譯成不同的語言,有可能是轉成機器語言(machine code)、byte code,甚至是另一種語言,如圖:

編譯器 compiler

產生出來的 target program 就能夠執行囉!所以程式的編譯到執行是這樣的:

從原始碼到編譯執行

直譯器 Interpreter

還有另一種語言處理的工具:直譯器 (interpreter)。相較於上圖,compiler 是編譯程式碼後產出可執行的程式碼,由使用者輸入 input 後,再得到 output。而直譯器是 source code 與 input 一次給,直接編譯並執行,產出 output ,而使用直譯器的語言有大名鼎鼎的 python。而的架構圖如下:

直譯器 Interpreter

而 compiler 與 interpreter 還有速度上的差異,compiler 產生的 target program 執行的比 interpreter 快。但 interpreter 的偵錯又比較好了,畢竟是一行行程式碼檢查與執行下來。

compilation + interpretation

再來,勢必要提及赫赫有名的 Java,為什麼呢?Java 是一個結合 compilation 和 interpretation 的程式語言,這是什麼意思?就是 Java 會先編譯成一種編碼稱為 byte code,接著再直譯成機器碼。這個的好處是,Java 經過一次編譯,就可以透過虛擬機 (virtual machine) 在不同的機器上直譯執行。沿用翻譯員的例子,byte code 就有像是目前的國際語言英文,只要 A 語言翻譯成英文,到 B 國人人都能直接把英語翻譯成自己的語言(當然前提是大家都會英文),因此到任何國家都能夠溝通了!整個架構可以繪製成下圖:

hybrid compiler

編譯系統 Compilation System

了解編譯器的角色與功能後,我們可以接著探討編譯系統是由哪些角色所組成的。

  1. 預處理 (preprocessor): 負責在引用 header file、libraries 的檔案插入完整的程式碼,以 C 為例就是根據 # 開頭的指令,e.g., #include stdio.h,直接插入 stdio.h 的內容。
  2. 編譯器 (compiler): 把重新編輯好的檔案再轉交給 compiler 編譯成組合語言 (assembly language),簡稱組語。而使用組語的最重要的原因是不同的高階程式語言都可以組譯成相同的組語,也比機器碼還好 debug。 p.s. 組語是因硬體架構而異。
  3. 組譯器 (assembler): 將組語轉譯為**機械碼 (machine code)**,並打包成重新定位的 **目標檔 (object file)**。
  4. 連結器 (linker): 負責合併所有 object files 並產生可執行的檔案,可以被加載到記憶體執行。

我們再把流程畫成下圖:

編譯系統

到目前為止,我們對編譯器以及編譯系統就有初步的了解了!