跳至主要內容

cuBLAS 備忘錄

· 4 分鐘閱讀

這篇備忘錄記錄了 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

· 4 分鐘閱讀

這篇備忘錄記錄了如何建立 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.rblib/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 社區分享你的程式碼。

建立自製發行版

· 4 分鐘閱讀

這篇備忘錄記錄了如何建立自製的 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. 創建分區和掛載文件系統

為你的新發行版創建獨立的分區(例如 //bootswap)並將它們掛載到主機系統上的臨時目錄(例如 /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/interfacessystemd-networkd 配置。
  • Init 系統的配置。

10. 安裝核心

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

11. 安裝引導加載器

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

12. 退出 Chroot 並清理

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

13. 重啟並測試

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

進階主題

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

總結

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

安裝 Firefox 建置版本

· 1 分鐘閱讀

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

· 3 分鐘閱讀

這篇備忘錄記錄了 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 是什麼?

· 1 分鐘閱讀

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]

部落格環境遷移

· 1 分鐘閱讀

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

在 Python 偵測極值

· 2 分鐘閱讀

摘要

偵測訊號的極值。

訊號的產生

舉例產生一個偽造的訊號。

  • 3 [Hz] 的訊號 + 0.01 [Hz] 的訊號 + 雜訊
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import signal

t = np.arange(30 * 100) / 100
x = np.sin(2 * np.pi * t / 3) + np.random.randn(len(t)) * 0.2 + np.sin(2 * np.pi * t / 100)
df = pd.DataFrame({'Data': x}, index=pd.to_datetime(t, unit='s').time)

fig, ax = plt.subplots()
df.plot(ax=ax, xlim=['00:00:00', '00:00:10'])
plt.show()

tmp

檢查訊號的特性

def acorr(df: pd.DataFrame, ra: int = 3, fs=100):
x = np.correlate(df.Data.values, df.Data.values, mode='full')
t = (np.arange(len(x)) - len(x) / 2 + 0.5) / fs
x /= x.max()
fig, ax = plt.subplots()
ax.set_xlim(-ra, ra)
ax.set_ylim(0, 1)
ax.plot(t, x)
ax.set_title('Autocorrelation')
ax.grid(axis='x')
ax.set_xticks(np.linspace(-ra, ra, 2 * ra + 1))
plt.show()

acorr(df)

可以看出主要的訊號是 1/3 [Hz]。因此,對 1/3 [Hz] 施加低通濾波器。

tmp

套用低通濾波器並偵測極值

套用低通濾波器以便偵測極值。

如果在 1/3 [Hz] 施加低通濾波器仍無法正確偵測,請逐步降低頻率。

tmp

fs = 10
lpf = 0.1
b, a = signal.butter(5, lpf / fs * 2, 'low')
df['Filter'] = signal.filtfilt(b, a, df.Data.values)
max_idx = signal.argrelmax(df['Filter'].values)
min_idx = signal.argrelmin(df['Filter'].values)

df['max_index'] = False
df.iloc[max_idx[0], 2] = True
df['min_index'] = False
df.iloc[min_idx[0], 3] = True

fig, ax = plt.subplots()
df.plot(ax=ax)
ax.set_xlim(['00:00:00', '00:00:30'])
ax.scatter(
df.loc[df['max_index'], ['Filter']].index,
df.loc[df['max_index'], ['Filter']],
color='tab:orange',
zorder=3)
ax.scatter(
df.loc[df['min_index'], ['Filter']].index,
df.loc[df['min_index'], ['Filter']],
color='tab:green',
zorder=3)
plt.show()

C 語言的二維陣列處理

· 1 分鐘閱讀

用結構體將行數、列數與儲存數值的記憶體整合在一起,會更方便操作。

#include <stdio.h>
#include <stdlib.h>

typedef struct {
float *data;
int col_size;
int row_size;
} Mat;

void MatInit(Mat *mat, int row_size, int col_size) {
mat->row_size = row_size;
mat->col_size = col_size;
mat->data = (float *)calloc(row_size * col_size, sizeof(float));
}

float *MatAt(Mat *mat, int i, int j) {
return mat->data + i * mat->col_size + j;
}

void MatFree(Mat *mat) { free(mat->data); }

int main(void) {
Mat mat;
MatInit(&mat, 30, 5); // 初始化 30 行 5 列的矩陣
*MatAt(&mat, 0, 0) = 50; // 將第 0 行第 0 列設為 50
printf("%f\n", *MatAt(&mat, 0, 0)); // 顯示第 0 行第 0 列的數值

MatFree(&mat);

return 0;
}

從安裝腳本中得到錯誤的結果

· 3 分鐘閱讀

這篇備忘錄記錄了當我從安裝腳本中得到錯誤結果時的解決方法。


問題描述

有時,當我運行某些安裝腳本(例如來自 GitHub 的自動安裝腳本或官方提供的 install.sh)時,會遇到以下錯誤信息:

Got bad result from install script

這個錯誤通常表示腳本執行不成功,或者沒有返回預期的成功代碼。這可能是由於多種原因造成的,例如:

  • 網絡問題:下載資源失敗。
  • 權限問題:腳本沒有足夠的權限執行某些操作。
  • 依賴缺失:系統中缺少腳本所需的工具或庫。
  • 環境不兼容:腳本是為不同環境設計的,或者當前環境變量不正確。
  • 腳本本身的問題:腳本有 bug。

解決方案

1. 檢查網絡連接

確保你的設備連接到互聯網,並且沒有任何防火牆或代理設置阻止腳本訪問外部資源。

ping google.com

2. 檢查權限

確保你以正確的權限運行腳本。對於需要系統級更改的腳本,通常需要使用 sudo

sudo bash install.sh

3. 安裝缺失的依賴

仔細閱讀腳本的輸出,查找是否有關於缺少命令或庫的錯誤信息。通常,腳本作者會在 README 中列出先決條件。

常見的依賴包管理器命令:

  • Ubuntu/Debian:sudo apt install <package_name>
  • CentOS/Fedora:sudo yum install <package_name>sudo dnf install <package_name>
  • macOS (Homebrew):brew install <package_name>

4. 檢查環境變量

某些腳本依賴特定的環境變量。確保你的 PATH 設置正確,並且所有必要的環境變量都已導出。

你可以使用 env 命令查看當前的環境變量。

5. 逐行執行腳本 (調試)

如果上述方法都無效,你可以嘗試逐行執行腳本,以找出問題的確切位置。

  1. 打開腳本
    vim install.sh
  2. 在腳本開頭添加 set -x: 這會讓 Shell 打印出每個執行的命令,幫助你追蹤問題。
    #!/bin/bash
    set -x
    # ... 腳本其餘內容
  3. 運行腳本:觀察輸出,當腳本失敗時,最後一個打印的命令通常是導致問題的原因。

6. 查閱腳本的 GitHub 頁面或文檔

如果腳本來自開源項目,請訪問其 GitHub 儲存庫。查看 Issues 頁面,看看是否有其他人遇到過相同的問題,或者在 Wiki/文檔中查找是否有特定的故障排除指南。

7. 嘗試不同的安裝方式

如果腳本提供的安裝方式失敗,看看項目是否提供了替代的安裝方式,例如:

  • 手動編譯
  • Docker 容器
  • 包管理器安裝(如 apt, yum, brew, npm, pip 等)

總結

遇到 "Got bad result from install script" 錯誤時,不要慌張。系統地檢查網絡、權限、依賴和環境變量,並利用調試工具,通常可以找到問題的根源並解決它。