GOGOGOGOGOGOG
    • Create new note
    • Create a note from template
      • 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
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me 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
    • Save as template
    • 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 Create Help
Create Create new note Create a note from template
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
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me 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
    # 期末考筆記: --- ## 中斷 Interrupt 當周邊(peripheral)或是硬體需要處理器(processor)做某些特殊處理時,會發現以下事件: - 周邊觸發一個中斷請求給處理器 - 處理器把目前處理的`task `停滯起來 - 處理器執行中斷的ISR並且將中斷請求清空 - 處理器回復成之前的`Task` 在任何的Cortex M上面都有`NVIC ` Nested Vectored Interrupt Controller ,其目的是為了對中斷進行管理。 以下圖來說: ![](https://i.imgur.com/VLiKcSo.png) ![](https://i.imgur.com/KJXQR0z.png) - `NVIC ` 會接受來自周邊的中斷請求。 - 為了回復到被中斷的程式碼中,`the exception sequence `中斷程序會把進入目前階段的程式碼儲存起來,以便用來在完成中斷服務程序後,系統可以回復到執行中斷程序前。 - 在Cortex M的處理器中某些暫存器會把目前的資料存到`stack `裡面,當`exception `發生時,處理器會根據`exception return sequence `的訊息回到相對應的程式碼中。 ## exception return + 為了在中斷後 return 回原先的 function 必須有儲存原先執行狀態的機制 + 當中斷發生時系統會備份caller saved register: + `r0`~`r3`, `r12`, `LR`, `PSR`,還有目前程式備份的address,此稱作stack frame。stack pointer便會開始更新 - 功能: EXC_RETURN的功能是,他要記錄你在發生exception前你的sp是位在psp還是msp,且你是在handler mode 還是 thread mode,以便得知exception return時應該有的行為。 exception return的條件: - 在Handler mode發生,且要使PC被設為EXC_RETURN。 將PC設為EXC_RETURN是為了要讓exception得的機制知道exception完成了, 藉由偵測31~4bits, 當全為 f 時,processor會detect到他不是一個一般的branch,並且會開始執行exception return的部分。 這張圖很清楚的說明exception return做了甚麼事: ![](https://i.imgur.com/HbnnFgS.png) ![](https://i.imgur.com/7pIKXcF.png) - ****List of System Exceptions**** ![](https://i.imgur.com/kzn8yfw.png) - List of Interrupts ![](https://i.imgur.com/a1MRnsr.png) # Vector table : [中斷向量表](https://blog.xuite.net/embeddedsystem_book/twblog/174144583-%E4%B8%AD%E6%96%B7%E5%90%91%E9%87%8F%E8%A1%A8%E6%B7%BA%E8%AB%87) ![](https://i.imgur.com/PTKnOMS.png) CPU(或中斷控制器)會為每一個中斷編一個序列號,而中斷可以分為兩個種類,一種是CPU自行產生的內部中斷(又稱例外 - exception),以及由周邊設備產生的外部中斷。而vector table便是紀錄在發生不同的中斷時,所要執行的函式位址。 **基本上中斷向量表的實作就是一個C的陣列,當然也可以用組合語言寫,但意義是一樣的。實際上CPU只有記憶體位址的觀念,當然不知道C的陣列是什麼;但說穿了所謂的中斷向量表,就是從某個位址開始,每4個byte為一個單位(entry),每一個entry記錄一個函數的位址** - 當`exceptions `發生時,處理器必須將開始發生的位址記錄在相對應`exception handler ` - 以一個ARM處理器來說,ARM7TDMI是負責中斷主要的部份 - 簡單來說,每個中斷(16~255)或者外部事件(exception: 1~15)都對應一個中斷號碼(exception numbers) - 而系統程式(program)必須告訴嵌入式系統的CPU,Vector table在哪裡(位於哪個位址)。 - 系統程式必須maintain一個中斷向量表,這是一個很單純的表格,每一個entry紀錄一個位址,這個位址指到一段程式(或說一個函數),稱為中斷處理程式(或ISR,Interrupt Service Routine) - 如下圖所示 - ![](https://i.imgur.com/UNPWTWJ.png) - 當外部事件或exception產生時,CPU或中斷處理器會知道第幾號中斷產生了,並參考中斷向量表,再將CPU“跳”到中斷向量表相對entry裡紀錄的位址 - 中斷處理程式執行完畢後,會返回被中斷的程式 ![](https://i.imgur.com/Bc1DPw3.png) #### Fault Handling(dummy) 當系統在執行中斷服務時,出現bug或者是未定義的行為時,便會return到一個錯誤的中斷服務函式裡,例如(除以0) ![](https://i.imgur.com/xydyh3g.png) - 除了`hard fault`是總是啟動的以外,其他都是事件發生才觸發啟動。 --- ## Exception handling in details : ![](https://i.imgur.com/y010gdR.png) - AAPCS: Procedure Call Standard for ARM Architecture - a C function can modify R0 to R3, R12, R14 (LR), and PSR - R0~R3, R12, LR, and PSR : caller saved registers在執行function call之前需要將這些暫存器裡的資料搬移到stack(內存)中 - R4~R11 are called “callee-saved registers” 這幾個暫存器內存的位址或是資料是不能改變的。若真的需要改變的話,就需要將R4~R11裡的資料暫存到stack裡面然後等中斷服務執行完後把這些值搬移回去。 ## stack frame : 上述需要被push到stack的資料會被放到一個資料表裡面,這個資料表我們就稱為`stack frame ` without float point unit: ![](https://i.imgur.com/STfjG9q.png) with float point unit : ![](https://i.imgur.com/iIJ02ul.png) #### Exception Sequences Example ![](https://i.imgur.com/9Zjkzes.png) ------ # Usart : ## USART Configuration - Enable the USART by writing the UE bit in USART_CR1 register to 1. - Select the desired baud rate using the USART_BRR register. - Set the TE bit in USART_CR1 to enable the transmitter - Set the RE bit in USART_CR1 to enable the receiver ## character transmission : 1. the USART_DR register consists of Transmit data register (TDR). 2. The TXE flag generates an interrupt if the TXEIE bit is set ## Character Reception 1. When a character is received, the RXNE bit is set. 2. Read to the USART_DR register (this clears the RXNE bit) ### RXNE : 1. 由硬體設備控制 2. 當RXNE設定為1的時候,表示資料已被傳送完成而且已經可以讀取到 3. 一旦RXNE被設定為1,則中斷程序便會產生 4. RXNE可以藉由軟體重設為0 5. RXNE在完成接收後且在接收下一段資料之前一定要清空,否則會出現Overrun error ## Baud rate : ![](https://i.imgur.com/vUpKUTe.png) # Porting Newlib: `malloc ` & `printf ` 自己找重點XD ------- # OS: User Mode and Kernel Mode [Cortex M4 暫存器介紹](https://www.itread01.com/content/1541967072.html) ## Privileged and Non-privileged Operation Modes - 在Cortex M4/M3中,其權限可以分為兩種=> privileged and non-privileged operation modes - 在一般的情況下coding,我們是處於`user mode `並且操作在unprivileged 的操作模式。 - 在non-privileged operation modes的情況下是不能使用一些`NVIC`的暫存器的 - MPU也可以用來防止在non-privileged operation modes下不會assess到不該assess的記憶體,而這種分為兩種模式的系統可以防止即使在non-privileged的執行權限下失敗了,系統也不會崩潰。 - 多數運行的模式只需要在non-privileged mode - ![](https://i.imgur.com/usC7PBA.png) - Operation states and modes: ![](https://i.imgur.com/pldoPxg.png) - processor在thread mode中有兩個LEVEL的權限下運行(privileged access level or unprivileged access level) - thread mode可以透過SP()在彼此之間互換 - Non-privileged operation mode restrictions - Limited access to the MSR and MRS instructions - Cannot access the system timer, NVIC, or system control block - Might have restricted access to memory or peripherals ## MSP和PSP的差別 - MSP :在handler mode下進行時是使用MSP,當然在thread mode下也可以使用PSP但這就需要特殊呼叫,在邏輯位址上為R13的暫存器 - PSP :在thread mode下進行時是使用PSP,在邏輯上的位址是R13的暫存器 - MSP和PSP的概念就像USART中的BUFF概念是相通的,在USART中我們傳送訊息時是用TXBUFF而接收訊息時是用RXBUFF,但實際上MCU的的UART就只有一個,而TXBUFF和RXBUFF都是在同一個位址。 - 目的: 是為了在進行模式轉換間,減少Stack的占用,同時也可以為不同權限的工作模式設置不同的stack ## Switch Between Privileged and Non-privileged Operation Modes - 透過SVC可以從Non-privileged 轉換到 Privileged - 透過software設定可以從Privileged 轉換到 Non-privileged ## Special register(特殊功能暫存器組): ![](https://i.imgur.com/S5v2kDN.png) - 程式狀態暫存器組(PSRs/xPSR) 3個 - 中斷遮蔽暫存器組(PRIMASK, FAULTMASK,以及BASEPRI) - 控制暫存器(CONTROL) 這7個暫存器只能被專用的MSR和MRS指令訪問,而且它們也沒有儲存器地址。 ### 程式狀態暫存器 ![](https://i.imgur.com/N2HkG1J.png) [詳細狀態暫存器說明](http://b8807053.pixnet.net/blog/post/3612679-arm%E5%9F%BA%E7%A4%8E%E7%9F%A5%E8%AD%98%E6%95%99%E7%A8%8B%E4%BA%8C) ### 中斷遮蔽暫存器: - PRIMASK : 這個暫存器只有一位,當該位為1時,就遮蔽所有可遮蔽的異常,除NMI和硬體fault可以響應。預設是0; - FAULTMASK: 這個暫存器也只有一位,置1時,只有NMI才能響應,所有其他異常均遮蔽。預設為0 - BASEPRI: 這個暫存器的有效位由表達優先順序的位數決定,最多9位。它定義了被遮蔽優先順序的閾值,當優先順序號大於等於此值的中斷均被關閉。預設為0,則不關閉任何中斷 - **簡單來說: 想要暫時關閉中斷 : 要處理PRIMASK和BASEPRI。** - **FAULTMASK: 被OS用於暫時關閉fault處理機能。** :::info PRIMASK=1;關中斷 PRIMASK=0; 開中斷 FAULTMASK=1; 關異常 FAULTMASK=0; 開異常 ::: ### 控制暫存器: - **用於定義特權級別和堆疊指標的使用(MSP/PSP)** - M4: 特有,當處理異常時是否儲存浮點環境 - 只能在 privileged access level 才是可讀可寫,而 unprivileged access levels則是可以讀取不能修改 ![](https://i.imgur.com/vqGbDny.png) **對映下圖:** ![](https://i.imgur.com/d1soTT6.png) - 在嵌入式系統被執行時,控制暫存器可以控制不同的Task運行在privileged或是unprivileged的情況下。 :::info ### 切換access level **處理器一開始一定是在優先級的執行緒模式**,這時候可以將其切換到非優先級(Control[0]=1)。 但是在執行緒模式下,非優先級不能直接變成優先級。 非優先級的執行緒模式要是想回到優先級,必須使用例外處理(exception handler),當處理器處在處理模式時會變成優先級(Control[0]=0)。 然後再跳回執行緒模式即可。 RTOS或嵌入式作業系統可以用這個技巧去設計更安全的應用程式。 ```css= [Kernel] Control: 0x0 [Kernel] SP: 0x2001ffc8 [Kernel] MSP: 0x2001ffc8 [Kernel] PSP: 0x0 [Kernel] Switch to unprivileged thread mode & start user task (psp_init = 0x20018000). [User] Start in unprivileged thread mode. [User] Control: 0x3 [User] SP: 0x20017ff8 [User] MSP: 0x0 [User] PSP: 0x0 [User] Switch to privileged thread mode by writing the control register directly. (forbidden) [User] Control: 0x3 [User] SP: 0x20017ff8 [User] MSP: 0x0 [User] PSP: 0x0 ``` ::: #### 結論: - 在Handler mode中執行必定是Privileged的權限 - 無法直接從unprivilege轉換成Privileged的權限,其用意是在保護系統和核心。 ---- ## 11 - OS: System Call ### SVC (Supervisor Call) Exception SVC的機制可以當作是一個API負責處理:是否允許Tasks可以assess到系統資源。 ![](https://i.imgur.com/5HYdcpu.png) 透過SVC-instruction我們可以產生SVC-中斷,根據其給的參數 決定要做什麼事(SVC numbers) 共有8個位元,也就是256個api ```css= SVC #0x3 ; Call SVC function 3 ``` ![](https://i.imgur.com/PhfQPlq.png) ## 為什麼要多花8bits給SVC來做儲存? 因為我們可能在執行程式時,會有需要不一樣的權限,若是使用register可能會造成值不正確。 ## 例外處理程序的實例: ![](https://i.imgur.com/oNuIsLN.png) :::success ## 背這張圖: ![](https://i.imgur.com/Y0YuJNQ.png) 有沒有發現, 當進入 user thread mode 狀態時, 並沒有一條路直接到 privileged thread , 而是要透過 exception 進到 privileged handler. 且 user thread 並沒有權限直接去改 program CONTROL register. ( user thread mode 是 unprivileged 的 ) 那怎麼去進到 所謂的 privileged kernel mode 去管理資源呢. 這時候靠的就是 SVC exception. - SVC Sequence: ![](https://i.imgur.com/158jlMz.png) ## LAB11-2 SVC Handler: Extract the SVC Parameter 1. ![](https://i.imgur.com/Bb5shWH.png) 透過SVC來幫我們做事情,程式透過`sys_call()`中的svc instruction進入到SVC_Hanler中,而在進入`handler`之前,硬體機制幫我們作了以下的事情: - 把r0~r3 LR PSR和return address push 到stack裡 - 把exc_return 讀取到LR中 - 讀取Handler_entry中的exception number給PC,已進入所對應的例外處理 ### SVC_handler : 2. - 組語: ```cpp= .type svc_handler, %function .global svc_handler svc_handler: mov r0, lr //把exc_return(在lr裡面)給r0 mrs r1, msp //把MSP(Main stack pointer)給r1 (main_back_up)因為在handler中或許會用到MSP,所以要先把之前的值處理起來 b svc_handler_c // 回到svc_handler_c函式裡 ``` 3. - c語言: ```cpp= if (handler&0b100) //Test bit 2 of EXC_RETURN { stack_frame_ptr = (uint32_t* )read_psp(); //if 1, stacking used PSP printf("[SVC Handler] Stacking used PSP: 0x%X \r\n\n", (unsigned int)stack_frame_ptr); } else { stack_frame_ptr = (uint32_t* )main_backup;//if 0, stacking used MSP printf("[SVC Handler] Stacking used MSP: 0x%X \r\n\n", (unsigned int)stack_frame_ptr); } ``` 透過暫存起來的exc_return判斷是用PSP還是MSP,並且把該Pointer給stack_frame_ptr,再透過`stack_frame_ptr `去找到`RETURN_ADDRESS`(stack_frame_ptr+6)即為return_address(LR)。 ```css= uint32_t stacked_r0 =(*(stack_frame_ptr));// (*(stack_frame_ptr)); uint32_t stacked_r1 =(*(stack_frame_ptr+1));; uint32_t stacked_return_addr = (*(stack_frame_ptr+6)); uint16_t svc_instruction = *((uint16_t *)stacked_return_addr - 1); //透過找到return_address 我們也可以找到SVC_instruction的值: 如下圖 uint8_t svc_num =(uint8_t)svc_instruction; ``` ![](https://i.imgur.com/12Y4nuu.png) 找到`SVC_instruction`後我們就可以找到對應的instruction number。 ## 為甚麼要找到SVC parameter? The SVC exception handler needs to extract the SVC parameter and determine what action it needs to perform. 以本例子來說,我們根據SVC parameter判斷為需要將r0和r1做相加,於是我們就做相加的動作: ```css= if (svc_num == 0xA) //return r0 + r1 *stack_frame_ptr = stacked_r0 + stacked_r1; else *stack_frame_ptr = 0;//return 0 ``` ::: ## 12 - OS: Context Switch + 簡單的 context switch 可使用 systick 產生 exception 達成 + 流程如圖所示 1. 當前的 task 被 sytick exception 中斷後,**進入 systick handler** + 此時,caller saved register 會被 push 至當前 task 的 user stack 中 2. 在 systick handler 中,是使用 msp + 先把 LR 存好 + 把 callee saved register 存好到 stack + 再 branch 至 sw_task (以 c 實作) 以切換成新的 psp + 最後把存好的 LR 取回 3. **離開 systick handler**後,因為 psp 已更新,就會到下一個 user stack 中 ![](https://i.imgur.com/l1aTobd.png) + 每個 user stack 結構如下圖 ![](https://i.imgur.com/COOoddK.png) ![](https://i.imgur.com/Oebe9AO.png) `systick`倒數,時間到了就進入到`systick handler`裡面先存取`r4~r11`還有`LR(excreturn )`,接著`branch`到`scheduler`裡面,`scheduler`再`return`下一個task的`psp`給`systick handler`,`systick-hanlder`回復`r4~r11`還有`lr(exc_return)`後就結束中斷,再回到task裡面時,硬體會回復`stack-frame`,就會發現所有的register變成下一個task。 這樣子就變成一個context switch ## Ivan, Joe ICB

    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