林士傑
    • 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 No publishing access yet

      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.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      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 New
    • Engagement control
    • Make a copy
    • 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 Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy 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 No publishing access yet

    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.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    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
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Assignment I - Compiling a Custom Linux Kernel & Implementing New System Calls **[NYCU \[CSIC30015\] Operating System](https://timetable.nycu.edu.tw/?r=main/crsoutline&Acy=114&Sem=1&CrsNo=535503&lang=zh-tw) by Prof. [Chun-Feng Wu](https://www.cs.nycu.edu.tw/members/detail/cfwu417)** <font color="#f00"> **Due Date: 23:59, October 22 (Wednesday), 2025** </font> --- **Table of Contents** [TOC] --- ## Goals - Run a minimal Linux kernel for testing. - Compile a custom Linux kernel from source. - Add a new Linux system call. - Create a kernel patch. ## Introduction This semester, all three hands-on projects will involve working with the Linux kernel. #### Kernel Documentation The Linux kernel is open source and well-documented. The documentation can be found in the `Documentation/` directory at the root of the kernel source tree. You can build the documentation using the `make htmldocs` or `make pdfdocs` commands. Alternatively, there is an online version for kernel **v6.1** available [here](https://www.kernel.org/doc/html/v6.1/index.html#) for all our assignments. Documentation for many other kernel versions can be found [here](https://www.kernel.org/doc/html/), with the latest version available [here](https://www.kernel.org/doc/html/latest/). Additionally, different kernel versions' online source code is provided by [Bootlin](https://elixir.bootlin.com/linux/v6.11-rc6/source), which allows you to easily browse and trace the code through their website. Please feel free to ask questions via email or on the forum if you have problem finding out the solution from the documentation provided. ## Environment Set-Up ::: info **Note:** This environment will be reused in later assignments, so skipping this one may affect your progress in the following assignments. ::: :::warning **Warning:** If your host machine uses **Windows** as operating system, please use **WSL** to perform the tasks. https://learn.microsoft.com/en-us/windows/wsl/install ::: ### I. Docker For this assignment, you will need a working Docker environment to compile the Linux kernel for a **RISC-V environment** #### 1. Installation First, you need to install Docker Compose on your machine. The easiest way is to follow the step-by-step instructions provided on the [official Docker website](https://docs.docker.com/compose/), which will guide you through the installation process for your operating system. Once Docker is installed, you should verify that the installation was successful with: ```bash docker --version ``` #### 2. Building the container In the directory with the provided `Dockerfile` and `docker-compose.yml`, run the following command to create the Docker container and begin working in the workspace. ```bash # build the container docker compose build # start up the container in the background docker compose up -d ``` After the container is started, the workspace will include a `docker/home/ubuntu/` directory, which is mounted to `/home/ubuntu` inside the container. You can copy files to and from this directory to transfer data between the host and the container. #### 3. Getting into the container You can access the container using either exec access or SSH. ##### a. Using `docker exec` If you just need to get into the container shell directly, you can use the following commands. ```bash # Start an interactive shell inside the running container docker exec -it <container_name> /bin/bash # Switch to the working directory cd /home/ubuntu ``` ##### b. Using SSH During the first startup, the container is preconfigured to generate an SSH key pair automatically. The private key can be used for passwordless login into the container. ```bash ssh -i docker/home/ubuntu/.ssh/id_rsa -p 22025 ubuntu@localhost ``` ::: success **Hint:** If you are prompted for a password, it usually means that the private key was not found. Make sure that you run the `ssh` command with administrator privileges and you run the command from the assignment directory. ::: If the setup was successful, you should now be greeted by the terminal as the `ubuntu` user. #### 4. Container Management You can use the following Docker commands outside the container to check and manage Docker processes. ```bash # to list all currently running Docker containers docker ps # stop and remove containers, networks, and other resources docker compose down # to stop a running container docker kill <containerID or name> # to show detailed information docker inspect <containerID or name> # real-time view of container resource usage docker stats ``` ### II. Verifying the Environment The container image has been preconfigured with the complete RISC-V toolchain, with the prefix `riscv64-linux-gnu-*`. Therefore, you only need to verify the installation with the following commands. ```bash # check the cross-compiler version riscv64-linux-gnu-gcc -v # check QEMU for RISC-V system emulation qemu-system-riscv64 --version ``` If the RISC-V toolchain is not available, you can install it by yourself. ```bash sudo apt install gcc-riscv64-linux-gnu ``` ### III. Downloading Linux source Here, We clone the [mainline Linux Git repository](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git) (i.e., Linus Torvalds’ tree). ```bash git clone https://github.com/torvalds/linux --branch v6.1 --depth 1 ``` ::: info **Note:** In our case, the compiled target is the release of **6.1 LTS kernel**. Using the arguments `--branch v6.1 --depth 1` avoids the download of all versions that are included in the Linux repository and only the version we care about in this assignment. ::: This concludes the setup phase of the assignment. Next, we will go over the requirements and tasks to complete. ## Requirements ### I. Compiling a custom Linux Kernel #### 1. Kernel Compilation (Do It Yourself!) Now, begin your journey of compiling the Linux kernel! The required kernel source version is v6.1 LTS kernel (or v6.1.0), which is a Long-Term Support (LTS) maintained until December 2026. Moreover, the Civil Infrastructure Project (CIP) has adopted v6.1 as an SLTS (Super LTS) and plan to maintain it for 10 years, until August 2033. We will not go through the kernel compilation steps here since there are numerous resources and documents available online. :::success **Hint:** Since the target platform is **RISC-V**, you must configure the **architecture flag** and **cross-compiler flag** correctly ::: #### 2. Changing kernel local version Before compiling the kernel, you need to modify the kernel local version suffix to `-os-<student ID>` (e.g. -os-313551000). This will serve as evidence that the kernel was built by you. To quickly check whether the kernel version string meets the requirements, you can run the following inside the container, even before starting QEMU: ```bash make kernelrelease ``` This will output something like: ```bash 6.1.0-os-313551000 ``` #### 3. Running a RISC-V Linux with QEMU To run the system, you need two components: - **Kernel Image** – the Linux kernel you compiled for RISC-V. - **Initramfs** (`initramfs.cpio.gz`) – a minimal root filesystem provided in the homework materials. Since the target is RISC-V, you will run the system in QEMU (`qemu-system-riscv64`), which provides full-system emulation. QEMU will boot using your custom Kernel Image together with the provided Initramfs. ```bash qemu-system-riscv64 -nographic -machine virt \ -kernel linux/arch/riscv/boot/Image \ -initrd initramfs.cpio.gz \ -append "console=ttyS0 loglevel=3" ``` ::: info **Note:** To quit a started QEMU session, press `Ctrl + A`, release both keys, and press `X`. ::: #### <font color="#f00">4. Requirement</font> In your report, complete the following: - **Include screenshot of the outputs of `uname -a` and `cat /proc/version`** to prove that you compiled the kernel by yourself. - Example: ![image](https://hackmd.io/_uploads/HkA4PR6cll.png) - **List the steps you took** to compile the kernel - **Answer the Q&As** 1. What are the main differences between the RISC-V and x86 architectures? 2. Why do the architecture differences matter when building the kernel? What happens if you build the kernel without the correct RISC-V cross-compilation flag? 3. Why is Docker used in this assignment, and what advantages does it provide? Please list at least two of them. :::warning **Warning:** If you do not include the above screenshot in your report, this item may receive a score of **zero**. ::: ### II. Implementing new System Calls In this section, you are required to implement two new Linux system calls in the kernel v6.1 you have built: **`sys_revstr`** and **`sys_tempbuf`** For guidance on adding system calls, refer to the [kernel documentation](https://www.kernel.org/doc/html/v6.1/process/adding-syscalls.html#generic-system-call-implementation). You may check [syscall(2)](https://man7.org/linux/man-pages/man2/syscall.2.html) to learn how to use `syscall` in linux system. Remember that we are using **RISC-V architecture** in this assignment, so please pay close attention to the implementation differences across architectures. :::success **Hint:** This [stackoverflow post](https://stackoverflow.com/questions/71551719/how-to-add-a-system-call-in-risc-v-linux) may be helpful. ::: #### 1. `sys_revstr` - **SYNOPSIS:** ```c #include <unistd.h> #define __NR_revstr 451 int syscall(__NR_revstr, char *str, size_t n); ``` - **DESCRIPTION:** You are required to implement a system call named `sys_revstr`, which accepts two arguments: a string `str` and its length `n`. This system call should reverse the string and perform the following actions: 1. Print a line "`The origin string: <str>`" to the **kernel ring buffer**, where `<str>` is the string the user passed in. 2. Reverse the given string. 3. Print a line "`The reversed string: <rev_str>`" to the **kernel ring buffer**, where `<rev_str>` is the resulting string you reversed. 4. copy the result string to the user's space. 5. Return zero to indicate successful execution. :::info **Note:** You don't need to handle invalid arguments such as a null pointer or mismatched string length. ::: :::success **💡Hint:** The data from the user space are not shared with the kernel space (and vice versa), so you need to figure out how to transfer data between them. ::: :::success **💡Hint:** Data printed to the kernel ring buffer must be written using kernel logging functions ::: - **SAMPLE EXPLANATION:** Ensure the following user-space code can be compiled and executed on your QEMU emulator. ```c #include <unistd.h> #include <string.h> #include <stdio.h> #include <assert.h> #define __NR_revstr 451 // System call number 451 (revstr implementation) int main(int argc, char *argv[]) { char str1[20] = "hello"; printf("Ori: %s\n", str1); int ret1 = syscall(__NR_revstr, str1, strlen(str1)); assert(ret1 == 0); printf("Rev: %s\n", str1); char str2[20] = "Operating System"; printf("Ori: %s\n", str2); int ret2 = syscall(__NR_revstr, str2, strlen(str2)); assert(ret2 == 0); printf("Rev: %s\n", str2); return 0; } ``` The output of the above user-space program should look like this: ![image](https://hackmd.io/_uploads/Hk_GiCTclg.png) Check the kernel ring buffer with the `dmesg` command. You should see output similar to: ![image](https://hackmd.io/_uploads/ry_Di0T5ex.png) #### 2. `sys_tempbuf` - **SYNOPSIS:** ```c #include <unistd.h> #define __NR_tempbuf 452 enum mode{ PRINT, // Print all stored records to the kernel ring buffer ADD, // Add a new record REMOVE // Remove the first matching record }; int syscall(__NR_tempbuf, enum mode, void *data, size_t size); ``` - **DESCRIPTION:** You are required to implement a system call named `sys_tempbuf`, which manages a temporary list of strings inside the kernel. It allows user programs to add strings, remove specific strings, or print all stored strings. The system call accepts three arguments: - `mode`: selects the operation (`ADD`, `REMOVE`, `PRINT`) - `data`: a pointer to a user string - `size`: the length of the string in `data` The behavior of each mode is described as follows: - **`ADD`**: - Copies the given string from user space pointer `data` and appends it to **the end of the list** as one record - Also, prints a message to the **kernel ring buffer** in the form `[tempbuf] Added: <data>`, where `<data>` is the string the user passed in - **`REMOVE`**: - Searches the list for the **first record that matches the given `data`**. If found, removes it. If there are multiple identical strings, **only the first match is removed** - Also, prints a message to the **kernel ring buffer** in the form `[tempbuf] Removed: <data>` if the record is found - **`PRINT`**: - Concatenates all strings **in the list in the order they were inserted**. Each string is separated by exactly one space. Then, copies the result into the user-provided `data` (up to `size` bytes) - Also, prints the result to the **kernel ring buffer**. The message must begin with `[tempbuf]` , followed by the concatenated result - eg. Suppose the list has three records inserted in this order ```bash "foo" "bar" "baz" ``` The buffer returned to user space via `data` should look like: ```bash foo bar baz ``` - **RETURN VALUE** You must clearly define the return values of your system call: - For `ADD` / `REMOVE`, returns 0 on success - For `PRINT`, returns the number of actual characters copied into the user buffer, **excluding the terminating null character** (`'\0'`) - **On failure**: returns a negative error code - **`-EFAULT`**: Indicates that the user-provided pointer is not a valid user-space address, and the kernel failed to access it - `data == NULL` / `size == 0` - `data` points to an illegal/unmapped region - **`-ENOENT`**: Used only in `REMOVE` mode. Indicates that the requested string does not exist in the tempbuf list, so there is nothing to remove :::info **Note:** In the test cases, the output length produced by the `PRINT` operation will not exceed 512 bytes ::: :::info **Note:** You don't need to handle mismatched string length ::: :::success **Hint:** The Linux kernel provides a generic linked list implementation (`struct list_head`) that may be useful for managing records. You can refer to [this documentation](https://docs.kernel.org/core-api/list.html). ::: - **SAMPLE EXPLANATION:** Ensure the following user-space code can be compiled and executed on your QEMU emulator. ```c #include <unistd.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <errno.h> #define __NR_tempbuf 452 // System call Nr 452 (tempbuf implementation) enum mode { PRINT, ADD, REMOVE }; int main() { char buf[512]; int ret; ret = syscall(__NR_tempbuf, ADD, "Hello", strlen("Hello")); assert(ret == 0); ret = syscall(__NR_tempbuf, ADD, "Operating Systems", strlen("Operating Systems")); assert(ret == 0); memset(buf, 0, sizeof(buf)); ret = syscall(__NR_tempbuf, PRINT, buf, sizeof(buf)); assert(ret >= 0); printf("%s\n", buf); ret = syscall(__NR_tempbuf, REMOVE, "NotExist", strlen("NotExist")); assert(ret == -1 && errno == ENOENT); ret = syscall(__NR_tempbuf, REMOVE, "Hello", strlen("Hello")); assert(ret == 0); memset(buf, 0, sizeof(buf)); ret = syscall(__NR_tempbuf, PRINT, buf, sizeof(buf)); assert(ret >= 0); printf("%s\n", buf); return 0; } ``` The output of the above user-space program should look like this: ![image](https://hackmd.io/_uploads/rJ8fOMXilg.png) Check the kernel ring buffer with the `dmesg` command. You should see output similar to: ![image](https://hackmd.io/_uploads/B1xpOz7jeg.png) #### 3. Running Your Test Programs in QEMU After compiling your custom kernel with the new system calls, you need to test them inside QEMU with the provided `initramfs.cpio.gz`. Since QEMU runs with a minimal root filesystem, you must manually include your test programs into the initramfs image. **a) Compile your test program** Cross-compile the test program for RISC-V inside the Docker container: ```bash riscv64-linux-gnu-gcc -static -o <test_program> <test_program.c> ``` **b) Place the binary into initramfs directory** Extract the provided `initramfs.cpio.gz` into a temporary directory: ```bash mkdir -p initramfs cd initramfs gzip -dc ../initramfs.cpio.gz | cpio -idmv ``` Then copy your test binary into wherever you want. **c) Repack the initramfs** After adding the file, repack the directory into a new initramfs image: ```bash find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz cd .. ``` Next, follow Requirements I-3 to test the kernel image with QEMU. #### <font color="#f00">4. Requirement</font> In your report, complete the following: - **Include four screenshots of the outputs** - The execution result of provided `test_revstr` user program for `sys_revstr` - The `dmesg` output of `sys_revstr` - The execution result of provided `test_tempbuf` user program for `sys_tempbuf` - The `dmesg` output of `sys_tempbuf` - Explain **how you added system calls to your custom Linux kernel** and the **implementation details** of `sys_revstr` and `sys_tempbuf` - **Answer the Q&As** 1. What does the `include/linux/syscalls.h` do? 2. Explain the difference between a system call and a glibc library call, then give one example that maps a glibc function to the specific system call it ultimately invokes. 3. Explain the difference between static linking and dynamic linking 4. In this assignment environment, why do we have to compile the test programs with the `-static` flag? What would happen if we omitted it? ### III. Patch In Linux kernel development, the **email format patch** is widely used. Patches adhere to a specific format designed to facilitate review, application, and tracking within the kernel development process. After finishing the required changes to your kernel version, it is time to create your own patch. This patch will be used by the TAs to build the kernel with the system calls you implemented. :::success **💡Hint:** You can refer to the [git-format-patch(1)](https://man7.org/linux/man-pages/man1/git-format-patch.1.html) documentation for more information. ::: :::info **Note:** It's fine if you have many `.patch` files, depending on the number of commits. Just simply place all the `.patch` files in the `HW1_<student ID>/` folder. ::: :::warning **Warning:** Using an incorrect patch format may cause the TA to be unable to build your patch, resulting in a zero grade. Therefore, please make sure the format is correct. ::: ### IV. Package list Since the `.config` file you set may require some packages to build the kernel you configured, use the following command to list the packages installed on your system: ```shell $ dpkg --get-selections > packages_list_<student ID>.txt ``` ## Grading #### Total (100%) - **Report (40%)** - I. Compiling a custom Linux kernel (20%) - Description (10%) - Q&As (10%) - II. Implementing new system calls (20%) - Description (10%) - Q&As (10%) - **Implementations (60%)** - Kernel local version (20%) - we will use the `.config` you provide to build the kernel, and we will check whether you set the correct local version specified [here](##Requirements). - `sys_revstr` hidden test case (20%) - `sys_tempbuf` hidden test case (20%) :::info **Note:** Hidden test case are just like the provided test programs and have nothing special — we just want to prevent someone from simply printing the answer. ::: ## Submissions #### 1. Required Files - **Report** - Only `.pdf` file is accepted - Make sure your report meets all of the <font color="#f00"> **requirement**</font> above - Filename - `<student ID>_report.pdf` (e.g `313551000_report.pdf`) - **Config File** - `.config` you used to build your kernel - **Package List** - Filename - `packages_list_<student ID>.txt` (e.g `packages_list_313551000.txt`) - **Patch file** - Filename - `0001-xxxx.patch` (e.g. `0001-Modify-Makefile.patch`) - `0002-xxxx.patch` (e.g. `0002-Add-the-revstr-syscall-definition.patch`) - `0003-xxxx.patch` (e.g. `0003-Implement-revstr-syscall.patch`) - ... ... - ... ... Ensure you include all necessary files above for this assignment. #### 2. Pack the required file All your files should be organized in the following hierarchy and zipped into a `.zip` file, named `HW1_<student ID>.zip` (e.g. `HW1_313551000.zip`) The structure inside the zipped file should be as the following: ``` HW1_<student ID>.zip |- HW1_<student ID>/ |- <student ID>_report.pdf |- .config |- packages_list_<student ID>.txt |- 0001-xxxx.patch |- 0002-xxxx.patch |- ...... |- ...... |- ...... ``` e.g. ``` HW1_313551000.zip |- HW1_313551000/ |- 313551000_report.pdf |- .config |- packages_list_313551000.txt |- 0001-Modify-Makefile.patch |- 0002-Add-the-revstr-syscall-definition.patch |- 0003-Implement-revstr-syscall.patch ``` :::info **Note:** It's fine if you have many `.patch` files, depending on the number of commits. Just simply place all the `.patch` files in the `HW1_<student ID>/` folder. ::: :::warning **DO NOT** include any files or directories not explicitly specified above in your archive, such as `__MACOSX`, `.git`, `.vscode`, `.vscode-server`,etc., as they may potentially cause our automated build system to crash. ::: #### 3. Submit the archive through the E3 platform before deadline. If you have any questions about this assignment, please feel free to contact the TA or make an appointment by emailing os.oscarlab@gmail.com. You may also reach out to the TA during class. :::danger **Warning:** Incorrect file formats will result in a 20-point deduction. Additionally, if a formatting error prevents the TA from building your kernel, you may not receive any points. ::: <!-- :::info **Note1**: The TA is here to help clarify assignment questions and requirements, not to serve as your personal tutor or a tool for debugging your code and environment. ::: --> :::danger **Plagiarism Warning:** Plagiarism is strictly prohibited. Homework assignments must be completed **individually**. Assignments found to be plagiarized will receive reduced credit or, in most cases, **no credit**. ::: ## Additional Note In formal kernel development, you should write a Kconfig file to allow users to decide whether to build your custom system call using menuconfig or by configuring the kernel via `scripts/config`. The configuration option might be named `NYCU_OS_AS1` or whatever you prefer. However, for this assignment, you are not required to do this. This task is an additional exercise for those interested. For more detail about Kconfig, please refer [this Kernel Document](https://www.kernel.org/doc/html/v6.1/kbuild/kconfig-language.html).

    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
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    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