跳至主要內容

Git 備忘錄

這篇備忘錄記錄了 Git 的一些常用命令和概念。

資訊

Git 是一個免費開源的分布式版本控制系統 (Distributed Version Control System, DVCS),旨在以速度和效率處理從小到大的所有項目。它允許開發人員跟踪其程式碼庫中的更改,協作開發,並在需要時回溯到早期版本。

1. 基本操作​

初始化倉庫​

在當前目錄中創建一個新的 Git 倉庫:

git init

克隆倉庫​

克隆一個遠端倉庫到本地:

git clone <repository_url>

範例:

git clone https://github.com/git/git.git

添加文件到暫存區​

將文件添加到暫存區(準備提交):

git add <file_name> # 添加單個文件
git add . # 添加所有更改過的文件
git add -u # 添加所有已追蹤但已修改/刪除的文件(不包括新文件)
git add -A # 添加所有更改過的文件(包括新文件、修改和刪除)

提交更改​

將暫存區中的更改提交到本地倉庫:

git commit -m "提交信息"

檢查狀態​

查看工作目錄和暫存區的狀態:

git status

查看更改​

查看工作目錄和暫存區之間的差異:

git diff # 查看未暫存的更改
git diff --staged # 查看已暫存但未提交的更改
git diff HEAD # 查看工作目錄與最新提交的差異

2. 分支操作​

列出分支​

列出本地分支:

git branch

列出所有分支(包括遠端分支):

git branch -a

創建分支​

創建一個新分支:

git branch <new_branch_name>

切換分支​

切換到指定分支:

git checkout <branch_name>

創建並切換到新分支:

git checkout -b <new_branch_name>

合併分支​

將一個分支的更改合併到當前分支:

git merge <branch_to_merge>

刪除分支​

刪除本地分支(需要先切換到其他分支):

git branch -d <branch_name> # 刪除已合併的分支
git branch -D <branch_name> # 強制刪除未合併的分支

3. 遠端倉庫操作​

查看遠端倉庫​

查看配置的遠端倉庫:

git remote -v

添加遠端倉庫​

添加一個遠端倉庫:

git remote add <remote_name> <repository_url>

範例:

git remote add origin https://github.com/yourusername/your_repo.git

推送更改​

將本地分支的更改推送到遠端倉庫:

git push <remote_name> <branch_name>

範例:

git push origin main

拉取更改​

從遠端倉庫拉取更改到本地分支:

git pull <remote_name> <branch_name>

範例:

git pull origin main

4. 歷史記錄​

查看提交歷史​

查看提交歷史:

git log
git log --oneline # 簡潔顯示
git log --graph --oneline --decorate # 圖形化顯示分支合併

回溯更改​

  • 撤銷最新提交 (不保留更改):
    git reset --hard HEAD~1
  • 撤銷最新提交 (保留更改在工作目錄):
    git reset HEAD~1
  • 撤銷特定文件在特定提交中的更改:
    git checkout <commit_hash> <file_path>

Revert 提交​

創建一個新的提交來撤銷指定提交的更改,保留歷史記錄:

git revert <commit_hash>

5. 標籤 (Tags)​

列出標籤​

git tag

創建標籤​

git tag -a v1.0 -m "版本 1.0"

推送標籤​

git push origin --tags

總結​

Git 是一個功能強大的版本控制系統,掌握這些基本命令可以讓你有效地管理程式碼、協同工作並應對各種開發場景。這篇備忘錄只涵蓋了 Git 的一部分功能,還有更多進階功能(如 rebase, cherry-pick, submodule 等)等待探索。

JavaScript 備忘錄

這篇備忘錄記錄了 JavaScript。

資訊

JavaScript 是一種高階、解釋型、多範式的程式語言。它最常用於網頁瀏覽器,為網頁添加互動性,但隨著 Node.js 的出現,它也已成為服務器端開發的重要語言。

1. 變量宣告​

