sysprog
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Linux 核心專題: 重做 lab0 > 執行人: bclegend > [專題解說影片](https://www.youtube.com/watch?v=FopiIjY-zgo) ### Reviewed by `LindaTing0106` > 參照 dudect 中函式 update_statistics() 會放棄前 10 個量測值,而不是從頭開始計算。因此在 lab0-c 中也做了對應的更改,即在進行統計分析時,同樣放棄前 10 個量測值。 dudect 中捨棄前 10 個量測值的理由為何? ### Reviewed by `Booker-Chen` 在 dudect 原始碼中的 `prepare_percentiles` 函式是將百分位數存進 `ctx->percentiles` 這個陣列,但你是把百分位數存進 `ctx->exec_times` 這個陣列,可以解釋為甚麼做這樣的改動嗎 ? ### Reviewed by `SuNsHiNe-75` > [commit ebc7886](https://github.com/sysprog21/lab0-c/commit/ebc78862d7bc8b3b680eccbd3d3439b53b15f84f) [commit 805c765](https://github.com/sysprog21/lab0-c/commit/805c765d730d66726b5058401d4b6e4b88270312) 你的 Commit message 可以寫得更好,參見 [How to Write a Git Commit Message](https://cbea.ms/git-commit/),大原則如「內文每行最多 72 字」等需特別注意。 > 收到,已更正 [Comparing changes](https://github.com/sysprog21/lab0-c/compare/master...bclegend:lab0-c:master) ### Reviewed by `lintin528` 在佇列操作的程式碼實作中,可以將 `commit` 的重點部分放到簡記裡,比對文字說明的效果會更好。 ## TODO: 重做 lab0 並彙整學員成果 > 務必依循作業規範,執行所有要求之任務 > 善用 git rebase 操作,在最新的 [sysprog21/lab0-c](https://github.com/sysprog21/lab0-c) 開發 ### 佇列操作的程式碼實作 #### q_new > [commit 1195f8a](https://github.com/sysprog21/lab0-c/commit/1195f8aa7fe77169ea438ba2d5441f843269f7e4) :::danger $\to$ [commit 6bb8bbf](https://github.com/bclegend/lab0-c/commit/6bb8bbfadb3e79d7139cf8dfb690f6891b52fd78): > Implementation of q_new > In q_new, we have to check the memory whether has been allocated correctly after we called malloc. 不是清晰的訊息,因為: 1. 標題的 `q_new` 多餘,本修改著重在鏈結串列的建立,沒必要寫出函式名稱,應描述其英語的操作 2. 內文也不用提 `q_new`,git 總是會追蹤變更的檔案內容,你該強調配置記憶體要注意什麼、在什麼時候可釋放已配置的空間,和定義「建立新的佇列」和「建立新的節點」有什麼差別 3. 之所以要寫好 git commit message,就是讓你的想法和相關舉措得以清晰地記錄下來,從而便於日後的協作。並非機械性地對程式碼變更進行逐行解釋 > <s> 收到,已改進 [commit 2cf0d43](https://github.com/sysprog21/lab0-c/commit/2cf0d439b3fd5a7a0bc61ea2d2e4df86781ecfe7) </s> > :warning: 你沒有改進!務必依循 [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) 的規範,再次 rebase! ::: > 已修正 [commit 1195f8a](https://github.com/sysprog21/lab0-c/commit/1195f8aa7fe77169ea438ba2d5441f843269f7e4) 宣告一個新的佇列並將 `list_head` 大小的空間分配給新的佇列。 由於 `malloc` 依據 C99 規格書 7.20.3 中所敘述,可能會配置失敗。 > If the space cannot be allocated, a null pointer is returned. 因此利用 `if(!q_empty)` 來判斷該佇列是否成功分配。 將佇列結構中指向下一個以及前一個節點的指標 `prev` 和 `next` 宣告在該新佇列之中。 :::danger Peter Hutterer 在 [On commit messages](https://who-t.blogspot.com/2009/12/on-commit-messages.html) 說得很精闢: > 「重新了解一段程式碼更動的脈絡很浪費腦力。雖然這件事情沒辦法完全避免,但是我們可以盡量[降低](https://www.osnews.com/story/19266/wtfsm/)這件事情的複雜度。Commit messages 正可以做到這點,而**我們可以從 commit message 看出一個開發者是否為一位好的合作對象**。」 一個專案是否能長期且成功地運作 (撇除其他影響的因素),取決於它的可維護性,而在這件事上沒有其他工具比專案本身的開發軌跡更為強大。因此花時間學習如何撰寫與維護專案的開發紀錄,是件值得投資的事。一開始你可能會覺得麻煩,但它很快就會變成一種習慣,甚至能成為你之所以感到自豪及具備生產力的因素。 Chris Beams 在 [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) 一文 (繁體中文翻譯: [如何寫一個 Git Commit Message](https://blog.louie.lu/2017/03/21/%E5%A6%82%E4%BD%95%E5%AF%AB%E4%B8%80%E5%80%8B-git-commit-message/)) 提出,好的 Git commit message 應符合七條規則: 1. 用一行空白行分隔標題與內容 2. 限制標題最多只有 50 字元 3. 標題開頭要大寫 4. 標題不以句點結尾 5. 以祈使句撰寫標題 6. 內文每行最多 72 字 7. 用內文解釋 what 以及 why vs. how ::: #### q_free > [commit d3d5e80](https://github.com/sysprog21/lab0-c/commit/d3d5e808697f7b744cc332d91b7ef017e3fc05e0) :::danger 改進你的漢語表達。 ::: 首先需要確認佇列是否存在。接著宣告指標 `entry` 用於指向當前的節點和指標 `safe` 用於指向下一個節點。利用 `list_for_each_entry_safe` 遍歷佇列中的每個節點,使用 `list_del` 刪除節點與佇列的連結,並使用 `q_release_element` 釋放該節點的記憶體。最後,在刪除完所有節點後,釋放 head 節點的記憶體。 #### q_insert_head / q_insert_tail > [commit 8c8f841](https://github.com/sysprog21/lab0-c/commit/8c8f84102ac928583f9a50639c248f9afc40617a) 為了符合 linux 的編寫風格,在佇列的頭部或尾端插入新節點可以使用 `list_add` 和 `list_add_tail` 函式。 首先需要確認佇列是否存在。接著宣告並分配一個新的 `element`,並確認分配是否成功。然後使用 strdup 將目標字元指標 s 複製到新的 `element->value` 中,並確認是否成功複製。最後,根據需要使用 `list_add` 將新節點插入到佇列的頭部,或者使用 `list_add_tail` 將新節點插入到佇列的尾端。 ```shell $ man strdup ``` > The `strdup()` function returns a pointer to a new string which is a duplicate of the string s. Memory for the new string is obtained with `malloc(3)`, and can be freed with `free(3)`. 利用 `list_add` 或是 `list_add_tail` 將 `element` 加入到佇列之中,並回傳 `true` 回報 `element` 已經加入佇列之中。 #### q_remove_head / q_remove_tail > [commit dc52bfc](https://github.com/sysprog21/lab0-c/commit/dc52bfcccb5cee04c29f384b8ed27c11988f7e08) :::danger 改進你的漢語表達。 ::: 首先,需要確認佇列是否存在且不為空。接著宣告一個新的指標 `node`,將 `head->next` 或 `head->prev` 指派給 `node`。`q_remove_head` 函式使用 `head->next` 來指定目標節點,而 `q_remove_tail` 函式使用 `head->prev` 來指定目標節點。 然後宣告一個 `element_t` 的指標 `element`,並將 `node` 的進入點指派給 `element`。接著,將該節點中的字串根據設定的長度 `bufsize` 使用 strncpy 複製到字元指標 `sp` 中,並在字串的最後加入 `\0`。最後,移除並釋放該節點。 ```shell $ man strdup ``` > The strndup() function is similar (with `strdup`) , but copies at most n bytes. If s is longer than n, only n bytes are copied, and a terminating null byte ('\0') is added. 使用函式 `list_del` 刪除該節點在佇列中的連結。 #### q_size > [commit 53e8039](https://github.com/sysprog21/lab0-c/commit/53e803927f6f7fbf88e348887756f378ab908593) :::danger 注意用語: * access 是「存取」,而非「訪問」 ::: 首先需要確認佇列是否存在。然後宣告一個整數變數 `count` 並將其初始化為 0。接著使用 `list_for_each` 函式遍歷每一個節點,每遍歷一個節點就將 count 加 1。這樣可以計算出在佇列中的節點數量。 #### q_delete_mid > [commit 05f80bf](https://github.com/sysprog21/lab0-c/commit/05f80bf8926419aecdff3c7c6f3573c5a2c97be4) :::danger 改進你的漢語表達。 ::: 首先確認佇列是否存在。接著宣告兩個指標 `move_head` 和 `move_tail`,將 `head` 指派給這兩個指標。在迴圈中,同時將 `move_head` 移動到下一個節點 `next` 以及將 `move_tail` 移動到前一個節點 `prev`。當 `move_head->next` 和 `move_tail->prev` 指向同一個位置,或者 `move_head->next` 指向 `move_tail` 時,離開迴圈。 接下來,將 move_head 移動到下一個節點,然後刪除並釋放該節點。這樣就能達成我們所期望的效果,即刪除佇列的中間節點。 #### q_delete_dup > [commit eecb514](https://github.com/sysprog21/lab0-c/commit/eecb5141f9f01b978457671874f9b6a1f115a5c0) :::danger 改進你的漢語表達。 ::: 根據 [wanghanchi](https://hackmd.io/@wanghanchi/linux2023-lab0#q_merge) 的筆記中提到的,`q_delete_dup` 函式是針對已經排序過的佇列進行刪除重複字串的節點。因此,在實作時,我們需要比較每個節點與下一個節點的字串,如果發現重複則刪除該節點。 用函式 `list_for_each_entry_safe` 可以組成巢狀迴圈來比對每個節點中的字串值是否一致,若一致則刪除該節點。 #### q_swap > [commit 00b33dd](https://github.com/sysprog21/lab0-c/commit/00b33dd3c5bc7729dcae8a365e85e02656790855) :::danger 改進你的漢語表達。 ::: 要將佇列中的節點從頭到尾進行兩兩交換,首先需要確認佇列是否存在。然後宣告一個新的指標 `node`,並利用函式 `list_for_each` 讓該指標經過佇列中的每個節點。在經過每個節點時,將該節點與其所指向的下一節點進行交換。 為了避免將佇列的頭部和尾端節點進行交換,我們需要在程式碼中加入以下判斷式:如果 `node` 或 `node->next` 為空,則跳過交換操作。這樣可以確保只進行預期的節點交換。 ```c if (node->next == head) break; ``` #### q_reverse/q_reverseK > [commit fad95c1](https://github.com/sysprog21/lab0-c/commit/fad95c14869db6eb52d091b39733d904f1dc8789) 將佇列中的所有節點進行反向操作時,使用 `list_for_each_safe` 函式會將指標 `safe` 指向 `node->next`。由於 `list_for_each_safe` 函式支持在迭代過程中移除節點的操作,因此我們選擇此函式來實現反向操作。 首先,我們需要確認佇列是否存在。然後宣告兩個指標 `node` 和 `safe`,在調用 `list_for_each_safe` 函式時,將 `node` 指向 `head->next`,並將 safe 指向 `node->next`。 接著,在每次迭代中,將 `safe` 指向 `node->next`,然後對調 `node` 中前後節點的指向。由於迭代結束後 `head` 節點沒有被修改,因此最後需要將 `head` 的前後指標進行對調。 #### q_descend / q_ascend > [commit 69ca1d1](https://github.com/sysprog21/lab0-c/commit/69ca1d1ab418e0629097af663c82184d63c11f04) ![git commit](https://hackmd.io/_uploads/SyuB2G5LC.png) :::danger 務必使用 git rebase,而非 git merge! 一旦正確使用 git rebase,就不會有 "Merge branch 'sysprog21:master' into master" 的訊息。 唯有掌握細節,方可挑戰更大的目標。你若無法從錯誤中學習,憑感覺持續盲目投入,只是徒勞。 > 收到,已更正 > ![git rebase](https://hackmd.io/_uploads/Sk-ux8nU0.png) ::: :::danger 詳閱〈[Git 教學和 GitHub 設定指引](https://hackmd.io/@sysprog/git-with-github)〉(含解說錄影),使用 git 命令的 `fetch` 操作取得最新的 [sysprog21/lab0-c](https://github.com/sysprog21/lab0-c) 變更,並用 `rebase` 操作讓自己提交的 commit 得以在 [sysprog21/lab0-c](https://github.com/sysprog21/lab0-c) 最新的變革之上。注意:你可能會遇到若干衝突,你需要自行排除。過程中,確保符合〈[How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/)〉規範,你或許會需要用 `git rebase -i` 命令來改進,最後用 `git push --force` (注意: 操作務必謹慎) 公開發佈於 GitHub。 > `git rebase` 示意: ![image](https://hackmd.io/_uploads/HkkxHWE6T.png =80%x) ::: ### 研讀 [lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) 並完成 q_sort :::danger 改進你的漢語表達。 ::: list_sort.c 是 Linux 核心中實作的 merge sort 檔案。在執行 `list_sort` 函式時,會先將環狀雙向鏈結串列轉換為 null-terminated 的單向鏈結串列,並在排序結束後使用 `merge_final` 函式將其轉換回環狀雙向鏈結串列。 ```c __attribute__((nonnull(2,3,4))) ``` > **5.25 Declaring Attributes of Functions** > The keyword `__attribute__` allows you to specify special attributes when making a declaration. 根據 [A GNU Manual](https://gcc.gnu.org/onlinedocs/gcc-3.4.3/gcc/Function-Attributes.html) 中所敘述在函式宣告時,檢查在位置 `(2,3,4)` 輸入的參數是否為 `non-null` 的指標。 ### 閱讀論文 〈[ Dude, is my code constant time? ](https://eprint.iacr.org/2016/1123.pdf)〉 並改進 dudect 論文中提到,評估一個實作是否以恆定時間運行並非易事,即使是在理論時間複雜度為 O(1) 的實作中也是可能存在 timing leakage 。 現有的幾種檢測變時性代碼的工具大多需要某種方式的硬件建模,然而這非常困難,因為 CPU 製造商對 CPU 內部運作原理的 <s>信息</s> 公開很少,且這種行為可能會因微碼更新而改變。因此論文中介紹了一種名為 dudect 的工具,用於檢測某段代碼是否在給定平台上以恆定時間運行。 論文中解釋了 time leakage 檢測測試的實作,首先測量兩個不同輸入數據類別的執行時間,然後檢查這兩個時間分佈是否在統計上存在顯著差異。在進行執行時間的量測時,leakage 檢測技術針對兩個不同輸入數據類別進行測量。其中,一種被廣泛應用的測試方法是 fix-vs-random 測試。在這種測試中,第一類輸入數據被固定為一個常數值,而第二類輸入數據則每次測量時隨機選擇。而根據論文中所描述,以上兩組測試數據所得到的時間分佈應該是相等的。 :::danger 探討更多該論文的議題,不要只做摘要! ::: #### 實驗方法 量測方法: 使用 fix-vs-random 的方法進行量測。具體來說,將第一組輸入設定為固定值,而第二組輸入則會根據每次的量測隨機設定。 後處理: 在進行統計分析之前,需要對每次的量測結果進行後處理。由於大部分的量測值集中在較小的執行時間,但也有部分量測值出現特別高的執行時間,這可能導致時間分佈呈現正偏態。因此,需要對結果進行篩選或取捨,以確保統計分析的準確性。 :::danger 一旦進行後處理,是否會影響統計模型? ::: >會,根據論文 〈[ Dude, is my code constant time? ](https://eprint.iacr.org/2016/1123.pdf)〉 中所描述,若少了對 t-test 資料的**前**處理,則 t-test 將只能檢測 first-order timing information leakage > >A simple statistical test to use and implement is Welch’s t-test. This statistic tests for equivalence of means, and hence failure of this test trivially implies that there is a ==first-order timing information leakage==. Note that when a t-test is coupled with cropping pre-processing, one is no longer testing only for equality of means ==but also for higher order== statistical moments since the cropping is a non-linear transform. >〈[ Dude, is my code constant time? ](https://eprint.iacr.org/2016/1123.pdf)〉 :::danger 翻閱統計學教科書,確認上述說法。 ::: 統計測試: 使用 [Welch's t-test](https://en.wikipedia.org/wiki/Welch%27s_t-test) 來測試兩組數據的時間分佈是否相等(例如,平均數是否相同)。如果檢驗結果顯示不相等,則可能意味著存在時間洩漏。 :::danger 先做數學分析,不要急著寫程式。 ::: #### 數學分析 #### 實作 >[commit f5c709b](https://github.com/bclegend/lab0-c/commit/f5c709b5f6698d313a15a2a3960bb294982edf3b) 在 lab0 的實作中加入 leakage detection test 來測試程式是否為 constant time 。 閱讀 [dudect](https://github.com/oreparaz/dudect/blob/master/src/dudect.h) 程式碼可以發現,函式 `dudect_main` 與 `lab0-c/dudect/fixture.c` 中的函式 `doit` 相對應,因此在函式 `doit` 中加入函式 `prepare_percentiles` ```diff bool ret = measure(before_ticks, after_ticks, input_data, mode); differentiate(exec_times, before_ticks, after_ticks); + prepare_percentiles(exec_times); update_statistics(exec_times, classes); ret &= report(); ``` :::danger 區分「函數」和「函式」,見: https://hackmd.io/@sysprog/it-vocabulary ::: 函式 `prepare_percentiles` 主要操作的是使用函式 `qsort` <s>函數</s> 對 `exec_times `進行排序,迭代每個測量點,計算其對應的百分位數,並將結果儲存 <s>存儲</s> 回 `exec_times` <s>數組</s> 陣列中。 :::danger 注意用語! ::: ```c static int cmp(const int64_t *a, const int64_t *b) { if (*a == *b) return 0; return (*a > *b) ? 1 : -1; } static int64_t percentile(int64_t *a_sorted, double which, size_t size) { size_t array_position = (size_t) ((double) size * (double) which); assert(array_position < size); return a_sorted[array_position]; } /* * set different thresholds for cropping measurements. * the exponential tendency is meant to approximately match * the measurements distribution, but there's not more science * than that. * Reference : https://github.com/oreparaz/dudect/blob/master/src/dudect.h */ static void prepare_percentiles(int64_t *exec_times) { qsort(exec_times, N_MEASURES, sizeof(int64_t), (int (*)(const void *, const void *)) cmp); for (size_t i = 0; i < N_MEASURES; i++) { exec_times[i] = percentile( exec_times, 1 - (pow(0.5, 10 * (double) (i + 1) / N_MEASURES)), N_MEASURES); } } ``` 參照 `dudect` 中函式 `update_statistics()` 會放棄前 10 個量測值,而不是從頭開始計算。因此在 lab0-c 中也做了對應的更改,即在進行統計分析時,同樣放棄前 10 個量測值。 :::danger 探討此操作在統計的意義。 ::: ```diff static void update_statistics(const int64_t *exec_times, uint8_t *classes) { - for (size_t i = 0; i < N_MEASURES; i++) { + for (size_t i = 10; i < N_MEASURES; i++) { ``` ### 利用 valgrind 分析記憶體使用情況 ### 整合網頁伺服器 :::danger 務必依循作業規範,執行所有要求之任務。 注意細節! 整個學期過去,我看不到你的成長,為何如此?說好的翻身呢? :::

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully