zig 编译 wasm

zig 编译为 wasm 给 js 调用

demo.zig

1
2
3
4
5
const std = @import("std");

export fn add(a: i32, b: i32) i32 {
return a + b;
}

编译

1
2
3
4
zig build-exe src/demo.zig -target wasm32-freestanding -fno-entry --export=add

# 验证
wasm-dis xxx.wasm | grep export

angular

ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import axios from "axios";

export class AppComponent {
result: number | null = null;
wasm: WebAssembly.Instance | null = null;

async ngOnInit() {
await this.loadWasm();
}

async loadWasm() {
try {
const response = await axios.get("/xxx/demo.wasm", {
responseType: "arraybuffer",
});
const bytes = new Uint8Array(response.data);
const module = await WebAssembly.instantiate(bytes);
this.wasm = module.instance;
} catch (error) {
console.error("err loading wasm:", error);
}
}

doAdd() {
if (this.wasm) {
console.log(this.wasm.exports);
const add = this.wasm.exports["add"] as (a: number, b: number) => number;
const result = add(5, 3);
this.result = result;
} else {
console.warn("wasm instance is not initialized.");
}
}
}
1
2
3
4
5
<div>
<h1>wasm with angular</h1>
<button (click)="doAdd()">call wasm</button>
<p>result: {{ result }}</p>
</div>

zig 构建相关

交叉编译

同时可以支持静态编译

查看

1
zig targets | grep musl

命令行交叉编译

1
zig build -Dtarget=x86_64-linux-musl --summary all --release=small

作为其它语言的交叉编译器

1
CC="zig cc -target x86_64-linux-musl" CXX="zig c++ -target x86_64-linux-musl"
1
CC="zig cc -target x86_64-linux-gnu.2.31" CXX="zig c++ -target x86_64-linux-gnu.2.31"

自定义构建

使用 musl, 无依赖

1
2
3
4
5
6
7
8
const default_target = std.Target.Query{
// .cpu_arch = std.Target.Cpu.Arch.x86_64,
// .os_tag = std.Target.Os.Tag.linux,
.abi = std.Target.Abi.musl,
};
const target = b.standardTargetOptions(.{
.default_target = default_target,
});

指定 release 使用方式

1
2
3
const optimize = b.standardOptimizeOption(.{
.preferred_optimize_mode = std.builtin.OptimizeMode.ReleaseSmall,
});

编译为动态库

build.zig

1
2
3
4
5
6
const lib = b.addSharedLibrary(.{
.name = "demo",
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});

zig基础用法

遍历

1
2
3
4
5
6
7
8
const std = @import("std");

pub fn main() !void {
const data_list = [_]u8{ 'a', 'b', 'c' };
for (data_list, 0..data_list.len) |item, index| {
std.log.debug("index: {d}, item: {c}", .{ index, item });
}
}

错误处理

普通用法

向上抛出错误

1
2
3
4
5
6
const std = @import("std");

pub fn main() !void {
const file = try std.fs.cwd().openFile("does_not_exist/foo.txt", .{});
defer file.close();
}

捕获错误

1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");

pub fn main() void {
const file = std.fs.cwd().openFile("does_not_exist/foo.txt", .{}) catch |err| {
std.log.debug("无法打开文件: {}", .{err});
return;
};
defer {
std.log.debug("错误的时候不会到这里", .{});
file.close();
}
}

自定义错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");

const MyError = error{
AAA,
BBB,
CCC,
};

fn demo_func(num: i32) !i32 {
if (num < 0) {
return MyError.AAA;
} else {
return num * num;
}
}

pub fn main() void {
const result = demo_func(-111) catch |err| {
std.log.debug("custom error reason {}", .{err});
return;
};
std.log.debug("result {d}", .{result});
}

errdefer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");

pub fn aa() !void {
errdefer {
std.log.debug("errdefer 清理垃圾", .{});
}
return error.aaa;
}

pub fn main() !void {
// try aa();

aa() catch |err| {
std.log.debug("捕获错误 {}", .{err});
};

std.log.debug("正常运行", .{});
}

泛型

1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");

const MyType = u64;

fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}

pub fn main() void {
const result = max(MyType, 11, 22);
std.log.debug("result {d}", .{result});
}

回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const std = @import("std");

const CallBackProc = fn (arg: u64) void;

fn demo(comptime callback_proc: CallBackProc, ms: u64) void {
callback_proc(ms);
}

fn on_callback(argument: u64) void {
std.log.debug("proc args {d}", .{argument});
}

pub fn main() void {
demo(on_callback, 10000);
}

类型转换

1
2
3
4
5
6
7
8
9
10
11
const std = @import("std");

pub fn main() !void {
const a: u64 = 111;
const b: u32 = @truncate(a);
std.log.debug("a {d} b {d}", .{ a, b });

const isok = false;
const val: u8 = @intCast(@intFromBool(isok));
std.log.debug("isok {d}", .{val});
}

内存分配器

建议把 allocator 当作为参数,类型是 std.mem.Allocator

  • page_allocator

通常用于处理大块内存的分配, 很慢, 不需要 deinit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const std = @import("std");
const expect = std.testing.expect;

test "page allocator" {
const allocator = std.heap.page_allocator;

const memory = demo(allocator) catch |err| {
std.debug.print("error occurred: {}\n", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • fba

不能使用堆内存的时候用,比如写内核的时候, 如果字节用完,会报 OutOfMemory 错误

不需要 deinit, alloc 出来的内存可以多次 free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const std = @import("std");
const expect = std.testing.expect;

test "fba" {
var buffer: [1000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();

const memory = demo(allocator) catch |err| {
std.log.debug("error occurred: {}", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • gpa

为安全设计的内存分配器

deinit 返回状态值,用来检测内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const std = @import("std");
const expect = std.testing.expect;

test "gpa" {
const config = .{};
var gpa = std.heap.GeneralPurposeAllocator(config){};
defer {
if (gpa.deinit() != .ok) {
@panic("发现内存泄漏");
}
}

const allocator = gpa.allocator();

const memory = demo(allocator) catch |err| {
std.log.debug("error occurred: {}", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • testing_allocator

用于测试用例里面检测 leak

运行 zig test xxx.zig 的时候才能用

1
const allocator = std.testing.allocator;
  • ArenaAllocator

用于短期内需要临时创建和销毁多个小对象

alloc 出来的内存会自动 free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const std = @import("std");

test "arena" {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer {
std.debug.print("准备 arena Allocator.deinit\n", .{});
arena.deinit();
}

const allocator = arena.allocator();

const m1 = try allocator.alloc(u8, 1);
const m2 = try allocator.alloc(u8, 10);
_ = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free arena Allocator\n", .{});
allocator.free(m1);
allocator.free(m2);
// allocator.free(m3);
}
}

封装

safe_gpa.zig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const std = @import("std");

pub const Gpa = struct {
gpa: std.heap.GeneralPurposeAllocator(.{}) = std.heap.GeneralPurposeAllocator(.{}){},

pub fn allocator(self: *Gpa) std.mem.Allocator {
return self.gpa.allocator();
}

pub fn deinit(self: *Gpa) void {
if (self.gpa.deinit() != .ok) {
@panic("发现内存泄漏");
}
}
};

pub fn init() Gpa {
return Gpa{};
}

main.zig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");
const safe_gpa = @import("safe_gpa.zig");

pub fn main() !void {
var gpa = safe_gpa.init();
defer gpa.deinit();

const gpa_allocator = gpa.allocator();

// 外面包一层线程安全的 allocator
var thread_safe_fba = std.heap.ThreadSafeAllocator{ .child_allocator = gpa_allocator };
const thread_safe_allocator = thread_safe_fba.allocator();

// 外面包一层 arena 分配器
var arena = std.heap.ArenaAllocator.init(thread_safe_allocator);
defer arena.deinit();
const allocator = arena.allocator();

const formatted_str = try std.fmt.allocPrint(allocator, "测试 {}", .{42});
// defer allocator.free(formatted_str);

std.log.debug("{s}", .{formatted_str});
}

指针

1
2
3
4
5
6
7
8
const std = @import("std");

pub fn main() !void {
var value: i32 = 42;
const ptr: *const i32 = &value;

std.log.debug("value: {}", .{ptr.*});
}

array_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");
const equal = std.mem.eql;
const ArrayList = std.ArrayList;
const allocator = std.heap.page_allocator;

pub fn main() !void {
var array_list = ArrayList(u8).init(allocator);
defer array_list.deinit();

try array_list.append('H');
try array_list.append('e');
try array_list.append('l');
try array_list.append('l');
try array_list.append('o');
try array_list.appendSlice(" World!");

std.log.debug("{s}", .{array_list.items});
}

array

array 固定大小

slice 动态大小

1
2
3
4
5
6
7
8
9
10
const std = @import("std");

pub fn abc(list: []u8) void {
std.log.debug("in abc {any}", .{list});
}

pub fn main() !void {
var list = [_]u8{ 11, 22, 33, 44 };
abc(list[0..]);
}

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");

const allocator = std.heap.page_allocator;

pub fn demo(list: []u8, data_list: []u8) []u8 {
for (list, 0..list.len) |_, index| {
data_list[index] = @truncate(index);
}

return data_list[0..];
}

pub fn main() void {
var list = [_]u8{ 11, 22, 33, 44 };
const data_list = allocator.alloc(u8, list.len) catch |err| {
std.log.debug("err {any}", .{err});
return;
};

defer allocator.free(data_list);
const aa = demo(list[0..], data_list);
std.log.debug("result {any}", .{aa});
}

enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const std = @import("std");
const expect = std.testing.expect;

const Result = enum(u32) {
aa,
bb = 1000,
cc = 1000000,
dd,
};

test "set enum ordinal value" {
try expect(@intFromEnum(Result.aa) == 0);
try expect(@intFromEnum(Result.bb) == 1000);
try expect(@intFromEnum(Result.cc) == 1000000);
try expect(@intFromEnum(Result.dd) == 1000001);
}

hash_map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const std = @import("std");
const expect = std.testing.expect;

const allocator = std.testing.allocator;

test "hash_map" {
const Point = struct { x: i32, y: i32 };

var hash_map = std.AutoHashMap(u32, Point).init(
allocator,
);
defer hash_map.deinit();

try hash_map.put(1525, .{ .x = 1, .y = -4 });
try hash_map.put(1550, .{ .x = 2, .y = -3 });
try hash_map.put(1575, .{ .x = 3, .y = -2 });
try hash_map.put(1600, .{ .x = 4, .y = -1 });

try expect(hash_map.count() == 4);

var sum = Point{ .x = 0, .y = 0 };
var iterator = hash_map.iterator();

while (iterator.next()) |item| {
std.log.debug("key {d}, x {d}, y {d}", .{ item.key_ptr.*, item.value_ptr.x, item.value_ptr.y });
sum.x += item.value_ptr.x;
sum.y += item.value_ptr.y;
}

try expect(sum.x == 10);
try expect(sum.y == -10);
}

json

标准库里面, 需要先定义 struct, 比较挫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const std = @import("std");

const Place = struct { lat: f32, long: f32 };

fn str_to_json(allocator: std.mem.Allocator, json_str: []u8) !Place {
const parsed = try std.json.parseFromSlice(
Place,
allocator,
json_str,
.{},
);
defer parsed.deinit();

const place = parsed.value;
return place;
}

fn json_to_str(allocator: std.mem.Allocator, json_obj: Place) ![]u8 {
var json_str = std.ArrayList(u8).init(allocator);
try std.json.stringify(json_obj, .{}, json_str.writer());

std.log.debug("{s}", .{json_str.items});

return json_str.items;
}

pub fn main() !void {
const allocator = std.heap.page_allocator;

const obj = Place{
.lat = 51.997664,
.long = -0.740687,
};
const json_str = try json_to_str(allocator, obj);
const json_obj = try str_to_json(allocator, json_str);
std.log.debug("{any}", .{json_obj.long});
}

log

1
2
3
4
5
6
7
const std = @import("std");

pub fn main() void {
const info = "hi";
const data_list = [_]u8{ 11, 22, 33, 44, 55 };
std.log.debug("info {s} data_list {any}", .{ info, data_list });
}

test 不支持 log

1
2
3
4
5
6
const std = @import("std");

test "debug log" {
// std.log.debug("log.debug", .{});
std.debug.print("debug.print\n", .{});
}

optional

用于表示一个值可能存在或者为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const std = @import("std");

pub fn main() !void {
// 创建 option
var demo_option: ?u8 = null;
demo_option = 111;

// 修改 option
if (demo_option) |*value| {
std.log.debug("set data to option", .{});
value.* += 1;
}

if (demo_option == null) {
std.log.debug("null\n", .{});
} else {
const val = demo_option.?;
std.log.debug("not null, is {d}", .{val});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");

var _gpa: ?std.heap.GeneralPurposeAllocator(.{}) = null;

pub fn get_allocator() std.mem.Allocator {
if (_gpa == null) {
_gpa = std.heap.GeneralPurposeAllocator(.{}){};
}

return _gpa.?.allocator();
}

pub fn deinit() void {
if (_gpa != null) {
_ = _gpa.?.deinit();
_gpa = null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");
const gpa = @import("gpa.zig");

pub fn main() !void {
const allocator = gpa.get_allocator();
defer gpa.deinit();

const formatted_str = try std.fmt.allocPrint(allocator, "测试 {}", .{42});
defer allocator.free(formatted_str);

std.debug.print("{s}\n", .{formatted_str});
}

string

zig 没有内置的 string 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
const std = @import("std");

const String = []const u8;

pub fn demo() String {
return "aaa";
}

pub fn main() !void {
const aa = demo();
const bb = "bbb";
std.log.debug("result {s} {s}", .{ aa, bb });
}

struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const std = @import("std");

const MyStruct = struct {
x: f32,
y: f32,
z: f32 = 222.0,

fn demo(self: *MyStruct) void {
const tmp = self.x;
self.x = self.y;
self.y = tmp;
}
};

pub fn main() !void {
var aa = MyStruct{
.x = 10,
.y = 20,
};
aa.demo();
std.log.debug("aa = {}", .{aa});
}

thread

1
2
3
4
5
6
7
8
9
10
11
12
13
const std = @import("std");
const expect = std.testing.expect;

fn threadProc(thread_arg: u32) void {
std.log.debug("{d} in thread", .{thread_arg});
}

pub fn main() !void {
const thread = try std.Thread.spawn(.{}, threadProc, .{11111});
_ = thread;
// thread.join();
std.time.sleep(1 * std.time.ns_per_s);
}

union

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const std = @import("std");

const Result = union(enum) {
a: u8,
b: f32,
c: bool,
};

pub fn main() !void {
var value = Result{
.b = 1.5,
};
value.b = 3.156;
std.log.debug("{d:.2}", .{value.b});
}

mise

多版本管理工具,支持多种后端,自动选择,更好用

安装命令

1
curl https://mise.run | sh

~/.profile

1
2
3
4
5
6
7
8
9
10
11
12
# 普通配置
eval "$(mise activate zsh)"

# cursor 下
if [[ "$TERM_PROGRAM" == "vscode" && "$CHROME_DESKTOP" == "cursor.desktop" && -z "$CURSOR_INITED" ]]; then
# echo "cursor shell"
export CURSOR_INITED=1
sh -c $SHELL
else
# 系统 shell
eval "$(mise activate zsh)"
fi

所在路径

1
2
3
4
$HOME/.local/bin/mise
$HOME/.config/mise/
$HOME/.local/share/mise/
$HOME/.local/state/mise/

搜索

1
2
3
4
5
mise search xxx
mise ls

# 可以看 tag
mise ls-remote node

安装并且写入 toml

1
2
3
4
5
mise use node@22
mise use -g node@lts

# 从 toml 里面删除
mise unuse node@22

安装卸载

1
2
mise install node@22
mise uninstall node@22

更新

1
mise self-update

virt-manager

权限问题

说明

Group 用途说明
libvirt virt-manager 管理虚拟机
virsh CLI 管理虚拟机
libvirt-qemu libvirt 启动 vm qemu-system-*
kvm 手动运行 qemu-system-xxx

查看 group 有没有

1
2
3
getent group kvm
getent group libvirt
getent group libvirt-qemu

如果你的 vm 目录不是标准目录,建议

1
/etc/libvirt/qemu.conf
1
user = "xxx"

网络配置

NAT

1
10.0.2.0/24

隔离网络

1
192.168.56.0/24

虚拟机 nic 里面添加两个网卡

nat 网卡

/etc/network/interfaces.d/nat

1
2
auto enp1s0
iface enp1s0 inet dhcp

或者

1
2
3
4
auto enp1s0
iface enp1s0 inet static
address 10.0.2.15/24
gateway 10.0.2.1

或者

1
2
3
4
5
auto enp1s0
iface enp1s0 inet static
address 10.0.2.15
netmask 255.255.255.0
gateway 10.0.2.1

host-only 网卡

/etc/network/interfaces.d/host_only

1
2
3
auto enp2s0
iface enp2s0 inet static
address 192.168.56.11/24

指定网关

如果双网卡都配置了网关,则需指定默认网关

否则由于加载顺序的原因,默认网关可能为 host-only,无法访问外网

1
/etc/network/interfaces

最后一行添加

1
ip route add 10.0.2.0/24 via 10.0.2.1 dev enp1s0

dns 配置

如果需要手动指定 dns

1
sudo systemctl stop NetworkManager

或者

1
/etc/NetworkManager/NetworkManager.conf
1
2
[main]
dns=none

手动设置 dns

1
/etc/resolv.conf
1
2
nameserver 223.5.5.5
nameserver 223.6.6.6

重启网络

1
sudo systemctl restart networking

检查路由

1
sudo route -n

vm 增强

用于共享剪贴板,分辨率自适应

物理机

debian

1
sudo apt install qemu-guest-agent

manjaro

1
yay -S spice-guest-tools-windows

虚拟机

win

虚拟机挂载驱动, 对应在物理机上的路径

1
/usr/share/spice-guest-tools/spice-guest-tools.iso

vm 最好使用 qxl 作为显卡,不然缩放会出问题

debian

虚拟机安装

1
sudo apt install spice-vdagent

manjaro

虚拟机安装

1
sudo pacman -S spice-vdagent

win7

必须在物理机上安装这个,否则无法安装 win7 虚拟机

1
pacman -S virtio-win

smb 共享

1
2
3
安装 smb 客户端
net use z: \\192.168.56.1\smb
net use * /del /y

远程桌面

win 远程 linux

rdp

linux 上配置,使用微软的远程工具连接

以 manjaro 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
echo "Updating system..."
sudo pacman -Syu

echo "Installing xrdp and xorgxrdp-git..."
sudo pacman -Sy yay base-devel xorg-xserver-devel
yay -S xrdp xorgxrdp

echo "Configuring Xwrapper..."
echo "allowed_users=anybody" | sudo tee -a /etc/X11/Xwrapper.config

echo "Configuring .xinitrc..."
sed -i 's/^\(SESSION=${1:-xfce-session}\)$/#\1 # original\nSESSION=${1:-xfce4-session}/' ~/.xinitrc
sed -i 's/^\s*\(local dbus_args=(--sh-syntax --exit-with-session)\)$/#\1 # original\nlocal dbus_args=(--sh-syntax)/' ~/.xinitrc
sed -i 's/^\(exec $(get_session "$1")\)$/#\1 # original\nexec $(get_session "$SESSION")/' ~/.xinitrc

echo "Enabling xrdp service..."
sudo systemctl enable --now xrdp.service

vnc

windows 用 vnc 客户端

安装

1
2
sudo pacman -S x11vnc
sudo apt install x11vnc -y

服务

1
/usr/local/lib/systemd/system/vnc.service
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=vnc
After=multi-user.target

[Service]
Type=simple
# ExecStart=x11vnc -auth guess -forever -loop -noxdamage -scale 0.9x0.9 -passwd 90909090 -repeat -shared -o /var/log/x11vnc.log
ExecStart=x11vnc -auth guess -forever -noxdamage -scale 0.9x0.9 -passwd 90909090

[Install]
WantedBy=multi-user.target

开机启动

1
systemctl enable vnc --now

linux 远程 win

一句话

1
rdesktop xx.xx.xx.xx:3389

ssh 的一些用法

配置密钥

生成公钥

最好指定密钥文件名,默认是在 ~/.ssh 下面

1
ssh-keygen -t rsa -b 4096 -C "xxx@xxx.com" -f ~/downloads/key

私钥权限必须是 600

1
chmod 600 ~/downloads/*

上传公钥

1
ssh-copy-id -i ~/downloads/key.pub root@192.168.56.11

服务器上公钥的保存路径

1
2
# /etc/ssh/sshd_config
AuthorizedKeysFile ~/.ssh/authorized_keys

用私钥连接

1
ssh -i ~/downloads/key root@192.168.56.11

免密登陆

启动 agent

1
2
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa

~/.profile

1
2
3
4
5
if ! pgrep -u "$USER" ssh-agent > /dev/null; then
eval "$(ssh-agent -s 2> /dev/null | grep -v 'Agent pid')" > /dev/null
trap 'ssh-agent -k > /dev/null 2>&1' EXIT
fi
ssh-add ~/downloads/keys/key > /dev/null 2>&1

连接

1
2
3
4
5
# 连接服务器 A
ssh -A root@xx.xx.xx.xx

# 成功以后,在当前 shell 内,连接服务器 B
ssh root@yy.yy.yy.yy

指纹更新

服务器无法登录,提示 fingerprint 更新

删除 ~/.ssh/known_hosts

或者

1
ssh-keygen -R $SERVER_IP:$SERVER_PORT

autossh

本地隧道

原来需要远程访问的服务,变为本地访问的服务

TARGET_IP 和 VPS_IP 可以不相同

1
2
3
4
5
autossh -M 0 -i ./keys/id_rsa \
-o "StrictHostKeyChecking no" \
-o "ServerAliveInterval 30" \
-CfNg -L $LOCAL_IP:$LOCAL_PORT:$TARGET_IP:$TARGET_PORT \
root@$VPS

把服务转发给其它机器

1
2
3
4
5
autossh -M 0 -i ./keys/id_rsa \
-o "StrictHostKeyChecking no" \
-o "ServerAliveInterval 30" \
-CfNg -R $NEW_SERVER_PORT:$OLD_SERVER_IP:$OLD_SERVER_PORT \
root@$NEW_SERVER_IP

远程 gui

远程 ssh 执行 gui,在本地显示

远程机器

1
2
3
4
5
# linux
sed -i 's/^#X11Forwarding no/X11Forwarding yes/' /etc/ssh/sshd_config

# freebsd
sed -i "" 's/^#X11Forwarding no/X11Forwarding yes/' /etc/ssh/sshd_config

本地

1
ssh -YC xxx@xx.xx.xx.xx

uv

uv 为项目创建隔离的虚拟环境

uvx 用于在临时环境中运行

镜像配置

1
export UV_INDEX="https://mirrors.aliyun.com/pypi/simple"

创建项目

1
uv init demo

指定版本

1
uv init demo --python 3.10

在当前目录创建虚拟环境

1
uv venv

指定 python的 版本

1
uv venv --python 3.10

增加依赖

1
2
3
4
5
# 这个会更新 pyproject.toml,推荐
uv add requests

# 这个 pip 是 uv 模拟的
uv pip install requests

生成依赖文件

1
2
uv pip freeze > requirements.txt
uv pip compile pyproject.toml -o requirements.txt

同步依赖

1
2
3
4
uv sync

uv pip sync ./pyproject.toml
uv pip sync ./requirements.txt

运行

基于当前 venv 运行

1
2
uv run xxx
uv run main.py

指定版本

查看

1
uv python list

用法

1
2
uv python install 3.10
uv python uninstall 3.10

版本缓存目录,如果有问题,删除即可

1
rm -rf $HOME/.local/share/uv/