JavaScript 有三種主要方式宣告變量:var、let 和 const。

  • var:

    • 函數作用域 (Function-scoped)。
    • 存在變量提升 (Hoisting),但只提升宣告,不提升賦值。
    • 可以重複宣告和重新賦值。
    var x = 10;
    function func() {
    var y = 20;
    }
    console.log(x); // 10
    // console.log(y); // ReferenceError
  • let:

    • 塊級作用域 (Block-scoped)。
    • 存在變量提升,但處於「暫時性死區」(Temporal Dead Zone, TDZ)。
    • 不能重複宣告,但可以重新賦值。
    let x = 10;
    if (true) {
    let y = 20;
    }
    console.log(x); // 10
    // console.log(y); // ReferenceError
  • const:

    • 塊級作用域。
    • 存在變量提升,處於 TDZ。
    • 不能重複宣告,也不能重新賦值(對於原始類型)。對於物件和陣列,可以修改其內部屬性,但不能重新賦值整個變量。
    const x = 10;
    // x = 20; // TypeError: Assignment to constant variable.

    const obj = { a: 1 };
    obj.a = 2; // 允許
    // obj = { b: 3 }; // TypeError

2. 數據類型​

JavaScript 有八種數據類型:

  • 原始類型 (Primitives):

    • String:文本數據 ("hello", 'world')
    • Number:數字 (整數或浮點數) (10, 3.14)
    • Boolean:布爾值 (true, false)
    • Null:表示空值或不存在的值 (null)
    • Undefined:表示未定義的值(變量已宣告但未賦值) (undefined)
    • Symbol (ES6):唯一且不可變的值。
    • BigInt (ES2020):表示大於 2^53 - 1 的整數。
  • 物件類型 (Object):

    • Object:所有其他數據類型的基礎。
      • 包括普通物件 ({ key: value })
      • 陣列 ([1, 2, 3])
      • 函數 (function() {})
      • 日期 (new Date())
      • 正則表達式 (/abc/)

3. 運算符​

比較運算符​

  • ==:寬鬆相等(會進行類型轉換)
  • ===:嚴格相等(不進行類型轉換)
  • !=:寬鬆不相等
  • !==:嚴格不相等
  • >, <, >=, <=

邏輯運算符​

  • &&:邏輯與
  • ||:邏輯或
  • !:邏輯非

其他運算符​

  • 算術運算符:+, -, *, /, %, ** (冪運算)
  • 賦值運算符:=, +=, -=, *= 等
  • 條件 (三元) 運算符:condition ? expr1 : expr2
  • 展開運算符 (Spread operator) (...):用於展開可迭代對象。
    const arr1 = [1, 2];
    const arr2 = [...arr1, 3, 4]; // [1, 2, 3, 4]
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
  • 其餘參數 (Rest parameters) (...):用於收集函數參數。
    function sum(a, b, ...rest) {
    console.log(rest); // 收集除了 a 和 b 之外的所有參數
    return a + b + rest.reduce((acc, val) => acc + val, 0);
    }
    sum(1, 2, 3, 4); // rest = [3, 4]

4. 函數​

函數宣告​

function greet(name) {
return `Hello, ${name}!`;
}

函數表達式​

const greet = function(name) {
return `Hello, ${name}!`;
};

箭頭函數 (Arrow Functions, ES6)​

  • 更簡潔的語法。
  • 沒有自己的 this,arguments,super 和 new.target。它們從父作用域繼承 this。
  • 不能用作構造函數。
const greet = (name) => `Hello, ${name}!`;

// 多行語句
const add = (a, b) => {
const result = a + b;
return result;
};

5. 模塊 (ES Modules, ES6)​

使用 import 和 export 關鍵字來組織程式碼。

導出 (Export)​

// math.js
export const add = (a, b) => a + b;
export function subtract(a, b) {
return a - b;
}
export default class Calculator { /* ... */ }

導入 (Import)​

// app.js
import { add, subtract } from './math.js';
import Calculator from './math.js';

console.log(add(5, 3));
const calc = new Calculator();

總結​

JavaScript 是一個功能強大且不斷發展的語言。從變量宣告到函數和模塊,掌握這些基本概念是成為一名熟練的 JavaScript 開發者的基礎。隨著 ES6+ 的發展,許多新的特性和語法糖也極大地提高了開發效率。

如何安裝 Docker

這篇備忘錄記錄了如何在 Linux(Ubuntu/Debian)上安裝 Docker。

資訊

Docker 是一個開源平台,用於開發、交付和運行應用程式。它允許你將應用程式及其所有依賴項打包到一個稱為容器的標準化單元中,該容器可以在任何地方運行。

1. 卸載舊版本 (如果存在)​

如果你之前安裝過舊版本的 Docker,建議先將其卸載,以避免潛在的衝突。

sudo apt-get remove docker docker-engine docker.io containerd runc

2. 更新套件索引並安裝必要工具​

更新 apt 套件索引,並安裝一些允許 apt 通過 HTTPS 使用儲存庫的必要工具:

sudo apt-get update
sudo apt-get install
ca-certificates
curl
gnupg
lsb-release

3. 添加 Docker 的官方 GPG 密鑰​

為了驗證 Docker 套件的真實性,你需要添加 Docker 的官方 GPG 密鑰:

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

4. 設置儲存庫​

添加 Docker 儲存庫到你的 apt 源列表:

echo
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
資訊

$(lsb_release -cs) 會輸出你的 Ubuntu 代號,例如 focal (20.04) 或 jammy (22.04)。

5. 安裝 Docker Engine​

再次更新 apt 套件索引,然後安裝 Docker Engine、Containerd 和 Docker Compose。

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

6. 驗證 Docker 安裝​

通過運行 hello-world 影像來驗證 Docker Engine 是否正確安裝:

sudo docker run hello-world

如果一切正常,你會看到一條消息,表明 Docker 安裝成功。

7. 管理 Docker 作為非 root 用戶 (可選但推薦)​

默認情況下,docker 命令需要 sudo 權限。如果你想作為非 root 用戶運行 Docker 命令,你需要將你的用戶添加到 docker 群組。

  1. 創建 docker 群組 (如果不存在):
    sudo groupadd docker
  2. 將當前用戶添加到 docker 群組:
    sudo usermod -aG docker $USER
  3. 重新登錄: 為了使群組更改生效,你需要註銷並重新登錄,或者重啟你的系統。 或者,你可以運行 newgrp docker 命令,但這只會影響當前會話。

完成後,你可以無需 sudo 即可運行 Docker 命令:

docker run hello-world

8. 配置 Docker 在啟動時自動運行 (可選)​

Docker 通常會自動配置為在系統啟動時運行。你可以檢查其狀態:

sudo systemctl status docker

如果它沒有在啟動時啟用,你可以手動啟用:

sudo systemctl enable docker.service
sudo systemctl enable containerd.service

總結​

通過上述步驟,你可以在 Ubuntu/Debian 系統上成功安裝 Docker Engine 和相關工具。這將使你能夠開始使用容器來打包、部署和運行你的應用程式。記住將你的用戶添加到 docker 群組以獲得更方便的體驗。

cuBLAS 備忘錄

這篇備忘錄記錄了 cuBLAS。

資訊

cuBLAS 是 NVIDIA 提供的一個 GPU 加速的 BLAS(基本線性代數子程序庫)實現。它提供了高性能的標準線性代數運算,如向量-向量運算、矩陣-向量運算和矩陣-矩陣運算。

cuBLAS 庫是基於 CUDA 構建的,允許開發者利用 NVIDIA GPU 的強大並行計算能力來加速線性代數的計算。

cuBLAS 的特性​

  • GPU 加速:所有運算都在 GPU 上執行,提供比 CPU 快得多的性能。
  • 標準 BLAS 接口:遵循 BLAS 函數的標準命名約定(level 1, 2, 3)。
  • 多種數據類型支持:支持單精度 (float)、雙精度 (double)、半精度 (half)、複數等數據類型。
  • 易於集成:可以輕鬆地與 CUDA C/C++ 程式碼集成。

cuBLAS 的基本用法​

使用 cuBLAS 通常涉及以下步驟:

  1. 初始化 cuBLAS 庫:創建一個 cuBLAS 句柄。
  2. 在 GPU 上分配內存:為輸入和輸出數據分配 GPU 內存。
  3. 將數據從主機複製到設備:將輸入數據從 CPU 內存複製到 GPU 內存。
  4. 執行 cuBLAS 運算:調用相應的 cuBLAS 函數。
  5. 將數據從設備複製回主機:將結果從 GPU 內存複製回 CPU 內存。
  6. 釋放 GPU 內存。
  7. 銷毀 cuBLAS 句柄。

範例:矩陣乘法 (GEMM)​

矩陣乘法 (GEMM: General Matrix-Matrix Multiplication) 是 cuBLAS 最常用的功能之一。

假設我們有兩個矩陣 $A$ 和 $B$,我們想計算它們的乘積 $C = \alpha AB + \beta C$。

#include <iostream>
#include <vector>
#include <cuda_runtime.h>
#include <cublas_v2.h>

// 錯誤檢查宏
#define CUDA_CHECK(call)
do {
cudaError_t err = call;
if (err != cudaSuccess) {
fprintf(stderr, "CUDA error in %s (%s:%d): %s
", __func__, __FILE__, __LINE__, cudaGetErrorString(err));
exit(EXIT_FAILURE);
}
} while (0)

#define CUBLAS_CHECK(call)
do {
cublasStatus_t status = call;
if (status != CUBLAS_STATUS_SUCCESS) {
fprintf(stderr, "cuBLAS error in %s (%s:%d): status %d
", __func__, __FILE__, __LINE__, status);
exit(EXIT_FAILURE);
}
} while (0)

int main() {
int N = 3; // 矩陣維度 (為簡化起見,假設方陣)

// 主機數據
std::vector<float> h_A(N * N);
std::vector<float> h_B(N * N);
std::vector<float> h_C(N * N);

// 初始化 A 和 B
for (int i = 0; i < N * N; ++i) {
h_A[i] = static_cast<float>(i + 1);
h_B[i] = static_cast<float>(N * N - i);
h_C[i] = 0.0f; // 初始化 C 為零
}

// 設備數據指針
float *d_A, *d_B, *d_C;

// 1. 初始化 cuBLAS 句柄
cublasHandle_t handle;
CUBLAS_CHECK(cublasCreate(&handle));

// 2. 在 GPU 上分配內存
CUDA_CHECK(cudaMalloc((void**)&d_A, N * N * sizeof(float)));
CUDA_CHECK(cudaMalloc((void**)&d_B, N * N * sizeof(float)));
CUDA_CHECK(cudaMalloc((void**)&d_C, N * N * sizeof(float)));

// 3. 將數據從主機複製到設備
CUBLAS_CHECK(cublasSetMatrix(N, N, sizeof(float), h_A.data(), N, d_A, N));
CUBLAS_CHECK(cublasSetMatrix(N, N, sizeof(float), h_B.data(), N, d_B, N));
CUBLAS_CHECK(cublasSetMatrix(N, N, sizeof(float), h_C.data(), N, d_C, N)); // 複製初始 C (這裡都是零)

// 設置標量 alpha 和 beta
const float alpha = 1.0f;
const float beta = 0.0f;

// 4. 執行 cuBLAS 運算 (C = A * B)
// 注意:cuBLAS 使用列優先存儲,所以這裡我們調用 cublasSgemm (S 代表 float, ge 表示通用矩陣, mm 表示矩陣乘法)
// 參數順序為:handle, transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc
// 對於 C = A * B,如果 A 是 M x K,B 是 K x N,則 C 是 M x N
// 在這裡,M=N, K=N, N=N
// transa, transb 分別表示是否轉置 A 和 B
// lda, ldb, ldc 分別是 A, B, C 的 leading dimension (通常是行數或列數,取決於存儲順序)
CUBLAS_CHECK(cublasSgemm(handle,
CUBLAS_OP_N, // A 不轉置
CUBLAS_OP_N, // B 不轉置
N, // m (C 的行數)
N, // n (C 的列數)
N, // k (A 的列數 / B 的行數)
&alpha, // alpha
d_A, // A 矩陣在設備上的指針
N, // A 的 leading dimension
d_B, // B 矩陣在設備上的指針
N, // B 的 leading dimension
&beta, // beta
d_C, // C 矩陣在設備上的指針
N)); // C 的 leading dimension

// 5. 將數據從設備複製回主機
CUBLAS_CHECK(cublasGetMatrix(N, N, sizeof(float), d_C, N, h_C.data(), N));

// 打印結果 (可選)
std::cout << "Result C matrix:" << std::endl;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
std::cout << h_C[i * N + j] << " ";
}
std::cout << std::endl;
}

// 6. 釋放 GPU 內存
CUDA_CHECK(cudaFree(d_A));
CUDA_CHECK(cudaFree(d_B));
CUDA_CHECK(cudaFree(d_C));

// 7. 銷毀 cuBLAS 句柄
CUBLAS_CHECK(cublasDestroy(handle));

return 0;
}

編譯和運行​

nvcc -lcublas your_program.cu -o your_program
./your_program

總結​

cuBLAS 提供了一個高效且易於使用的接口,用於在 NVIDIA GPU 上執行 BLAS 運算。對於需要進行大量線性代數計算的應用程式(如深度學習、科學計算),使用 cuBLAS 可以顯著提高性能。

建立 Ruby Gem

這篇備忘錄記錄了如何建立 Ruby Gem。

資訊

Ruby Gem 是一種打包和分發 Ruby 庫的標準方式。它包含了代碼、文檔、測試和元數據,使得 Ruby 程式碼的共享和重用變得非常容易。

1. 使用 Bundler 建立 Gem 骨架​

Bundler 提供了一個方便的命令來生成一個新的 Gem 的基本結構。

bundle gem my_gem_name

這會創建一個名為 my_gem_name 的目錄,其中包含 Gem 的基本文件,例如:

  • my_gem_name.gemspec:Gem 的元數據文件。
  • lib/my_gem_name.rb:Gem 的主文件。
  • lib/my_gem_name/version.rb:版本文件。
  • Rakefile:Rake 任務,用於測試、發布等。
  • README.md:說明文檔。
  • LICENSE.txt:許可證文件。

2. 理解 Gem 結構​

my_gem_name.gemspec​

這是 Gem 的核心元數據文件。它包含了 Gem 的名稱、版本、描述、作者、依賴等信息。

# my_gem_name.gemspec
require_relative "lib/my_gem_name/version"

Gem::Specification.new do |spec|
spec.name = "my_gem_name"
spec.version = MyGemName::VERSION
spec.authors = ["Your Name"]
spec.email = ["[email protected]"]

spec.summary = "A short summary of MyGemName."
spec.description = "A longer description of MyGemName."
spec.homepage = "https://github.com/yourusername/my_gem_name" # 你的 GitHub 倉庫或其他主頁
spec.license = "MIT"
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")

# 指定哪些文件應該包含在 Gem 中
spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

# 運行時依賴
# spec.add_dependency "rack", "~> 2.0"

# 開發時依賴 (測試、文檔生成等)
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "minitest", "~> 5.0"
end

lib/my_gem_name.rb​

這是 Gem 的主入口文件,你可以在這裡定義你的模組和類。

# lib/my_gem_name.rb
require_relative "my_gem_name/version"

module MyGemName
class Error < StandardError; end
# 在這裡定義你的 Gem 邏輯
def self.greet(name)
"Hello, #{name} from MyGemName!"
end
end

lib/my_gem_name/version.rb​

這個文件用於定義 Gem 的版本號。

# lib/my_gem_name/version.rb
module MyGemName
VERSION = "0.1.0"
end

3. 開發你的 Gem​

在 lib/my_gem_name.rb 或 lib/my_gem_name/ 目錄下的其他文件中編寫你的 Ruby 程式碼。

範例:添加一個簡單的 Calculator 類。

# lib/my_gem_name/calculator.rb
module MyGemName
class Calculator
def add(a, b)
a + b
end

def subtract(a, b)
a - b
end
end
end

然後在 lib/my_gem_name.rb 中引入它:

# lib/my_gem_name.rb
require_relative "my_gem_name/version"
require_relative "my_gem_name/calculator" # 引入新的文件

module MyGemName
class Error < StandardError; end
def self.greet(name)
"Hello, #{name} from MyGemName!"
end
end

4. 測試你的 Gem​

Bundler 生成的骨架通常會包含一個 test/ 或 spec/ 目錄。 你可以使用 MiniTest 或 RSpec 來編寫測試。

範例 (test/my_gem_name_test.rb):

require "minitest/autorun"
require "my_gem_name"

class MyGemNameTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::MyGemName::VERSION
end

def test_it_greets_correctly
assert_equal "Hello, World from MyGemName!", MyGemName.greet("World")
end

def test_calculator_add
calculator = MyGemName::Calculator.new
assert_equal 5, calculator.add(2, 3)
end
end

運行測試:

bundle exec rake test

5. 建立和安裝你的 Gem​

建立 Gem​

bundle exec rake build

這會在 pkg/ 目錄下生成一個 .gem 文件。

本地安裝 Gem​

你可以將建立的 .gem 文件安裝到你的本地系統中,以便在其他項目中使用它:

gem install pkg/my_gem_name-0.1.0.gem

6. 發布你的 Gem (到 RubyGems.org)​

  1. 創建 RubyGems.org 帳戶:如果還沒有,在 RubyGems.org 上註冊一個帳戶。
  2. 登錄:
    gem push --help # 查看幫助
    你需要配置你的 API 密鑰。
  3. 推送到 RubyGems.org:
    bundle exec rake release
    或者直接使用 gem push:
    gem push pkg/my_gem_name-0.1.0.gem

確保你的 Gem 名稱在 RubyGems.org 上是唯一的。

總結​

建立 Ruby Gem 是一個相對直接的過程,Bundler 提供了很好的起點。通過理解 gemspec 文件、組織你的程式碼、編寫測試以及正確發布,你可以輕鬆地與 Ruby 社區分享你的程式碼。

建立自製發行版

這篇備忘錄記錄了如何建立自製的 Linux 發行版。

資訊

建立一個自製的 Linux 發行版是一個複雜的過程,它涉及選擇核心組件、配置構建環境、編譯軟體包、打包系統以及創建安裝介質。這通常是為了特定的目的或學習目的而進行的。

核心組件​

一個基本的 Linux 發行版通常包含以下核心組件:

  • Linux 核心 (Kernel):操作系統的核心。
  • Glibc (GNU C Library):C 語言標準庫。
  • Binutils (GNU Binary Utilities):編譯器工具鏈的一部分。
  • GCC (GNU Compiler Collection):編譯器。
  • Coreutils (GNU Core Utilities):基本命令行工具(ls, cp, mv 等)。
  • Bash (GNU Bash):Shell。
  • Filesystem Hierarchy Standard (FHS):文件系統佈局標準。
  • Init 系統:啟動和管理系統進程(例如 Systemd、SysVinit、OpenRC)。

建立步驟概覽​

建立自製發行版通常遵循類似於 Linux From Scratch (LFS) 的過程。以下是一個高階的步驟概覽:

1. 準備構建環境 (Host System)​

你需要一個現有的 Linux 系統作為主機來進行構建。這個主機系統將提供編譯工具和必要的庫。

2. 創建分區和掛載文件系統​

為你的新發行版創建獨立的分區(例如 /、/boot、swap)並將它們掛載到主機系統上的臨時目錄(例如 /mnt/lfs)。

3. 下載源碼​

下載所有你需要編譯的軟體包的源碼,例如 Linux 核心、Glibc、Binutils、GCC 等。

4. 構建基本的工具鏈 (Cross-Compilation)​

這是最關鍵也是最複雜的步驟之一。你需要構建一個獨立的、自給自足的工具鏈,它可以在新系統上編譯其他軟體。這通常涉及:

  • 構建 Binutils 的第一階段。
  • 構建 GCC 的第一階段(只包含 C)。
  • 構建 Glibc 的第一階段。
  • 重新構建 Binutils 和 GCC,使其鏈接到新的 Glibc。

5. 構建臨時系統​

使用新構建的工具鏈,編譯和安裝一系列基本的實用程式,例如:

  • Zlib
  • File
  • Readline
  • Bash
  • Coreutils
  • Sed
  • Tar
  • Xz
  • Grep
  • ...等

這一步的目的是創建一個最小但功能齊全的 Linux 環境,以便在其中繼續構建。

6. 進入新系統 (Chroot)​

使用 chroot 命令將根文件系統切換到你正在構建的新系統的目錄。從現在開始,所有命令都將在新系統的環境中執行。

chroot /mnt/lfs /usr/bin/env -i
HOME=/root TERM="$TERM"
PS1='(lfs chroot) \u:\w\$ '
PATH=/usr/bin:/usr/sbin
/bin/bash --login

7. 構建其餘的系統軟體包​

在新系統中,編譯和安裝其餘的關鍵軟體包,包括:

  • 完整的 Glibc
  • 完整的 GCC
  • Linux 核心標頭
  • M4
  • Ncurses
  • Procps
  • Util-linux
  • E2fsprogs (文件系統工具)
  • GRUB (引導加載器)
  • ...等

8. 配置系統​

配置網絡、用戶、密碼、時區、語言環境等。

9. 創建啟動腳本和配置文件​

  • /etc/fstab:文件系統掛載點。
  • /etc/passwd, /etc/group:用戶和組。
  • /etc/network/interfaces 或 systemd-networkd 配置。
  • Init 系統的配置。

10. 安裝核心​

編譯和安裝最終的 Linux 核心,並創建 initramfs。

11. 安裝引導加載器​

配置和安裝 GRUB,使其能夠引導你的新發行版。

12. 退出 Chroot 並清理​

從 chroot 環境中退出,卸載文件系統,並清理臨時文件。

13. 重啟並測試​

從新建立的發行版啟動系統。

進階主題​

  • 套件管理:考慮使用或開發一個簡單的套件管理器。
  • 桌面環境:安裝 Xorg、桌面環境(如 GNOME、KDE、XFCE)和相關應用程式。
  • Live CD/USB:創建一個可啟動的 Live 系統。

總結​

建立一個自製的 Linux 發行版是一項艱巨但非常有益的學習經歷,它能讓你深入理解 Linux 系統的底層運作方式。遵循 LFS 的方法是一個很好的起點,但也可以根據你的具體需求進行客製化和簡化。

安裝 Firefox 建置版本

Ubuntu 22.04 似乎預設安裝了 snap 版本的 Firefox,在某些環境下無法啟動,因此記錄如何安裝預先建置的 Firefox。

移除 apt / snap 版本的 Firefox​

sudo apt purge firefox
sudo snap remove firefox

安裝 Firefox 建置版本​

# 下載
wget "https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64&lang=ja" --trust-server-names

# 解壓縮
tar xvf firefox-*.tar.bz2

# 安裝
sudo cp -r firefox /usr/lib

# 建立執行檔的符號連結
sudo ln -s /usr/lib/firefox/firefox /usr/bin/firefox

# 下載並放置桌面捷徑檔案
sudo mkdir -p /usr/share/applications
sudo wget https://bit.ly/3Mwigwx -O /usr/share/applications/firefox.desktop

Ubuntu 上的 Dock

這篇備忘錄記錄了 Ubuntu 上的 Dock。

資訊

Ubuntu 的 Dock(也稱為啟動器或側邊欄)是 GNOME 桌面環境中的一個組件,它通常位於屏幕的左側,用於快速啟動應用程式和管理當前正在運行的窗口。

1. Dock 的基本操作​

  • 啟動應用程式:點擊 Dock 上的應用程式圖標即可啟動。
  • 切換窗口:如果應用程式有多個窗口,點擊圖標會顯示所有窗口的縮略圖,你可以選擇要切換的窗口。
  • 添加/移除應用程式:
    • 添加到 Dock:右鍵點擊 Dock 上正在運行的應用程式圖標,然後選擇「加入最愛」。
    • 從 Dock 移除:右鍵點擊 Dock 上的應用程式圖標,然後選擇「從最愛移除」。
  • 拖放:你可以將應用程式圖標拖放到 Dock 上進行重新排序。

2. 配置 Dock​

Ubuntu 提供了多種方式來配置 Dock 的行為和外觀。

A. 使用「設定」應用程式​

這是最簡單直觀的方法。

  1. 打開 設定。
  2. 導航到 外觀 (或在舊版本中是 Dock)。
  3. 你可以配置以下選項:
    • Dock 位置:將 Dock 放置在屏幕的左側、底部或右側。
    • Dock 大小:調整 Dock 圖標的大小。
    • 自動隱藏 Dock:當窗口最大化或接近 Dock 時,自動隱藏 Dock。
    • 顯示個人主目錄、磁碟機等:選擇是否在 Dock 上顯示可移動媒體和網絡卷。
    • 顯示應用程式菜單:在 Dock 上顯示應用程式菜單按鈕。

B. 使用 GNOME Tweaks 工具 (推薦用於更多自定義)​

GNOME Tweaks(以前稱為 GNOME Tweak Tool)提供了更多進階的選項來自定義 GNOME 桌面環境,包括 Dock。

  1. 安裝 GNOME Tweaks:
    sudo apt install gnome-tweaks
  2. 啟動 GNOME Tweaks: 在應用程式菜單中搜索「Tweaks」或在終端中運行 gnome-tweaks。
  3. 配置 Dock: 導航到「Extensions」(擴展),找到「Dash to Dock」擴展(如果已安裝,Ubuntu 默認的 Dock 實際上是 Dash to Dock 的一個變體)。 在這裡,你可以找到更多控制 Dock 行為的選項,例如:
    • 智能隱藏:更精細的自動隱藏控制。
    • 應用程式圖標行為:點擊應用程式圖標時的行為。
    • 自定義 Dock 主題:更改 Dock 的視覺樣式。

C. 使用命令行 (gsettings)​

對於更精確的控制或腳本化配置,你可以使用 gsettings 命令。

範例:

  • 將 Dock 放置在底部:
    gsettings set org.gnome.shell.extensions.dash-to-dock dock-position 'BOTTOM'
  • 設置 Dock 圖標大小:
    gsettings set org.gnome.shell.extensions.dash-to-dock dash-max-icon-size 32
  • 啟用自動隱藏:
    gsettings set org.gnome.shell.extensions.dash-to-dock autohide true

要查看所有可用的 Dash to Dock 設置:

gsettings list-keys org.gnome.shell.extensions.dash-to-dock

總結​

Ubuntu 的 Dock 是其桌面體驗不可或缺的一部分,它提供了方便的應用程式啟動和窗口管理功能。通過內置的「設定」應用程式、GNOME Tweaks 工具或命令行 gsettings,你可以根據自己的喜好靈活地自定義 Dock 的外觀和行為。

OpenLDAP 中 slappasswd 產生的 SSHA 是什麼?

slappasswd 指令是什麼?​

slappasswd 指令是用來為 OpenLDAP 產生密碼的工具,預設使用 SSHA 對密碼進行雜湊處理。

認證機制​

在 SSHA 中,產生的雜湊值最後 4 個位元組為鹽值(salt)。認證時,系統會將輸入的密碼與儲存的鹽值組合後產生雜湊,並比對是否與儲存的雜湊相符。

以下程式在輸入正確密碼(例如 admin)時,會產生與原始雜湊相同的結果。

require 'base64'
require 'digest'

pass = 'admin'
ssha = '{SSHA}23AUBfRZytVFNpe7onuFhyCSJOHRzCWh'
ssha =~ /{.+}(.+)/
salt256s = Base64.decode64(Regexp.last_match(1)).unpack('C*'[-4..-1])

salt = salt256s.pack('C*')
b_ssha = Digest::SHA1.digest(pass + salt)
Base64.strict_encode64(
(b_ssha.unpack('C*') + salt256s).pack('C*')
)

[EOL]

部落格環境遷移

我們已將部落格環境從 Jekyll 遷移至 Docusaurus。 我們計劃將 Jekyll 中點閱率較高的文章一併移轉過來。