-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 157 KB
/
index.json
1
[{"categories":["Golang"],"content":"如何编写清晰、地道的 Go 代码 ","date":"2024-08-15","objectID":"/effective-go/:0:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Formatting 格式化 在 Golang 中,gofmt 以包未处理对象而非源文件,它将 Go 程序按照标准风格缩进、对齐,保留注释并在需要时重新格式化。 Indentation 缩进:使用 制表符 Tab 缩进,gofmt 默认使用 Line length 行长度:Go 对行的长度没有限制 Parentheses 括号:控制结构(if, for, switch)在语法上并不需要圆括号 ","date":"2024-08-15","objectID":"/effective-go/:1:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Commentary 注释 Go 支持 C 风格的块注释 /* */ 和 C++ 风格的单行注释 //,其中,// 注释更常用,而 /* */ 则主要用于包的注释 godoc 即使一个程序,又是一个 Web 服务器,它对 Go 的源码进行处理,并提取包中的文档内容: 出现在顶级声明之前,且与该声明之间没有空行的注释,将与该声明一起被提出来,作为该条目的说明文档。 每个包都应包含一段包注释,即放置在包子句前的一个块注释。 对于包含多个文件的包,包注释只需出现在其中的任一文件中即可。 包注释应在整体上对该包进行介绍,并提供包的相关信息。 它将出现在 godoc 页面中的最上面,并为紧随其后的内容建立详细的文档。 /* Package regex implements a simple library for regular expressions. The syntax of the regular expressions accepted is: regexp: concatenation { '|' concatenation } concatenation: { closure } closure: term [ '*' | '+' | '?' ] term: '^' '$' '.' character '[' [ '^' ] character-range ']' '(' regexp ')' */ package regex ","date":"2024-08-15","objectID":"/effective-go/:2:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Names 命名 ","date":"2024-08-15","objectID":"/effective-go/:3:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Package names 包名 当一个包被导入后,包名就会成为内容的访问器 import \"bytes\",按照惯例,包应当以某个小写的单个单词命名,且不应使用下划线或驼峰记法。 例如,err 的命名就是出于简短考虑。 包名是导入时所需的唯一默认名称,它并不需要在所有源码中保持唯一,即便在少数发生冲突的情况下,也可为导入的包选择一个别名来局部使用。 另一个约定:包名应为其源码目录的基本名称。例如,src/pkg/encoding/base64 中的包应作为 encoding/basee64 导入,其包名应为 base64 而不是 encoding_base64 / encodingBase64. 长命名并不会使包更具有可读性,反而一份有用的说明文档通常比额外的长名更具价值。 ","date":"2024-08-15","objectID":"/effective-go/:3:1","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Getter / Setter Go 并不对 getter 和 setter 提供自动支持。 如将 Get 放入 getter 的名字中,既不符合习惯,也没有必要,但大写字母作为字段导出的 getter 是一个不错的选择,另外 Set 放入 setter 是个不错的选择。 type Object struct { ower string } func (o *Object) Ower() string { return o.ower } func (o *Object) SetOwer(s string) { o.ower = s } ","date":"2024-08-15","objectID":"/effective-go/:3:2","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Interface names 接口名 按照规定,只包含一个方法的接口应当以该方法的名称加上 er 后缀来命名,如 Reader / Writer / Formater 等。 字符串转换方法命名应为 String 而非 ToString ","date":"2024-08-15","objectID":"/effective-go/:3:3","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"MixedCaps 驼峰记法 Go 中约定使用驼峰记法 ","date":"2024-08-15","objectID":"/effective-go/:3:4","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"分号 和 C 一样,Go 的正式语法使用分号 ; 来结束语句,但 Go 的分号不一定出现在源码中,而是词法分析器会使用一条简单的规则来自动插入分号 规则:如在新行前的最后一个标记为标识符(int/float64等)、数值或字符串常量之类的基本字面或break、continue、fallthrough、return、++、--、)、} 之一,则词法分析器将始终在该标记后面插入分号,即如果新行前的标记为语句的末尾,则插入分号;。 通常,Go 程序只在诸如 for 循环子句这样的地方使用分号,来以此将初始化器、条件及增量元素分开; ","date":"2024-08-15","objectID":"/effective-go/:4:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Control structures 控制结构 Go 不再使用 do / while 循环,只有一个更为通用的 for, // C: for for init; condition; post { } // C: while for condition { } // C: for(;;) for { } // [12]aT, []vT, map[sting]any mT for key, value := range aT/vT/mT { } for key := range aT/vT/mT { } for _, value := range aT/vT/mT { } Go 没有逗号操作符,且 ++/-- 是语句而非表达式 for i, j := 0, len(aT) - 1; i \u003c j; i, j = i + 1, j - 1 { // Not: i++, j-- a[i], a[j] = a[j], a[i] } switch 更加灵活,其表达式无需为常量或整数,case 语句会自上而下逐一进行求值直至匹配为止,它不会自动下溯,但 case 可通过逗号分隔来列举相同的处理条件 break 语句会使 switch 提前终止 func unhex(c byte) byte { switch { case '0' \u003c= c \u0026\u0026 c \u003c= '9': return c - '0' case 'a' \u003c= c \u0026\u0026 c \u003c= 'f': return c - 'a' + 10 case 'A' \u003c= c \u0026\u0026 c \u003c= 'F': return c - 'A' + 10 } return 0 } func shouldEscape(c byte) bool { switch c { case ' ', '?', '\u0026', '=', '#', '+', '%': return true } return false } if 强制使用大括号,并且接受初始化语句 if err := file.Chmod(0664); err != nil { return err } ","date":"2024-08-15","objectID":"/effective-go/:5:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Function 函数 Go 与众不同的特性之一,就是函数和方法可以返回多个值,返回值或结果“形参”可被命名,并作常规变量使用。 Go 的 defer 语句用于预设一个函数调用(即延迟执行函数),该函数会在执行 defer 的函数返回之前立即执行。 被推迟的多个函数,会按照后进先出(LIFO)的顺序执行。 func Contents(filename string) (string, error) { f, err := os.Open(filename) if err != nil { return \"\", err } defer f.Close() var result []byte buf := make([]byte, 100) for { n, err := f.Read(buf[0:]) result = append(result, buf[0:n]...) if err != nil { if err == io.EOF { break } return \"\", err } } return string(result), nil } ","date":"2024-08-15","objectID":"/effective-go/:6:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Data 数据 new(T) 会为类型为 T 的新项分配已置零的内存空间, 并返回它的地址,也就是一个类型为 *T 的值(返回一个指针, 该指针指向新分配的,类型为 T 的零值)。 内建函数 make(T, args) 的目的不同于 new(T)。它只用于创建切片、映射和信道,并返回类型为 T(而非 *T )的一个已初始化 (而非置零)的值。 出现这种用差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。 // Allocates slice structure; *p == nil; rarely useful var p *[]int = new([]int) // The slice v now refers to a new array of 100 ints var v []int = make([]int, 100) // 惯用法 v := make([]int, 100) ","date":"2024-08-15","objectID":"/effective-go/:7:0","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Array 数组 数组主要用作切片的构件,主要特点: 数组是值,讲一个数组赋值给另一个数组会复制其所有元素 如将数组作为参数传入某个函数,则会收到该数组的一份副本而非指针 数组的大小是其类型的一部分 func Sum(a *[3]float64) (sum float64) { for _, v := range *a { sum += v } return } aV := [...]float64{1, 2, 0.7} fmt.Println(Sum(\u0026aV)) ","date":"2024-08-15","objectID":"/effective-go/:7:1","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Slice 切片 切片通过对数组进行封装,为数据序列提供了更通用、强大而方便的接口。 slice 保存了对底层数组的引用,如将某个 slice 赋值给另一个 slice,则他们会引用同一个数组。 若某个函数将一个切片作为参数传入,则它对该切片元素的修改对调用者而言同样可见, 这可以理解为传递了底层数组的指针 只要切片不超出底层数组的限制,它的长度就是可变的,只需将它赋予其自身的切片即可。切片的容量可通过内建函数 cap 获得,它将给出该切片可取得的最大长度。 若数据超出其容量,则会重新分配该切片,返回值即为所得的切片。 尽管 Append 可修改 slice 的元素,但切片自身(其运行时数据结构包含指针、长度和容量)是通过值传递的. 二维数组 一种是独立地分配每一个切片;而另一种就是只分配一个数组, 将各个切片都指向它 // 独立地分配每一个切片 pic := make([][]uint8, YSize) for i := range pic { // 一次一行 pic[i] = make([]uint8, XSize) } // 顶层 slice pic := make([][]uint8, YSize) // 分配一个大的切片来保存所有像素 pixels := make([]uint8, XSize*YSize) // 遍历行,从剩余像素切片的前面切出每一行 for i := range pic { pic[i], pixels = picxels[:XSize], pixels[XSize:] } ","date":"2024-08-15","objectID":"/effective-go/:7:2","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Golang"],"content":"Map 可以关联不同类型的值。其键可以是任何相等性操作符支持的类型, 如整数、浮点数、复数、字符串、指针、接口(只要其动态类型支持相等性判断)、结构以及数组。 切片不能用作映射键,因为它们的相等性还未定义。与切片一样, 映射也是引用类型。 若将映射传入函数中,并更改了该映射的内容,则此修改对调用者同样可见。 若试图通过映射中不存在的键来取值,就会返回与该映射中项的类型对应的零值 要删除 map 中的某项,可使用内建函数 delete,它以映射及要被删除的键为实参。 即便对应的键不在该 map 中,此操作也是安全的。 ","date":"2024-08-15","objectID":"/effective-go/:7:3","tags":["Golang"],"title":"Effective Go","uri":"/effective-go/"},{"categories":["Design Patten"],"content":"设计模式: 在特定环境下人们解决某类重复出现的一套成功或有效的解决方案. ","date":"2024-07-28","objectID":"/design-pattern/:0:0","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Design Patten"],"content":"I. 前言 软件设计模式(Design Pattern),是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。 简单来说,设计模式就是在一定环境下,用固定套路解决问题。 设计模式的基础:多态 ","date":"2024-07-28","objectID":"/design-pattern/:1:0","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Design Patten"],"content":"面向对象设计原则 \u003e 目的:高内聚、低耦合 如何同时提⾼⼀个软件系统的可维护性和可复⽤性是⾯向对象设计需要解决的核⼼问题之⼀。 ⾯向对象设计原则为⽀持可维护性复⽤⽽诞⽣,这些原则蕴含在很多设计模式中,它们是从许多设计⽅案中总结出的指导性原则。 单一职责原则: 类的职责单⼀,对外只提供⼀种功能,⽽引起类变化的原因都应该只有⼀个 开闭原则: 类的改动是通过增加代码进⾏的,⽽不是修改源代码 里式代换原则: 任何抽象类出现的地⽅都可以⽤他的实现类进⾏替换,实际就是虚拟机制,语⾔级别实现⾯向对象功能 依赖倒转原则: 依赖于抽象(接⼝),不要依赖具体的实现(类),也就是针对接⼝编程 接口隔离原则: 不应该强迫⽤户的程序依赖他们不需要的接⼝⽅法。⼀个接⼝应该只提供⼀种对外功能,不应该把所有操作都封装到⼀个接⼝中去 合成复用原则: 如果使⽤继承,会导致⽗类的任何变换都可能影响到⼦类的⾏为。如果使⽤对象组合,就降低了这种依赖关系。对于继承和组合,优先使⽤组合 迪米特法则: ⼀个对象应当对其他对象尽可能少的了解,从⽽降低各个对象之间的耦合,提⾼系统的可维护性 ","date":"2024-07-28","objectID":"/design-pattern/:1:1","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Design Patten"],"content":"II. 分类 创建型(Creational)模式:如何创建对象 模式名称 用途 单例模式 🌟🌟🌟🌟 保证一个类仅有一个实例,并提供一个访问它的全局访问点 简单工厂方法 🌟🌟🌟 通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的基类 抽象工厂方法 🌟🌟🌟🌟🌟 提供一个创建一系列相关或相互依赖的接口,而无需指定它们具体的类 原型模式 ⽤原型实例指定创建对象的种类,并且通过拷⻉这些原型创建新的对象 建造者模式 将⼀个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示 结构型(Structural)模式:如何实现类或对象的组合 模式名称 用途 适配器模式 🌟🌟🌟🌟 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 桥接模式 将抽象部分与实际部分分离,使它们可以独立的变化 组合模式 🌟🌟🌟🌟 将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,使得用户对单个对象和组合对象的使用具有一致性 装饰模式 🌟🌟🌟 动态地给一个对象添加一些额外的职责:就增加功能来说,此模式比生成子类更加灵活 外观模式 🌟🌟🌟🌟🌟 为子系统的一组接口提供一个一致的界面,此模式定义了一个高层次接口,使得这一子系统更容易使用 享元模式 以共享的方式高效的支持大量的细粒度的对象 代理模式 为其他对象提供一种代理以控制这个对象的访问 行为型(Behavioral)模式:类或对象如何交互以及如何分配指责 ","date":"2024-07-28","objectID":"/design-pattern/:2:0","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Design Patten"],"content":"III. 创建型设计模式 ","date":"2024-07-28","objectID":"/design-pattern/:3:0","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Design Patten"],"content":"1. 简单工厂模式 亦称:虚拟构造函数、Virtual Constructor、Factory Method 意图:它是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型 NOTE: 由于 Golang 中缺少类和继承等 OOP 特性,因此,无法使用 Go 来实现经典的工厂方法模式,但我们仍能实现基础版本,即简单工厂。 ","date":"2024-07-28","objectID":"/design-pattern/:3:1","tags":["Design Patten"],"title":"Design Pattern","uri":"/design-pattern/"},{"categories":["Golang","Project Layout","Stardard"],"content":"Go 应用程序项目的基本布局介绍 ","date":"2024-07-13","objectID":"/go-proj-layout/:0:0","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"Go Module 从 Go 1.14 版本开始,除非存在特定不使用 Go Modules 的理由,否则请使用,并且一旦使用,就无需再担心 $GOPATH 以及项目的存放位置。 ","date":"2024-07-13","objectID":"/go-proj-layout/:1:0","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"Go 目录 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:0","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"/cmd /cmd,本项目的主干,其中每一个应用程序的目录名应该与你想要的可执行程序的名称相对应,例如 /cmd/myApp。 在 /cmd 目录下,不应该放置太多代码: 如果认为代码可以导入并可在其他项目中使用,那么它应该位于 /pkg 目录中. 如果代码不是可重用的,或者不希望其他人重用它,那么应该位于 /internal 目录中. 该目录下,通常有一个小的 main 函数,从 /internal 和 pkg 目录中导入和调用代码,除此之外没有别的东西. 微服务中的 app 服务类型分为4类:interface、service、job、admin |---cmd | |---kydenapp-admin | |---kydenapp-interface | |---kydenapp-job | |---kydenapp-service | |---kydenapp-task interface: 对外的 BFF 服务,接受来自用户的请求,比如暴露了 HTTP/gRPC 接口 service: 对内的微服务,仅接受来自内部其他服务或网关的请求,比如暴露了 gRPC 接口只对内服务 admin: 区别于 service,更多是面向运营测的服务,通常数据权限更高,隔离带来更好的代码级别安全 job: 流式任务处理的服务,上游一般依赖 message broker task: 定时任务,类似 cronjob,部署到 task 托管平台中 /cmd 应用目录负责程序的: 启动、关闭、配置初始化等 DTO(Data Transfer Object),数据传输对象,这个概念来源于 J2EE 的设计模式, 但这里泛指用于展示层/API层与服务层(业务逻辑层)之间的数据传输对象。 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:1","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"internal internal,私有应用程序和库代码,它是不希望其他人在其应用程序或库中导入的代码。 该目录由 Go 强制执行,确保私有包不可导入。 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:2","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"pkg /pkg,外部应用程序可以使用的库代码(例如 /pkg/mypubliclib). 如果应用程序项目真的很小,并且额外的嵌套并不能增加多少价值(除非你真的想要:-),那就不要使用它。 当它变得足够大时,根目录会变得非常繁琐时(尤其是当你有很多非 Go 应用组件时),请考虑使用。 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:3","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"api /api,协议定义目录,(xxapi.proto) protobuf 文件,以及生成的 go 文件。 通常把 api 文档直接在 proto 文件中描述。 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:4","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"configs 配置文件模版或默认配置 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:5","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"scripts 执行各种构建、安装、分析等操作的脚本,是的根级别的 Makefile 变得小而简单 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:6","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"test 额外的外部测试应用程序和测试数据 Go 会忽略以 . 或 _ 开头的目录和文件 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:7","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"docs 设计和用户文档(godoc 生成的文档除外) ","date":"2024-07-13","objectID":"/go-proj-layout/:2:8","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"tools 项目的支持工具,可以从 /pkg 和 /internal 目录导入代码 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:9","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"examples 应用程序和/或公共库的示例 ","date":"2024-07-13","objectID":"/go-proj-layout/:2:10","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"third_party 外部辅助工具,分叉代码和其他第三方工具(例如 Swagger UI) ","date":"2024-07-13","objectID":"/go-proj-layout/:2:11","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"assets 与存储库一起使用的其他资源(图像、徽标等) 按理来说我们不应该 src 目录,但有些 Go 项目拥有一个 src 文件夹,这通常发生在开发人员具有 Java 背景 $GOPATH 环境变量指向你的(当前)工作空间(默认情况下,它指向非 windows 系统上的 $HOME/go),这个工作空间包括顶层 /pkg, /bin 和 /src 目录,而实际项目最终是 /src 下的一个子目录,即 /xxx/workspace/src/proj/src/xxx.go(Go 1.11 之后,项目 proj 可以放在 GOPATH 之外). ","date":"2024-07-13","objectID":"/go-proj-layout/:2:12","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Project Layout","Stardard"],"content":"Reference https://talks.golang.org/2014/names.slide https://golang.org/doc/effective_go.html#names https://blog.golang.org/package-names https://go.dev/wiki/CodeReviewComments Style guideline for Go packages (rakyll/JBD) ","date":"2024-07-13","objectID":"/go-proj-layout/:3:0","tags":["Golang","Project Layout","Stardard"],"title":"Stardard Go Project Layout","uri":"/go-proj-layout/"},{"categories":["Golang","Context"],"content":"Context:当需要在多个 Goroutine 中传递上下文信息时,可以使用 Context 实现 ","date":"2024-06-01","objectID":"/context-of-go/:0:0","tags":["Golang","Context"],"title":"Context of Go","uri":"/context-of-go/"},{"categories":["Golang","Context"],"content":"Context Context 除了用来传递上下文信息,还可以用来传递终结执行子任务的相关信号,终止多个执行子任务的 Goroutine。 context.Context 接口数据结构: // A Context carries a deadline, a cancellation signal, and other values across // API boundaries. // // Context's methods may be called by multiple goroutines simultaneously. type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this // context should be canceled. Done may return nil if this context can // never be canceled. Successive calls to Done return the same value. // The close of the Done channel may happen asynchronously, // after the cancel function returns. // // WithCancel arranges for Done to be closed when cancel is called; // WithDeadline arranges for Done to be closed when the deadline // expires; WithTimeout arranges for Done to be closed when the timeout // elapses. // // Done is provided for use in select statements: // // // Stream generates values with DoSomething and sends them to out // // until DoSomething returns an error or ctx.Done is closed. // func Stream(ctx context.Context, out chan\u003c- Value) error { // for { // v, err := DoSomething(ctx) // if err != nil { // return err // } // select { // case \u003c-ctx.Done(): // return ctx.Err() // case out \u003c- v: // } // } // } // // See https://blog.golang.org/pipelines for more examples of how to use // a Done channel for cancellation. Done() \u003c-chan struct{} // If Done is not yet closed, Err returns nil. // If Done is closed, Err returns a non-nil error explaining why: // Canceled if the context was canceled // or DeadlineExceeded if the context's deadline passed. // After Err returns a non-nil error, successive calls to Err return the same error. Err() error // Value returns the value associated with this context for key, or nil // if no value is associated with key. Successive calls to Value with // the same key returns the same result. // // Use context values only for request-scoped data that transits // processes and API boundaries, not for passing optional parameters to // functions. // // A key identifies a specific value in a Context. Functions that wish // to store values in Context typically allocate a key in a global // variable then use that key as the argument to context.WithValue and // Context.Value. A key can be any type that supports equality; // packages should define keys as an unexported type to avoid // collisions. // // Packages that define a Context key should provide type-safe accessors // for the values stored using that key: // // // Package user defines a User type that's stored in Contexts. // package user // // import \"context\" // // // User is the type of value stored in the Contexts. // type User struct {...} // // // key is an unexported type for keys defined in this package. // // This prevents collisions with keys defined in other packages. // type key int // // // userKey is the key for user.User values in Contexts. It is // // unexported; clients use user.NewContext and user.FromContext // // instead of using this key directly. // var userKey key // // // NewContext returns a new Context that carries value u. // func NewContext(ctx context.Context, u *User) context.Context { // return context.WithValue(ctx, userKey, u) // } // // // FromContext returns the User value stored in ctx, if any. // func FromContext(ctx context.Context) (*User, bool) { // u, ok := ctx.Value(userKey).(*User) // return u, ok // } Value(key any) any } Deadline:返回 Context 被取消的时间,也就是完成工作的截至日期; Done:返回一个 channel,这个 channel 会在当前工作完成或者上下文被取消之后关闭,多次调用 Done 方法会返回同一个 channel; Err:放回 Context 结束的原因,只会在 Done 返回的 channel 被关闭时才会返回非空的值,如果 Context 被取消,会返回 Canceled 错误;如果 Context 超时,会返回 DeadlineExceeded 错误; Value:可用于从 Context 中获取传递的键值信息; ","date":"2024-06-01","objectID":"/context-of-go/:1:0","tags":["Golang","Context"],"title":"Context of Go","uri":"/context-of-go/"},{"categories":["Golang","Context"],"content":"Example 在 Web 请求的处理过程中,一个请求可能启动多个 goroutine 协同工作,这些 goroutine 之间可能需要共享请求的信息,且当请求被取消或者执行超时时,该请求对应的所有 goroutine 都需要快速结束,释放资源,Context 就是为了解决上述场景而开发的。 package main import ( \"context\" \"fmt\" \"time\" ) const DB_ADDRESS = \"db_address\" const CALCULATE_VALUE = \"calculate_value\" func readDB(ctx context.Context, cost time.Duration) { fmt.Println(\"DB address is \", ctx.Value(DB_ADDRESS)) select { case \u003c-time.After(cost): fmt.Println(\"read data from db\") case \u003c-ctx.Done(): fmt.Println(ctx.Err()) } } func calculate(ctx context.Context, cost time.Duration) { fmt.Println(\"calculate value is\", ctx.Value(CALCULATE_VALUE)) select { case \u003c-time.After(cost): // 模拟数据计算 fmt.Println(\"calculate finish\") case \u003c-ctx.Done(): fmt.Println(ctx.Err()) // 任务取消的原因 // 一些清理工作 } } func main() { ctx := context.Background() // Add Context info ctx = context.WithValue(ctx, DB_ADDRESS, \"localhost:3306\") ctx = context.WithValue(ctx, CALCULATE_VALUE, \"123\") ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() go readDB(ctx, time.Second*4) go calculate(ctx, time.Second*4) time.Sleep(time.Second * 5) } 使用 Context,能够有效地在一组 goroutine 中传递共享值、取消信号、deadline 等信息,及时关闭不需要的 goroutine。 ","date":"2024-06-01","objectID":"/context-of-go/:2:0","tags":["Golang","Context"],"title":"Context of Go","uri":"/context-of-go/"},{"categories":["Golang","Context"],"content":"Reference Go Context ","date":"2024-06-01","objectID":"/context-of-go/:3:0","tags":["Golang","Context"],"title":"Context of Go","uri":"/context-of-go/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"深入理解 Golang Escape Analysis:变量何时逃逸? ","date":"2024-05-19","objectID":"/golang-escape-analysis/:0:0","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"I. Golang Escape Ananlysis Golang 编译器会自动决定把一个变量放在堆栈还是栈上,即逃逸分析(Escape Analysis). Go 声称逃逸分析可以释放程序员关于内存的使用限制,更多地关注程序逻辑本身。 ","date":"2024-05-19","objectID":"/golang-escape-analysis/:1:0","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"II. 逃逸规则 众所周知,当变量需要使用堆(heap)空间时,那么变量就应该进行逃逸。 一般情况下,一个引用对象中的引用类成员进行赋值,可能出现逃逸现象:可以理解为访问一个引用对象实际上底层就是通过一个指针来间接的访问,但如果再访问里面的引用成员就会有第二次间接访问,这样操作这部分对象的话,极大可能会出现逃逸的现象。 Golang 中的引用类型有函数类型 func(),接口类型 interface,切片类型 slice,字典类型 map,管道类型 channel,指针类型 * 等。 ","date":"2024-05-19","objectID":"/golang-escape-analysis/:2:0","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"逃逸场景I: []interface{} 使用 [] 赋值 []interface{} 数据类型,必定逃逸 package main import \"fmt\" func main() { data := []interface{}{1, 2} val := data[0] fmt.Printf(\"%v\\n\", val) data[0] = 3 } $ go build -v -gcflags='-m' ./main.go command-line-arguments # command-line-arguments ./main.go:8:12: inlining call to fmt.Printf ./main.go:6:23: []interface {}{...} does not escape ./main.go:6:24: 1 escapes to heap ./main.go:6:27: 2 escapes to heap ./main.go:8:12: ... argument does not escape ./main.go:9:12: 3 escapes to heap ","date":"2024-05-19","objectID":"/golang-escape-analysis/:2:1","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"逃逸场景II: map[string]interface{} 使用 [] 赋值 map[string]interface{} 数据类型,必定逃逸 package main import \"fmt\" func main() { dat := make(map[string]interface{}) dat[\"BlogName\"] = \"Kyden's Blog\" val := dat[\"BlogName\"] fmt.Printf(\"%v\\n\", val) } $ go build -v -gcflags='-m' ./main.go command-line-arguments # command-line-arguments ./main.go:9:12: inlining call to fmt.Printf ./main.go:6:13: make(map[string]interface {}) does not escape ./main.go:7:20: \"Kyden's Blog\" escapes to heap ./main.go:9:12: ... argument does not escape ","date":"2024-05-19","objectID":"/golang-escape-analysis/:2:2","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"content":"逃逸场景 III: map[interface{}]interface{} 使用 [] 赋值 map[interface{}]interface{} 数据类型,必定逃逸 package main import ( \"fmt\" ) func main() { dat := make(map[interface{}]interface{}) dat[\"BlogName\"] = \"Kyden's Blog\" val := dat[\"BlogName\"] fmt.Printf(\"%v\\n\", val) } $ go build -v -gcflags='-m' ./main.go command-line-arguments # command-line-arguments ./main.go:11:12: inlining call to fmt.Printf ./main.go:8:13: make(map[interface {}]interface {}) does not escape ./main.go:9:6: \"BlogName\" escapes to heap ./main.go:9:20: \"Kyden's Blog\" escapes to heap ./main.go:10:13: \"BlogName\" does not escape ./main.go:11:12: ... argument does not escape 逃逸场景 IV:map[string][]string map[string][]string 数据类型,赋值会发生 []string 逃逸 package main import ( \"fmt\" ) func main() { dat := make(map[string][]string) dat[\"BlogName\"] = []string{\"Kyden's Blog\"} val := dat[\"BlogName\"] fmt.Printf(\"%v\\n\", val) } $ go build -v -gcflags='-m' ./main.go command-line-arguments # command-line-arguments ./main.go:11:12: inlining call to fmt.Printf ./main.go:8:13: make(map[string][]string) does not escape ./main.go:9:28: []string{...} escapes to heap ./main.go:11:12: ... argument does not escape ./main.go:11:21: val escapes to heap 逃逸场景 V:[]*int []*int 数据类型,赋值的右值会发生逃逸 package main import \"fmt\" func main() { dat := []*int{nil} a := 10 dat[0] = \u0026a fmt.Printf(\"%v\\r\\n\", *dat[0]) fmt.Printf(\"%v\\r\\n\", dat[0]) } $ go build -v -gcflags='-m' ./main.go command-line-arguments # command-line-arguments ./main.go:9:12: inlining call to fmt.Printf ./main.go:10:12: inlining call to fmt.Printf ./main.go:7:2: moved to heap: a ./main.go:6:15: []*int{...} does not escape ./main.go:9:12: ... argument does not escape ./main.go:9:23: *dat[0] escapes to heap ./main.go:10:12: ... argument does not escape 逃逸场景 VI:func(*int) func(*int) 数据类型,进行函数赋值,会使传递的形参逃逸 package main import \"fmt\" func f(a *int) { fmt.Printf(\"%v\\n\", *a) return } func main() { a := 10 fn := f fn(\u0026a) fmt.Printf(\"a = %v\\n\", a) } $ go build -v -gcflags='-m' ./main.go # command-line-arguments ./main.go:6:12: inlining call to fmt.Printf ./main.go:14:12: inlining call to fmt.Printf ./main.go:5:8: a does not escape ./main.go:6:12: ... argument does not escape ./main.go:6:21: *a escapes to heap ./main.go:14:12: ... argument does not escape ./main.go:14:25: a escapes to heap ","date":"2024-05-19","objectID":"/golang-escape-analysis/:2:3","tags":["Golang","逃逸分析","Escape Analysis","Heap","Stack"],"title":"Golang Escape Analysis","uri":"/golang-escape-analysis/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"深入理解 GMP: Go 的调度流程本质上是一个生产-消费流程. ","date":"2024-05-15","objectID":"/gmp-of-go/:0:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"I. 进程(Process)、线程 (Thread) 与协程 (Co-routine) ","date":"2024-05-15","objectID":"/gmp-of-go/:1:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"进程 Process 在操作系统中,进程使用进程控制块 (PCB, Process Control Block) 数据结构 task_struct 来描述,PCB 是进程存在的唯一标识。 进程是指在系统中正在运行的一个应用程序,程序一旦运行就是进程; 进程可以认为是程序执行的一个实例,进程是系统进行资源分配的最小单位,且每个进程拥有独立的地址空间; 一个进程无法直接访问到另一个进程的变量和数据结构,如果希望一个进程去访问另一个进程的资源,需要使用进程间的通信,如fifo、pipe、signal、socket 等; 进程调度算法:先来先服务调度算法、短作业优先调度算法、最短剩余作业优先调度算法、最高响应比优先调度算法、最高优先级优先调度算法、时间片轮转算法(公平调度,$20 - 50 ms$)、多级反馈队列调度算法($最高优先级 + 时间片轮转$); ","date":"2024-05-15","objectID":"/gmp-of-go/:1:1","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"线程 Thread 用户态线程,是基于用户态的线程管理库来实现的,线程控制块 (Thread Control Block) 也是在库里实现,操作系统只能看到整个进程的PCB,即进程与用户线程属于多对一的模型。 内核态线程(Thread),是由操作系统管理,对应的 TCB 存储在操作系统里,且其创建、销毁、调度都由操作系统完成; 轻量级线程 LWP(Light-weight process),是由内核支持的用户线程,一个进程可以有一个或多个 LWP,每个 LWP 是跟内核线程一对一映射的,即 LWP 都是由一个内核线程支持,而且 LWP 是由内核管理并像普通进程一样被调度。 在大多数系统中,LWP 和 普通进程的区别在于,LWP 只有一个最小的执行上下文和调度程序所需的统计信息。 线程是进程的一个实体,是进程的一条执行路径; 线程是比进程更小的独立运行的基本单位 一个程序至少存在一个进程,一个进程可以有多个($\u003e=1$)线程 进程与线程的区别 进程是资源(包括内存、打开的文件等)分配的单位,线程是 CPU 调度的单位; 进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈; 同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间; 同一进程内的线程共享本地的资源,但是进程之间的资源是独立的; 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程崩溃,即多进程比多线程健壮; 进程切换,消耗的资源大(主要是虚拟地址空间的切换开销),线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系; 多进程、多线程都可以并发执行,线程能减少并发执行的时间和空间开销; 每个独立的进程有一个程序入口、程序出口;线程不能独立运行,必须依存于应用程序中,有应用程序提供多个线程执行控制; ","date":"2024-05-15","objectID":"/gmp-of-go/:1:2","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"协程 Co-routine 协程,又称 “微线程”,表现为一个可以 suspend 和 resume 的函数。 实现协程的关键点:在于如何保存、恢复和切换上下文,协程切换只涉及基本的CPU上下文切换(CPU寄存器). 所有的协程共用的都是一个栈,即系统栈,也就也不必我们自行去给协程分配栈,因为是函数调用,我们当然也不必去显示的保存寄存器的值; Co-routine 分类 有栈 (stackful) 协程:实现类似于内核态线程的实现,不同协程的切换还是要切换对应的栈上下文,只是不用陷入内核,例如 goroutine、libco 无栈 (stackless) 协程:无栈协程的上下文都会放到公共内存中,在协程切换时使用状态机来切换,而不用切换对应的上下文(都已经在堆中),相比有栈协程更轻量,例如 C++20、Rust、JavaScript;==本质就是一个状态机(state machine),即同一协程协程的切换本质不过是指令指针寄存器的改变== Co-routine 特点 一个线程可以有多个协程;协程不是被操作系统内核管理,而是完全由程序控制; 协程的开销远远小于线程;协程拥有自己的寄存器上下文和栈,在进行协程调度时,将寄存器上下文和栈保存到其他地方,在切换回来时恢复先前保存的寄存器上下文和栈; 每个协程表示一个执行单元,有自己的本地数据,与其他协程共享全局数据和其他资源; 跨平台、跨体系架构、无需线程上下文切换的开销、方便切换控制流,简化编程模型; 协程的执行效率极高,和多线程相比,线程数量越多,协程的性能优势越明显; ","date":"2024-05-15","objectID":"/gmp-of-go/:1:3","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"II. GMP Golang 为提供更加容易使用的并发工具,基于 GMP 模型实现了 goroutine 和 channel。 Goroutine 属于 Co-routine 的概念,非常轻量,一个 goroutine 初始空间只占几 KB 且可伸缩,使得在有限空间内支持大量 goroutine 并发。 Channel 可以独立创建和存取,在不同的 Goroutine 中传递使用,作为队列,遵循 FIFO 原则,同时保证同一时刻只能有一个 goroutine 访问。 channel 作为一种引用类型,声明时需要指定传输数据类型,声明形式如下(T 是 channel 可传输的数据类型): // 声明 channel var ch chan T // 双向 channel var ch chan\u003c- T // 只能发送 msg 的 channel var ch \u003c-chan T // 只能接收 msg 的 channel // 创建 channel ch := make(chan T, capicity) // 双向 channel ch := make(chan\u003c- T, capicity) // 只能发送 msg 的 channel ch := make(\u003c-chan T, capicity) // 只能接收 msg 的 channel // 访问 channel ch \u003c- msg // 发送 msg msg := \u003c-ch // 接收 msg msg, ok := \u003c-ch // 接收 msg,同时判断 channel 是否接收成功 close(ch) // 关闭 channel ","date":"2024-05-15","objectID":"/gmp-of-go/:2:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"Golang 调度 调度组件 G:Goroutine,一个计算任务. 由需要执行的代码和其上下文组成,上下文包括:当前代码位置、栈空间(初始2K,可增长)、状态等。 M:Machine,系统线程,执行实体。与 C 语言中的线程相同,通过 clone 创建。 P: Processor,虚拟处理器,包含了 G 运行所需的资源,因此 M 必须获得 P 才能执行代码,否则必须陷入休眠(后台监控线程除外)。可理解为一种 token,有这个 token,才有在物理 CPU 核心上执行的权限。 相关数据结构定义如下: g 的数据结构: type g struct { // Stack parameters. // stack describes the actual stack memory: [stack.lo, stack.hi). // stackguard0 is the stack pointer compared in the Go stack growth prologue. // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption. // stackguard1 is the stack pointer compared in the //go:systemstack stack growth prologue. // It is stack.lo+StackGuard on g0 and gsignal stacks. // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash). stack stack // offset known to runtime/cgo stackguard0 uintptr // offset known to liblink stackguard1 uintptr // offset known to liblink _panic *_panic // innermost panic - offset known to liblink _defer *_defer // innermost defer m *m // current m; offset known to arm liblink sched gobuf syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc stktopsp uintptr // expected sp at top of stack, to check in traceback // param is a generic pointer parameter field used to pass // values in particular contexts where other storage for the // parameter would be difficult to find. It is currently used // in four ways: // 1. When a channel operation wakes up a blocked goroutine, it sets param to // point to the sudog of the completed blocking operation. // 2. By gcAssistAlloc1 to signal back to its caller that the goroutine completed // the GC cycle. It is unsafe to do so in any other way, because the goroutine's // stack may have moved in the meantime. // 3. By debugCallWrap to pass parameters to a new goroutine because allocating a // closure in the runtime is forbidden. // 4. When a panic is recovered and control returns to the respective frame, // param may point to a savedOpenDeferState. param unsafe.Pointer atomicstatus atomic.Uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid uint64 schedlink guintptr waitsince int64 // approx time when the g become blocked waitreason waitReason // if status==Gwaiting preempt bool // preemption signal, duplicates stackguard0 = stackpreempt preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule preemptShrink bool // shrink stack at synchronous safe point // asyncSafePoint is set if g is stopped at an asynchronous // safe point. This means there are frames on the stack // without precise pointer information. asyncSafePoint bool paniconfault bool // panic (instead of crash) on unexpected fault address gcscandone bool // g has scanned stack; protected by _Gscan bit in status throwsplit bool // must not split stack // activeStackChans indicates that there are unlocked channels // pointing into this goroutine's stack. If true, stack // copying needs to acquire channel locks to protect these // areas of the stack. activeStackChans bool // parkingOnChan indicates that the goroutine is about to // park on a chansend or chanrecv. Used to signal an unsafe point // for stack shrinking. parkingOnChan atomic.Bool // inMarkAssist indicates whether the goroutine is in mark assist. // Used by the execution tracer. inMarkAssist bool coroexit bool // argument to coroswitch_m raceignore int8 // ignore race detection events nocgocallback bool // whether disable callback from C tracking bool // whether we're tracking this G for sched latency statistics trackingSeq uint8 // used to decide whether to track this G trackingStamp int64 // timestamp of when the G last started being tracked runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking lockedm muintptr sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr parentGoid uint64 // goid of g","date":"2024-05-15","objectID":"/gmp-of-go/:2:1","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"Goroutine 切换成本 gobuf 描述了一个 Goroutine 所有现场,从一个 g 切换到另一个 g,只要把这几个现场字段保存下来,再将 g 入队,M 就可以执行其他 g 了,无需进入内核态。 gobuf 数据结构如下 type gobuf struct { // The offsets of sp, pc, and g are known to (hard-coded in) libmach. // // ctxt is unusual with respect to GC: it may be a // heap-allocated funcval, so GC needs to track it, but it // needs to be set and cleared from assembly, where it's // difficult to have write barriers. However, ctxt is really a // saved, live register, and we only ever exchange it between // the real register and the gobuf. Hence, we treat it as a // root during stack scanning, which means assembly that saves // and restores it doesn't need write barriers. It's still // typed as a pointer so that any other writes from Go get // write barriers. sp uintptr pc uintptr g guintptr ctxt unsafe.Pointer ret uintptr lr uintptr bp uintptr // for framepointer-enabled architectures } ","date":"2024-05-15","objectID":"/gmp-of-go/:2:2","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"runtime 可拦截 goroutine 阻塞场景解析 Goroutine 属于协程的一种,因此存在运行态、阻塞态等各种状态。 那么 goroutine 什么情况下会发生阻塞? 当 goroutine 发生阻塞时,GMP 模型如何应对? 显然,当 goroutine 发生可被 runtime 拦截的阻塞时,GMP 模型并不会阻塞调度循环, 而是把 goroutine 挂起,即让 g 先进某个数据结构,待 ready 后在继续执行,并不会占用线程, 同时线程会进入 schedule,继续消费队列,执行其他的 g. 场景 I: 延迟 package main import ( \"fmt\" \"time\" ) func main() { fmt.Println(\"Before: \", time.Now()) time.Sleep(30 * time.Minute) fmt.Println(\"After: \", time.Now()) } 函数调用链如下: time.Sleep -\u003e runtime.timeSleep { ... gp := getg() t := gp.timer ... t.arg = gp ... } -\u003e gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceBlockSleep, 1) 显然,在 runtime.timeSleep 函数中,获取到的当前 g 被挂在 runtime.timer.arg 上,然后被挂起。 场景 II: Channel send / recv (chan / select) package main import ( \"fmt\" \"sync\" \"time\" ) func main() { var ch = make(chan int) var wg = sync.WaitGroup{} wg.Add(2) go func(ch chan\u003c- int) { defer close(ch) defer wg.Done() time.Sleep(time.Second) ch \u003c- 1 }(ch) go func(ch \u003c-chan int) { defer wg.Done() val := \u003c-ch fmt.Println(val) }(ch) wg.Wait() } 函数 ch\u003c- 调用链如下: ch\u003c- -\u003e runtime.chansend1 -\u003e runtime.chansend { ... gp := getg() mysg := acquireSudog() ... gp.waiting = mysg gp.param = nil c.sendq.enqueue(mysg) // Signal to anyone trying to shrink our stack that we're about // to park on a channel. The window between when this G's status // changes and when we set gp.activeStackChans is not safe for // stack shrinking. gp.parkingOnChan.Store(true) gopark(chanparkcommit, unsafe.Pointer(\u0026c.lock), waitReasonChanSend, traceBlockChanSend, 2) ... } -\u003e gopark 函数 ch\u003c- 调用链如下: \u003c-ch -\u003e runtime.chanrecv1(c *hchan, elem unsafe.Pointer) -\u003e runtime.chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { ... // no sender available: block on this channel. gp := getg() mysg := acquireSudog() ... gp.waiting = mysg mysg.g = gp ... c.recvq.enqueue(mysg) // Signal to anyone trying to shrink our stack that we're about // to park on a channel. The window between when this G's status // changes and when we set gp.activeStackChans is not safe for // stack shrinking. gp.parkingOnChan.Store(true) gopark(chanparkcommit, unsafe.Pointer(\u0026c.lock), waitReasonChanReceive, traceBlockChanRecv, 2) } -\u003e runtime.gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceReason traceBlockReason, traceskip int) 根据调用链可知,g 被封装进 sudog 中,然后挂在了 hchan.sendq 链表上。 相关数据结构 sudog, sendq 如下: sudog 的数据结构: // sudog (pseudo-g) represents a g in a wait list, such as for sending/receiving // on a channel. // // sudog is necessary because the g ↔ synchronization object relation // is many-to-many. A g can be on many wait lists, so there may be // many sudogs for one g; and many gs may be waiting on the same // synchronization object, so there may be many sudogs for one object. // // sudogs are allocated from a special pool. Use acquireSudog and // releaseSudog to allocate and free them. type sudog struct { // The following fields are protected by the hchan.lock of the // channel this sudog is blocking on. shrinkstack depends on // this for sudogs involved in channel ops. g *g next *sudog prev *sudog elem unsafe.Pointer // data element (may point to stack) // The following fields are never accessed concurrently. // For channels, waitlink is only accessed by g. // For semaphores, all fields (including the ones above) // are only accessed when holding a semaRoot lock. acquiretime int64 releasetime int64 ticket uint32 // isSelect indicates g is participating in a select, so // g.selectDone must be CAS'd to win the wake-up race. isSelect bool // success indicates whether communication over channel c // succeeded. It is true if the goroutine was awoken because a // value was delivered over channel c, and false if awoken // because c was closed. success bool // waiters is a count of semaRoot waiting list other than head of list, // clamped to a uint16 to fit in unused space. // Only meaningful at the head of the list. // (If we wanted to be overly clever, we could store a high 16 bits // in the se","date":"2024-05-15","objectID":"/gmp-of-go/:2:3","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"runtime 不可拦截 goroutine 阻塞场景解析 time.Sleep / channel send / channel recv / select / net read / net write / sync.Mutex 等阻塞场景可被 runtime 拦截,然而仍存在一些阻塞情况是 runtime 无法拦截的,例如:在执行 C 代码或阻塞在 syscall 上时,必须占用一个线程。 ","date":"2024-05-15","objectID":"/gmp-of-go/:2:4","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"III. Sysmon system monitor,高优先级,在专有线程中执行,不需要绑定 p. ","date":"2024-05-15","objectID":"/gmp-of-go/:3:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"IV. Summary Runtime 构成:Scheduler、Netpoll、内存管理、垃圾回收 GMP:M - 任务消费者;G - 计算任务;P - 可以使用 CPU 的 token GMP 中的队列抽象:P 的本地 runnext 字段 –» P 的 local run queue –» global run queue;采用多级队列减少锁竞争 调度循环:线程 M 在持有 P 的情况下不断消费运行队列中的 G 的过程 处理阻塞: runtime 可以接管的阻塞: channel send / recv,sync.Mutex,net read / write,select,time.Sleep 所有 runtime 可接管的阻塞都是通过 gopark / goparkunlock 挂起,goready 恢复 runtime 不可接管的阻塞:syscall,cgo,长时间运行需要剥离 P 执行; sysmon: 一个后台高级优先级循环,执行时不需要绑定任何的 P 负责: 检查是否已经没有活动线程,如果是则崩溃 轮询 netpoll 剥离在 syscall 上阻塞的 M 的 P 发信号,抢占已经执行时间过长的 G ","date":"2024-05-15","objectID":"/gmp-of-go/:4:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"V. Q \u0026 A 为什么阻塞等待的 goroutine,有时表现为 g 有时表现为 sudog ? sudog (pseudo-g) 表示等待列表中的 g,例如用于在 channel 上的 send/recv. g 与同步对象是多对多的关系: 一个 g 可以出现在多个等待列表中,因此一个 g 可能有多个 sudog; 很多 g 可能在等待同一个同步对象,因此一个对象可能有很多 sudog 一个 g 可能对应多个 sudog,比如一个 g 会同时 select 多个 channel ","date":"2024-05-15","objectID":"/gmp-of-go/:5:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Golang","GMP","Goroutine","Machine","Process"],"content":"VI. Reference Golang的协程调度器原理及GMP设计思想 Golang 生产-消费调度流程: Producer Golang 生产-消费调度流程: Consumer 极端情况下收缩 Go 的线程数 Go Scheduler 变更史 internal/poll/fd_poll_runtime.go internal/poll/fd_unix.go net/fd_unix.go runtime/runtime2.go runtime/time.go runtime/proc.go runtime/netpoll.go runtime/netpoll_epoll.go runtime/sema.go sync/mutex.go time/sleep.go ","date":"2024-05-15","objectID":"/gmp-of-go/:6:0","tags":["Golang","GMP","Goroutine","Machine","Process"],"title":"深入理解 GMP","uri":"/gmp-of-go/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"深入剖析 Golang 网络编程之 Netpoll,主要涉及 Linux 环境下的 Epoll 初始化、 Go 网络编程基本流程(Listen、Accept、Read、Write)以及netpoll 执行流程 本文所涉及的源码版本:v1.22.3 ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:0:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"I. 基础概念 网络编程,是允许不同计算机上的程序通过网络通信的开发过程,涉及多种协议(HTTP、TCP/IP等)以及不同编程语言的应用。 ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:1:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"同步、异步、并发模型 IO 模型 读写操作和阻塞阶段 阻塞 IO 程序阻塞于读写函数 IO 复用 程序阻塞于 IO 复用系统调用,但可同时监听多个 IO 事件;对 IO 本身的读写操作是非阻塞的 SIGIO 信号 信号触发读写就绪事件,用户程序执行读写操作;程序本身没有阻塞阶段 异步 IO 内核执行读写操作并触发读写完成事件;程序没有阻塞阶段 主要用于区分内核向应用程序通知的是何种 IO 事件(就绪事件 or 完成事件),以及由谁来完成 IO 读写(应用程序 or 内核) IO模型中的同步 同步 IO 模型,指的是应用程序发起 IO 操作后,必须等待 IO 操作完成后才能继续执行后续的操作,即 IO 操作的结果需要立即返回给应用程序;在此期间,应用程序处于阻塞状态,无法做其他操作。 优点:编程模型简单 缺点:效率较低(应用程序的执行速度被 IO 操作所限制) 对于操作系统内核来说,同步 IO 操作是指在内核处理 IO 请求时需要等待 IO 模型中的异步 异步 IO 模型,指的是应用程序发起 IO 操作后,无须等待 IO 操作完成,可以立即进行后续的操作;在此期间,操作系统负责把 IO 操作的结果返回给应用程序; 优点:可以充分利用系统资源,提高 IO 操作的效率 缺点:编程模型相对复杂 对于操作系统内核来说,异步 IO 操作指的是,在内核处理 IO 请求时无需等待,立即返回 并发模式 并发模式,指的是 I/O 处理单元和多个逻辑单元之间协调完成任务的方法 ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:1:1","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"Linux Epoll epoll 在内核里使用红黑树(Red-black tree)来跟踪进程所有待检测的文件描述字 fd,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里(红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)) epoll 使用事件驱动的机制,在内核里维护了一个链表(List)来记录就绪事件。 当某个 socket 有事件发生时,内核通过回调函数将其加入到这个就绪事件列表中。 当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率 两种触发模式 Level trigger:服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束 Edge trigger:服务器端只会从 epoll_wait 中苏醒一次 事件宏 EPOLLIN 表示对应的文件描述符可读(包括对端 socket 正常关闭) EPOLLOUT 表示对应的文件描述符可写 EPOLLPRI 表示对应的文件描述符有紧急的数据可读(带外数据) EPOLLERR 表示对应的文件描述符发生错误 EPOLLHUP 表示对应的文件描述符被挂断 EPOLLET 将 EPOLL 设为边缘触发模式(默认电平触发) EPOLLONESHOT 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个 socket 的话,需要再次把这个 socket 加入到内核中的事件注册表中 ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:1:2","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"II. 应用示例 package main import \"net\" func main() { l, _ := net.Listen(\"tcp\", \"127.0.0.1:2333\") for { conn, _ := l.Accept() go func() { defer conn.Close() buf := make([]byte, 4096) _, _ = conn.Read(buf) conn.Write(buf) }() } } ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:2:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"III. 相关数据结构 // src/net/fd_fake.go // Network file descriptor. type netFD struct { pfd poll.FD // immutable until Close family int sotype int isConnected bool // handshake completed or use of association with peer net string laddr Addr raddr Addr // The only networking available in WASI preview 1 is the ability to // sock_accept on a pre-opened socket, and then fd_read, fd_write, // fd_close, and sock_shutdown on the resulting connection. We // intercept applicable netFD calls on this instance, and then pass // the remainder of the netFD calls to fakeNetFD. *fakeNetFD } // poll.FD`: `src/internal/poll/fd_unix.go // FD is a file descriptor. The net and os packages use this type as a // field of a larger type representing a network connection or OS file. type FD struct { // Lock sysfd and serialize access to Read and Write methods. fdmu fdMutex // System file descriptor. Immutable until Close. Sysfd int // Platform dependent state of the file descriptor. SysFile // I/O poller. pd pollDesc // Semaphore signaled when file is closed. csema uint32 // Non-zero if this file has been set to blocking mode. isBlocking uint32 // Whether this is a streaming descriptor, as opposed to a // packet-based descriptor like a UDP socket. Immutable. IsStream bool // Whether a zero byte read indicates EOF. This is false for a // message based socket connection. ZeroReadIsEOF bool // Whether this is a file rather than a network socket. isFile bool } // Addr represents a network end point address. // // The two methods [Addr.Network] and [Addr.String] conventionally return strings // that can be passed as the arguments to [Dial], but the exact form // and meaning of the strings is up to the implementation. type Addr interface { Network() string // name of the network (for example, \"tcp\", \"udp\") String() string // string form of address (for example, \"192.0.2.1:25\", \"[2001:db8::1]:80\") } // fdMutex is a specialized synchronization primitive that manages // lifetime of an fd and serializes access to Read, Write and Close // methods on FD. type fdMutex struct { state uint64 rsema uint32 wsema uint32 } type SysFile struct { // Writev cache. iovecs *[]syscall.Iovec } type pollDesc struct { runtimeCtx uintptr } 通过源码可以看到,Golang 网络编程涉及到的 netFD, poll.FD, Addr, SysFile 以及 pollDesc 之间的关系如下: fdmu 是为了保证对同一个文件的读、写操作能分别被序列化 Sysfd 就是操作系统中 syscall 返回的 fd 值 pd,pollDesc I/O poller,是 Go 对 poll 过程的一个抽象,所有平台的抽象都是一样的 csema,当文件被关闭时会被触发 isBlocking 表明 FD 是否为 blocking 模式 IsStream 标志该 FD 是否是流式,与流式相反的是基于 packet 的,即 UDP socket ZeroReadIsEOF,当连接读到 0 长度时,用来区分是否代表 EOF. 如果是基于 packet 的 socket 连接,则始终是 false isFile 标志该 FD 是否代表文件,还是网络连接 netFD 结构中包含一个 poll.FD 类型的成员 pfd,以及 Addr 接口类型的 laddr 和 raddr poll.FD 结构含有 SysFile 和 pollDesc 类型的成员,以及 fdMutex 类型的 fdmu ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:3:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"IV. TCP 网络编程基本流程 本部分涉及众多函数调用,为了描述清晰,采用了图的形式,其中,每一块第一行表示该块所表示的函数名称,其他部分表示该函数中关键函数调用。 ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:4:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"创建 TCP socket 并监听: net.Listen NOTE 需要注意的是,在执行 net.(*netFD).listenStream 之前,由于 maxListenerBacklog 函数调用了 open(\"/proc/sys/net/core/somaxconn\"),则会导致 epoll 底层红黑树的提前创建: runtime.netpollinit -\u003e syscall.EpollCreate1 -\u003e Syscall6(SYS_EPOLL_CREATE1, uintptr(flags), 0, 0, 0, 0, 0). 另外,当启用 Timer 时,也存在提前初始化 netpoll 的可能,原因: Timers rely on the network poller time.NewTimer -\u003e runtime.startTimer -\u003e runtime.addtimer -\u003e runtime.doaddtimer -\u003e netpollGenericInit() // doaddtimer adds t to the current P's heap. // The caller must have locked the timers for pp. func doaddtimer(pp *p, t *timer) { // Timers rely on the network poller, so make sure the poller // has started. if netpollInited.Load() == 0 { netpollGenericInit() } ... } ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:4:1","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"获取 TCP 连接: net.(*TCPListener).Accept ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:4:2","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"TCP 连接读数据: net.(*TCPConn).Read ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:4:3","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"TCP 连接写数据: net.(*TCPConn).Write ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:4:4","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"V. netpoll 执行流程: netpoll 在调度和 GC 的关键点上都会检查一次 netpoll,确定是否存在 ready 状态的 FD: startTheWorldWithSema // reason is the same STW reason passed to stopTheWorld. start is the start // time returned by stopTheWorld. // // now is the current time; prefer to pass 0 to capture a fresh timestamp. // // stattTheWorldWithSema returns now. func startTheWorldWithSema(now int64, w worldStop) int64 { assertWorldStopped() mp := acquirem() // disable preemption because it can be holding p in a local var if netpollinited() { list, delta := netpoll(0) // non-blocking injectglist(\u0026list) netpollAdjustWaiters(delta) } lock(\u0026sched.lock) procs := gomaxprocs if newprocs != 0 { procs = newprocs newprocs = 0 } p1 := procresize(procs) sched.gcwaiting.Store(false) if sched.sysmonwait.Load() { sched.sysmonwait.Store(false) notewakeup(\u0026sched.sysmonnote) } unlock(\u0026sched.lock) worldStarted() ... } findrunnable // Finds a runnable goroutine to execute. // Tries to steal from other P's, get g from local or global queue, poll network. // tryWakeP indicates that the returned goroutine is not normal (GC worker, trace // reader) so the caller should try to wake a P. func findRunnable() (gp *g, inheritTime, tryWakeP bool) { ... // Poll network until next timer. if netpollinited() \u0026\u0026 (netpollAnyWaiters() || pollUntil != 0) \u0026\u0026 sched.lastpoll.Swap(0) != 0 { sched.pollUntil.Store(pollUntil) if mp.p != 0 { throw(\"findrunnable: netpoll with p\") } if mp.spinning { throw(\"findrunnable: netpoll with spinning\") } delay := int64(-1) if pollUntil != 0 { if now == 0 { now = nanotime() } delay = pollUntil - now if delay \u003c 0 { delay = 0 } } if faketime != 0 { // When using fake time, just poll. delay = 0 } list, delta := netpoll(delay) // block until new work is available ... } ... } pollWork // pollWork reports whether there is non-background work this P could // be doing. This is a fairly lightweight check to be used for // background work loops, like idle GC. It checks a subset of the // conditions checked by the actual scheduler. func pollWork() bool { if sched.runqsize != 0 { return true } p := getg().m.p.ptr() if !runqempty(p) { return true } if netpollinited() \u0026\u0026 netpollAnyWaiters() \u0026\u0026 sched.lastpoll.Load() != 0 { if list, delta := netpoll(0); !list.empty() { injectglist(\u0026list) netpollAdjustWaiters(delta) return true } } return false } sysmon // Always runs without a P, so write barriers are not allowed. // //go:nowritebarrierrec func sysmon() { ... lock(\u0026sched.sysmonlock) // Update now in case we blocked on sysmonnote or spent a long time // blocked on schedlock or sysmonlock above. now = nanotime() // trigger libc interceptors if needed if *cgo_yield != nil { asmcgocall(*cgo_yield, nil) } // poll network if not polled for more than 10ms lastpoll := sched.lastpoll.Load() if netpollinited() \u0026\u0026 lastpoll != 0 \u0026\u0026 lastpoll+10*1000*1000 \u003c now { sched.lastpoll.CompareAndSwap(lastpoll, now) list, delta := netpoll(0) // non-blocking - returns list of goroutines if !list.empty() { // Need to decrement number of idle locked M's // (pretending that one more is running) before injectglist. // Otherwise it can lead to the following situation: // injectglist grabs all P's but before it starts M's to run the P's, // another M returns from syscall, finishes running its G, // observes that there is no work to do and no other running M's // and reports deadlock. incidlelocked(-1) injectglist(\u0026list) incidlelocked(1) netpollAdjustWaiters(delta) } } ... } 根据 ready 的事件时 Read 或 Write,分别从 poolDesc 的 rg、wg 上获取该唤醒的 goroutine. 然后将已经 ready 的 goroutine push 到 toRun 链表,并且 toRun 链表最终会从 netpoll() 返回,通过 injectglist 进入全局队列. 相当于每次调度循环都要执行 netpoll,检查频率还是比较高的 // netpoll checks for ready network connections. // Returns list of goroutines that become runnable. // delay \u003c 0: blocks indefinitely // delay == 0: does not block, just polls // delay \u003e 0: block for up to that many nanoseconds func netpoll(delay int64) (gList, int32) { if epfd == -1 { return gList{}, 0 } var waitms int32 if delay \u003c 0 { waitms = -1 } else if ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:5:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"VI. 总结 Golang 通过对 Linux 内核提供的 epoll 实现进行封装,实现了同步编程异步执行的效果,其核心数据结构是 netFD,并将 Sysfd 与 pollDesc 结构绑定。 当某个 netFD 产生 EAGAIN 错误时,则当前 Goroutine 将会被存储到其对应的 pollDesc 中,同时 Goroutine 会 gopark(),直至这个 netFD 再次发生读写事件,会将此 Goroutine 设置为 ready 并放入 toRun 队列等待重新运行,而底层事件通知机制就是 epoll. Golang 中 netpoll 的创建与初始化的可能来源:Timer、读文件、TCP Listen. 如下的调度和 GC 关键函数 startTheWorldWithSema、findrunnable、pollWork、sysmon 都会进行 netpoll 执行流程,检查是否存在 ready 状态的 FD. ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:6:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Netpoll","Epoll","Network Program","Golang","Linux"],"content":"VII. Reference runtime/netpoll.go runtime/netpoll_epoll.go runtime/proc.go net/fd_unix.go internal/poll/fd_poll_runtime.go internal/poll/fd_unix.go ","date":"2024-05-08","objectID":"/netpoll-of-network-program-for-golang/:7:0","tags":["Netpoll","Epoll","Network Program","Golang","Linux"],"title":"Netpoll of Network Program for Golang","uri":"/netpoll-of-network-program-for-golang/"},{"categories":["Golang","Profile","Optimize"],"content":"Golang 常见的性能剖析 (Profile) 与优化 (Optimize) ","date":"2024-04-20","objectID":"/go-profile/:0:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"I. Profile 在进行 API 压测、全链路压测、线上生产环境被高峰流量打爆的过程中随时可能发生故障等问题,例如: CPU 占用过高,超过 90%; 内存爆掉,OOM(Out of memory); Goroutine 数量过多,80W; 线程数超高; 延迟过高; 在发生以上故障时,一般需要结合 pprof 寻找故障原因,并根据不同的情况选择不同的方案; 线上一定要具有开启 pprof 的能力,如果考虑安全性,也要具有通过配置开启的能力; ","date":"2024-04-20","objectID":"/go-profile/:1:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"压测时需要关注的服务指标 Request rate: The number of service requests per second. Errors: The number of request that failed. Duration: The time for requests to complete. Goroutine / Thread 数量: 如果 Goroutine 数量很多,需要关注这些 Goroutine 的执行情况. GC 频率 gctrace 的内容: GC 的 STW 时间 还有一些其他 Memstats 相关的其他指标,可以参考 Prometheus. ","date":"2024-04-20","objectID":"/go-profile/:1:1","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"压测手段 wrk: a HTTP benchmarking tool wrk2: a HTTP benchmarking tool based mostly on wrk HEY: a tiny program that sends some load to a web application. Vegate: a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate. h2load: HTTP/2 benchmarking tool ghz: gRPC benchmarking and load testing tool ","date":"2024-04-20","objectID":"/go-profile/:1:2","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"pprof 应用实例 package main import ( \"net/http\" _ \"net/http/pprof\" ) var quit chan struct{} = make(chan struct{}) func f() { \u003c- quit } func main() { go func() { http.ListenAndServe(\":8080\", nil) }() for i := 0; i \u003c 10000; i++ { go f() } for {} // Test } go tool pprof -http=:9999 localhost:8080/debug/pprof/heap 注意事项 测试代码中引入 net/http/pprof 包: _ \"net/http/pprof\" 单独启动一个 Goroutine 开启监听(端口自定,例如这里是 8080):go func() { http.ListenAndServe(\":8080\", nil) }() $ go tool pprof -http=:9999 localhost:8080/debug/pprof/heap ","date":"2024-04-20","objectID":"/go-profile/:1:3","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"II. Optimize ","date":"2024-04-20","objectID":"/go-profile/:2:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"优化方向 在分析上图的应用程序运行过程,可以发现进行程序优化时,一般从可以从以下方面入手: 应用层优化: 主要指的是逻辑优化、内存使用优化、CPU 使用优化、阻塞优化等,并且本层优化效果可能优于底层优化; 底层优化:GC优化、Go 标准库优化、Go runtime 优化等 ","date":"2024-04-20","objectID":"/go-profile/:2:1","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"基本优化流程 外部依赖:在监控系统中查看是否存在问题,例如依赖的上游服务 (DB/redis/MQ) 延迟过高; CPU 占用:通过查看 CPU profile 检查是否存在问题,优化占用 CPU 较多的部分逻辑; 内存占用:看 Prometheus,内存 RSS / Goroutine 数量 / Goroutine 栈占用 –» 如果 Goroutine 数量不多,则重点关注 heap profile 中的 inuse –» 定时任务类需要看 alloc Goroutine 数量过多 –» 从 profile 网页进去看看 Goroutine 的执行情况(在干什么?) –» 检查死锁、阻塞等问题 –» 个别不在意延迟的选择第三方库优化 ","date":"2024-04-20","objectID":"/go-profile/:2:2","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"常见优化场景 字符串拼接 package main import ( \"fmt\" \"testing\" ) func BenchmarkConcat0(b *testing.B) { var str string for i := 0; i \u003c b.N; i++ { str = \"\" str += \"userid : \" + \"1\" str += \"localtion : \" + \"ab\" } } func BenchmarkConcat1(b *testing.B) { var str string for i := 0; i \u003c b.N; i++ { str = \"\" str += fmt.Sprintf(\"userid : %v\", \"1\") str += fmt.Sprintf(\"localtion : %v\", \"ab\") } } $ go test -bench=. -benchmem goos: linux goarch: amd64 pkg: github.com/lutianen/go-test/bench0 cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz BenchmarkConcat0-16 35702518 32.86 ns/op 24 B/op 1 allocs/op BenchmarkConcat1-16 8105732 140.9 ns/op 56 B/op 3 allocs/op PASS ok github.com/lutianen/go-test/bench0 2.506s 逃逸分析 用户声明的对象,被放在栈上还是堆上? 可以通过编译器的 escape analysis 来决定 go build -gcflags=\"-m\" xxx.go package main func main() { var sl = make([]int, 1024) println(sl[0]) var sl0 = make([]int, 10240) println(sl0[0]) } $ go build -gcflags=\"-m\" main.go # command-line-arguments ./main.go:3:6: can inline main ./main.go:4:15: make([]int, 1024) does not escape ./main.go:7:16: make([]int, 10240) escapes to heap TODO: 各种逃逸分析的可能性有哪些? Trasval 2-D Matrix package bench1 import \"testing\" func BenchmarkHorizontal(b *testing.B) { arrLen := 10000 arr := make([][]int, arrLen, arrLen) for i := 0; i \u003c arrLen; i++ { arrInternal := make([]int, arrLen) for j := 0; j \u003c arrLen; j++ { arrInternal[j] = 0 } arr[i] = arrInternal } for i := 0; i \u003c b.N; i++ { for x := 0; x \u003c len(arr); x++ { for y := 0; y \u003c len(arr); y++ { arr[x][y] = 1 } } } } func BenchmarkVertical(b *testing.B) { arrLen := 10000 arr := make([][]int, arrLen, arrLen) for i := 0; i \u003c arrLen; i++ { arrInternal := make([]int, arrLen) for j := 0; j \u003c arrLen; j++ { arrInternal[j] = 0 } arr[i] = arrInternal } for i := 0; i \u003c b.N; i++ { for x := 0; x \u003c len(arr); x++ { for y := 0; y \u003c len(arr); y++ { arr[y][x] = 1 } } } } $ go test -bench=. -benchmem goos: linux goarch: amd64 pkg: github.com/lutianen/go-test/bench1 cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz BenchmarkHorizontal-16 15 71020410 ns/op 54629717 B/op 666 allocs/op BenchmarkVertical-16 1 1059649022 ns/op 819445856 B/op 10002 allocs/op PASS ok github.com/lutianen/go-test/bench1 3.676s Zero Garbage / Allocation Zero Grabage 一般指的是通过利用 sync.Pool 将堆分配完全消灭的优化技术。 例如,在 http router 框架 fasthttp 中应用较多. False Sharing CPU 运行过程中修改数据是一个 cache line为单位,当两个变量A/B满足以下条件: 在内存中相邻 并发修改频繁 那么,当 CPU0 修改变量 A 时,会导致 CPU1 中的变量 B 缓存失效。 解决方法,在定义数据结构中,填充一些 padding 用以满足该数据结构正好是 cache line 的整数倍; type NoPad struct { x uint64 y uint64 } type WithPad struct { x uint64 _ [6]uint64 y uint64 } 查看 cache line 大小:cat /sys/devices/system/cpu/cpu\u003ccore-num\u003e/cache/index0/coherency_line_size 降低外部命令调用频次 优化前: func f(wr http.ResponseWriter, r *http.Request) { uuid, _ := exec.Command(\"uuidgen\").Output() // Use exec.Command wr.Header()[\"Content-Type\"] = []string{\"application/text\"} io.WriteString(wr, string(uuid)) } 优化后: import uuid \"github.com/satori/go.uuid\" func f(wr http.ResponseWriter, r *http.Request) { uuid, _ := uuid.NewV4() // Replace exec.Command with existing library wr.Header()[\"Content-Type\"] = []string{\"application/text\"} io.WriteString(wr, uuid.String()) } 总结: 线上使用 exec 命令是非常危险的 采用第三方库代替外部命令 阻塞导致高延迟 锁阻塞 var mtx sync.Mutex var data = map[string]string{ \"hint\": \"hello wold\", } func f(wr http.ResponseWriter, r *http.Request) { mtx.Lock() defer mtx.Unlock() buf := data[\"hint\"] time.Sleep(time.Millisecond * 10) // 临界区内的慢操作 wr.Header()[\"Content-Type\"] = []string{\"application/json\"} io.WriteString(wr, buf) } 减小临界区 - 优化后: var mtx sync.Mutex var data = map[string]string{ \"hint\": \"hello wold\", } func f(wr http.ResponseWriter, r *http.Request) { mtx.Lock() buf := data[\"hint\"] mtx.Unlock() time.Sleep(time.Millisecond * 10) // 慢操作放置于临界区之外 wr.Header()[\"Content-Type\"] = []string{\"application/json\"} io.WriteString(wr, buf) } 在后端系统开发中,锁瓶颈是较常见的问题,例如文件锁 双 Buffer 完全干掉锁阻塞 使用双 Buffer / RCU 完全消除读阻塞:全量更新,直接替换原 config func updateConfig() { var newConfig = \u0026MyConfig { WhiteList: make(map[int]struct{}), } // Do a lot of compulation ","date":"2024-04-20","objectID":"/go-profile/:2:3","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"III. Coutinuous Profiling 压测是一个蹲点行为,然而真实场景并不美好,它们通常是难以发现的偶发问题: 该到吃饭的时候,CPU 使用尖刺 凌晨四点半,系统发生 OOM 刚睡着的时候,Goroutine 数量爆炸 产品被部署到客户那里,想登陆客户的环境并不方便 此时 Coutinuout Profiling 就派上用场了. 自省式的 Profile Dumper,可以根据 CPU 利用率、Memory 利用率、Goroutine 数量等多个指标检测系统,设置定时周期进行检测,当发现某个指标异常时,自动 Dump file. ","date":"2024-04-20","objectID":"/go-profile/:3:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"IV. Summary _pad 优化,针对多个线程更新同一个结构体内不同的字段场景有效,而针对一个线程同时更新整个结构体的场景意义不大; 第三方接口出现问题,如何保护自己的服务? 对外部调用必须有超时 ==\u003e 熔断 goroutine 初始化栈空间为 2KB,最大 1GB,那么 heap 为什么不爆栈? 在 Go 语言中,goroutine 和 heap 使用单独的内存空间:Goroutine 有自己的堆栈空间,用于存储局部变量、函数帧和其他运行时信息;heap 则是一个共享内存空间,用于存储动态分配的对象,例如 slice、map 和 strings。 当 Goroutine 需要分配的内存多于起堆栈上的可用内存时,它将自动从 stack 中分配内存,采用的是 stack 分配机制完成,运行 goroutine 分配任何数量的内存,而不用担心 stack 空间耗尽; 除了堆分配之外,goroutine 还可以使用一种称为堆栈复制的技术来在它们之间共享数据,堆栈复制比堆分配更有效,但它只能用于共享足够小以适合堆栈的数据。 ","date":"2024-04-20","objectID":"/go-profile/:4:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Profile","Optimize"],"content":"V. Reference Benchmarks Game Go Web Frame Benchmarks Go HTTP Router Benchmark Web 场景跨语言性能对比 《Systems Performance》 Dave 分享的 High Performance Go Workshop go-perfbook: best practices for writing high-performance Go code Delve What is Continuous Profiling? Google-Wide Profiling: A Continuous Profiling Infrastructure for Data Centers ","date":"2024-04-20","objectID":"/go-profile/:5:0","tags":["Golang","Profile","Optimize"],"title":"Profile and Optimize for Go","uri":"/go-profile/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"在 Golang 生态系统中,Linter 工具是开发者提升代码质量的关键。 本文将深入介绍几款常用的 Linter 工具及其最佳实践,帮助您在开发中避免常见错误并提高代码的可维护性。 ","date":"2024-04-19","objectID":"/linter-go/:0:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"目录 说明 何为 Linter? Gocyclo bodyclose sqlrows funlen goconst ineffassign lll errcheck whitespace GolangCI-Lint reviewdog Summary Reference ","date":"2024-04-19","objectID":"/linter-go/:1:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"O. 说明 如特殊说明,文中代码已在在 Mac 和 Linux 系统下进行测试 ","date":"2024-04-19","objectID":"/linter-go/:2:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"I. 何为 Linter? Linter 是一种静态代码分析工具,用于在编译前检查代码中的错误、风格问题及潜在的 Bug。 在 Golang 生态中,Linter 工具帮助开发者在早期阶段就发现问题,从而避免后期修复的高成本。 ","date":"2024-04-19","objectID":"/linter-go/:3:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"II. Gocyclo Gocyclo 是一款用于分析 Go 代码中函数圈复杂度的 Linter 工具,帮助开发者识别需要重构的复杂函数。 通过降低圈复杂度,代码变得更加简洁、易读且更易维护。 ","date":"2024-04-19","objectID":"/linter-go/:4:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"函数圈复杂度(cyclomatic complexities) 圈复杂度,是一种衡量代码复杂性的指标,通过计算代码中的决策点(如if语句、循环等)来评估函数的复杂度,具体计算方法如下: 一个函数的基本圈复杂度为 1 当函数中存在的每一个 if, for, case, \u0026\u0026 or ||,都会使得该函数的圈复杂度加 1 在 Go 语言中,由于 if err != nil 的特殊情况存在,因此,其圈复杂度阈值默认为 15,而其他编程语言中圈复杂度阈值一般默认为 10。 在 Go 语言中,switch 中的 default 并不会增加函数的圈复杂度; Gocyclo 可以作为单独的命令行工具使用,也可以与其他 Linter 工具(如 golangci-lint)集成使用,提供更全面的代码质量检查。 同时,它也可以集成到 CI/CD 流程中,帮助团队持续改善代码质量。 ","date":"2024-04-19","objectID":"/linter-go/:4:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 go install github.com/fzipp/gocyclo/cmd/gocyclo@latest ","date":"2024-04-19","objectID":"/linter-go/:4:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 Gocyclo linter ? Calculate cyclomatic complexities of Go functions. Usage: gocyclo [flags] \u003cGo file or directory\u003e ... Flags: -over N show functions with complexity \u003e N only and return exit code 1 if the set is non-empty -top N show the top N most complex functions only -avg, -avg-short show the average complexity over all functions; the short option prints the value without a label -ignore REGEX exclude files matching the given regular expression The output fields for each line are: \u003ccomplexity\u003e \u003cpackage\u003e \u003cfunction\u003e \u003cfile:line:column\u003e 使用示例 // gocyclo-test/main.go package main import ( \"fmt\" \"strconv\" ) func main() { var a = 10 if a == 10 { f() } else { fmt.Printf(\"%s\", strconv.Itoa(a)) } switch a{ case 10: fmt.Println(a) default: fmt.Println(\"default\") } } func f() { a := 10 b := 12 if a != b { // do something fmt.Println(\"a != b\") } } $ gocyclo gocyclo-test/main.go 3 main main gocyclo-test/main.go:8:1 2 main f gocyclo-test/main.go:24:1 ","date":"2024-04-19","objectID":"/linter-go/:4:3","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"III. bodyclose 在 Go 中,即使读取了所有的响应内容,也需要显式关闭响应体以释放资源,否则可能导致资源泄漏、连接池耗尽,进而影响应用性能。 bodyclose 主要关注于 HTTP 响应体的正确关闭,通过检查 resp.Body 是否被正确关闭。 它既可以单独使用,也可以集成到其他 linter 工具(例如 golangci-lint)中。 ","date":"2024-04-19","objectID":"/linter-go/:5:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 go install github.com/timakin/bodyclose@latest ","date":"2024-04-19","objectID":"/linter-go/:5:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 bodyclose ? $ bodyclose bodyclose is a tool for static analysis of Go programs. Usage of bodyclose: bodyclose unit.cfg # execute analysis specified by config file bodyclose help # general help, including listing analyzers and flags bodyclose help name # help on specific analyzer and its flags 使用示例 这里展示借助 golangci-lint 的方式使用 bodyclose. // main.go package kyden import ( \"fmt\" \"io\" \"net/http\" ) func f() error{ resp, err := http.Get(\"http://example.com/\") if err != nil { return err } // defer resp.Body.Close() // \u003c\u003c\u003c body, err := io.ReadAll(resp.Body) fmt.Println(body) return nil } $ golangci-lint run --disable-all -E bodyclose main.go main.go:11:26: response body must be closed (bodyclose) resp, err := http.Get(\"http://example.com/\") 避免使用 http 库中 body 忘记 close 的更优方案是: 对 Go 官方提供的 http 进行封装,使调用方(Caller)不用显示调用 close 函数. package httpclient import ( \"io/ioutil\" \"net/http\" ) // Client 是一个自定义的 HTTP 客户端结构体 type Client struct { http.Client } // Get 封装了 http.Get 方法 func (c *Client) Get(url string) (string, error) { resp, err := c.Client.Get(url) if err != nil { return \"\", err } // 确保在函数返回时关闭响应体 defer resp.Body.Close() // 读取响应内容 body, err := ioutil.ReadAll(resp.Body) if err != nil { return \"\", err } return string(body), nil } ","date":"2024-04-19","objectID":"/linter-go/:5:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"IV. sqlrows 在 Go 的 database/sql 包中,sql.Rows 是一个 struct,用于表示从数据库查询中返回的多行结果。 它提供了一组方法,允许开发者逐行读取查询结果。 迭代结果:使用 Next() 方法逐行遍历结果集。 扫描数据:使用 Scan() 方法将当前行的列值复制到指定的变量中。 关闭结果集:使用 Close() 方法释放与结果集相关的资源。 sqlrows 的官方介绍: sqlrows is a static code analyzer which helps uncover bugs by reporting a diagnostic for mistakes of sql.Rows usage. ","date":"2024-04-19","objectID":"/linter-go/:6:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 go install github.com/gostaticanalysis/sqlrows@latest ","date":"2024-04-19","objectID":"/linter-go/:6:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 sqlrows ? $ sqlrows sqlrows is a tool for static analysis of Go programs. Usage of sqlrows: sqlrows unit.cfg # execute analysis specified by config file sqlrows help # general help sqlrows help name # help on specific analyzer and its flags Go 源码【注意 Not Good(NG) 处】 // main.go package kyden import ( \"context\" \"database/sql\" ) func f(ctx context.Context, db *sql.DB) (interface{}, error) { rows, err := db.QueryContext(ctx, \"SELECT * FROM users\") defer rows.Close() // NG: using rows before checking for errors if err != nil { return nil, err } // defer rows.Close() // NG: this return will not release a connection. for rows.Next() { err = rows.Scan() if err != nil { return nil, err } } return nil, nil } 针对两种 NG 的不同输出: go vet -vettool=$(which sqlrows) main.go # command-line-arguments ./main.go:10:11: using rows before checking for errors go vet -vettool=$(which sqlrows) main.go # command-line-arguments ./main.go:9:33: rows.Close must be called ","date":"2024-04-19","objectID":"/linter-go/:6:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"V. funlen funlen,用于检查函数的长度,确保函数的可读性和可维护性。 默认情况下,funlen 将函数的最大行数限制(lines)为 60 行,最大语句数(statements)限制为 40 条。 通常,funlen 会结合 golangci-lint 使用, 并集成到开发工作流中,提升代码质量. ","date":"2024-04-19","objectID":"/linter-go/:7:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 funlen 可以通过 golangci-lint 安装: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest ","date":"2024-04-19","objectID":"/linter-go/:7:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 funlen ? linters: disable-all: true enable: - funlen linters-settings: funlen: lines: 60 statements: 40 使用示例 // main.go package main import ( \"fmt\" ) func main() { f() } func f () { fmt.Println(\"Test funlen\") a := 1 fmt.Println(a) b := 1 fmt.Println(b) c := 1 fmt.Println(c) } 下面的 .golangci.yml 仅用于展示 funlen 的用法,具体参数请根据实际项目自行调整。 # .golangci.yml linters: disable-all: true enable: - funlen linters-settings: funlen: lines: 6 statements: 4 $ golangci-lint run main.go:12: Function 'f' has too many statements (7 \u003e 4) (funlen) ","date":"2024-04-19","objectID":"/linter-go/:7:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"VI. goconst goconst 会扫描代码,识别出在多个地方重复出现的字符串。 这些字符串通常是相同的文本,开发者通过将重复的字符串提取为常量,代码变得更加清晰,减少了硬编码的出现,降低了出错的可能性。 可以根据项目需求自定义 goconst 的行为,例如设置字符串的最小长度、最小出现次数等。 goconst 通常作为 golangci-lint 的一部分使用。 ","date":"2024-04-19","objectID":"/linter-go/:8:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 goconst ? linters: disable-all: true enable: - goconst linters-settings: goconst: min-len: 3 min-occurrences: 3 使用示例 // main.go package main import \"fmt\" func f() { a := \"Hello\" fmt.Println(a) b := \"Hello\" fmt.Println(b) c := \"Hello\" fmt.Println(c) } 下面的 .golangci.yml 仅用于展示 funlen 的用法,具体参数请根据实际项目自行调整。 # .golangci.yml linters: disable-all: true enable: - goconst linters-settings: goconst: min-len: 3 min-occurrences: 3 $ golangci-lint run main.go:7:10: string `Hello` has 3 occurrences, make it a constant (goconst) a := \"Hello\" ^ ","date":"2024-04-19","objectID":"/linter-go/:8:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"VII. ineffassign ineffassign,主要用于检测代码中对现有变量的赋值操作是否未被使用。 这种未使用的赋值通常是代码中的潜在错误,可能导致逻辑上的混乱或资源的浪费。 ","date":"2024-04-19","objectID":"/linter-go/:9:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 ineffassign ? 通常作为 golangci-lint 的一部分使用。 linters: disable-all: true enable: - ineffassign 使用示例 // main.go package main import \"fmt\" func f() { a := \"Hello\" // ... // Not assign a value to `a` // ... a = \"kyden\" fmt.Println(a) } $ golangci-lint run main.go:7:5: ineffectual assignment to a (ineffassign) a := \"Hello\" ^ ","date":"2024-04-19","objectID":"/linter-go/:9:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"VIII. lll 通过限制行的长度,lll 有助于确保代码在查看时不会横向滚动,提升代码的可读性。 lll,主要用于检查代码行的长度,检查每一行的长度是否超过指定的最大值。 默认情况下,lll 将最大行长度限制为 120 个字符。 ","date":"2024-04-19","objectID":"/linter-go/:10:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 lll ? lll 通常作为 golangci-lint 的一部分使用。 linters: disable-all: true enable: - lll linters-settings: lll: line-length: 80 使用示例 // main.go package kyden func f() int { a := \"This is a very long line that exceeds the maximum line length set by the linter and should be broken up into smaller, more manageable lines.\" return len(a) } golangci-lint run main.go:5: the line is 151 characters long, which exceeds the maximum of 80 characters. (lll) a := \"This is a very long line that exceeds the maximum line length set by the linter and should be broken up into smaller, more manageable lines.\" 解决方案 使用反引号(`)定义多行字符串,允许字符串跨越多行而不需要使用连接符 ","date":"2024-04-19","objectID":"/linter-go/:10:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"IX. errcheck errcheck,专门检查未处理的错误,确保开发者在调用可能返回错误的函数时,正确地检查和处理这些错误,从而提高代码的健壮性和可靠性。 errcheck 会扫描 Go 代码,查找未检查错误的地方 除了检查函数返回的错误,还可以检查类型断言是否被忽略 可以检查是否将错误赋值给了空白标识符 ","date":"2024-04-19","objectID":"/linter-go/:11:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 ? errcheck 通常作为 golangci-lint 的一部分使用 linters-settings: errcheck: check-type-assertions: true # 检查类型断言是否被忽略,默认为 false check-blank: true # 检查是否将错误赋值给空白标识符,默认为 false disable-default-exclusions: true # 禁用默认的忽略函数列表,默认为 false exclude-functions: # 指定要忽略检查的函数列表 # ... 使用示例 // main.go package main import ( \"fmt\" ) func main() { hello(\"Kyden\") // err Not Check _ = hello(\"Kyden\") // err assign to _ err := hello(\"Go\") if err != nil { return } } func hello(str string) error { fmt.Printf(\"Hello, %s\", str) return nil } 下面的 .golangci.yml 仅用于展示 errcheck 的用法,具体参数请根据实际项目自行调整。 # .golangci.yml linters: disable-all: true enable: - errcheck linters-settings: errcheck: check-type-assertions: true check-blank: true golangci-lint run main.go:9:10: Error return value is not checked (errcheck) hello(\"Kyden\") // err Not Check ^ main.go:11:5: Error return value is not checked (errcheck) _ = hello(\"Kyden\") // err assign to _ ^ ","date":"2024-04-19","objectID":"/linter-go/:11:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"X. whitespace whitespace 是一个 Go 语言的 linter,主要用于检查代码中不必要的空行,即检查函数、条件语句(如 if、for)等开头和结尾的多余空行。 ","date":"2024-04-19","objectID":"/linter-go/:12:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 whitespace ? whitespace 也包含在 golangci-lint 中,只需在配置中启用即可。 linters: disable-all: true enable: - whitespace 使用示例 // main.go package main import ( \"fmt\" ) func main() { err := hello(\"Kyden\") if err != nil { return } } func hello(str string) error { if len(str) \u003c= 0 { return fmt.Errorf(\"str len \u003c= 0\") } fmt.Printf(\"Hello, %s\", str) return nil } $ gosrc golangci-lint run main.go:15:31: unnecessary leading newline (whitespace) ^ main.go:25:1: unnecessary trailing newline (whitespace) ^ main.go:17:23: unnecessary leading newline (whitespace) ^ ","date":"2024-04-19","objectID":"/linter-go/:12:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"XI. GolangCI-Lint 生产级静态分析工具 golangci-lint is a fast Go linters runner. It runs linters in parallel, uses caching, supports YAML configuration, integrates with all major IDEs, and includes over a hundred linters. golangci-lint 是一款快速的 Go 语言 linter,它并行运行多个 linter 程序,使用缓存,支持 YAML 配置,与所有主流集成开发环境集成,并包含一百多个 linter 程序。 ","date":"2024-04-19","objectID":"/linter-go/:13:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest # Verify golangci-lint --version ","date":"2024-04-19","objectID":"/linter-go/:13:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 golangci-lint ? 在不进行任何配置的情况下,GolangCI-Lint 将默认采用启动以下 Linters: errcheck, gosimple, govet, ineffassign, staticcheck, unused. 也可以通过传递 -E(--enable) 参数来启动 Linter,传递 -D(--disable) 来禁用 Linter. golangci-lint run --disable-all -E errcheck ","date":"2024-04-19","objectID":"/linter-go/:13:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"Visual Studio Code 集成 由于个人一直使用 VSCode 开发各种程序,这里只展示其如何集成 GolangCI-Lint。 Step 1. settings.json 启用 golangci-lint \"go.lintTool\": \"golangci-lint\", \"go.lintFlags\": [ \"--fast\" // Using it in an editor without --fast can freeze your editor. ] Step 2. 配置 .golangci.yml 当使用 Golangci-lint 时,它会自动在编辑的 Go 文件所在的目录或父目录中查找 .golangci.yml 配置文件。 如果找到了配置文件,Golangci-lint 就会根据该配置文件的设置来运行 linter。 因此,在 VS Code 的设置中,不需要专门配置 Golangci-lint。 只需要在项目根目录或相应的目录下创建 .golangci.yml 配置文件,并在其中指定需要启用的 linter 和相关选项即可。 Step 3. Enjoy your coding time 🥂 Golangci-lint 同样支持 GoLang、NeoVim 等流行 IDE 集成. ","date":"2024-04-19","objectID":"/linter-go/:13:3","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":".golangci.yml 参考配置 这里给出一个个人在用的 golangci-lint 完整配置文件,以供参考: run: timeout: 5m go: 1.21 linters-settings: funlen: lines: 150 statements: 100 goconst: min-len: 3 min-occurrences: 3 lll: line-length: 80 govet: # 对于linter govet,这里手动开启了它的某些扫描规则 shadow: true check-unreachable: true check-rangeloops: true check-copylocks: true # 启动nilness检测 enable: - nilness linters: disable-all: true enable: - bodyclose - errcheck - funlen - goconst - gocyclo - gofmt - goimports - gosimple - govet - ineffassign - lll - misspell # Go 静态分析工具,专注于检查代码中的拼写错误 - nilerr - rowserrcheck - staticcheck - typecheck - unconvert - unparam - unused - whitespace issues: skip-dirs: - test exclude-files: - _test.go 更多详细信息,请参考官方文档 ","date":"2024-04-19","objectID":"/linter-go/:13:4","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"XII. reviewdog A code review dog who keeps your codebase healthy. reviewdog 是一个用于自动化代码审查的工具,旨在通过集成各种 linter 工具来简化代码质量检查。它能够将 lint 工具的输出结果作为评论发布到代码托管服务(如 GitHub、GitLab 等),从而提高代码审查的效率和准确性。 ","date":"2024-04-19","objectID":"/linter-go/:14:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"功能 自动发布评论:reviewdog 可以将 lint 工具的结果自动发布为评论,帮助开发者快速识别代码中的问题。 支持多种 linter:它支持多种静态分析工具,包括 golangci-lint、eslint、pylint 等,可以方便地集成到现有的开发流程中。 过滤输出:支持根据 diff 过滤 lint 工具的输出,只报告在当前变更中出现的问题。 多种报告模式:支持多种报告模式,如 GitHub PR 评论、GitHub Checks、GitLab 合并请求讨论等。 本地运行:除了在 CI/CD 环境中运行外,reviewdog 也可以在本地环境中使用,方便开发者在提交代码前进行检查。 ","date":"2024-04-19","objectID":"/linter-go/:14:1","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"安装 # Install the latest version. (Install it into ./bin/ by default). $ curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s # Specify installation directory ($(go env GOPATH)/bin/) and version. $ curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b $(go env GOPATH)/bin [vX.Y.Z] # In alpine linux (as it does not come with curl by default) $ wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s [vX.Y.Z] 推荐使用第二种安装方式 curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b $(go env GOPATH)/bin,具体安装实例如下: $ curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b $(go env GOPATH)/bin reviewdog/reviewdog info checking GitHub for latest tag reviewdog/reviewdog info found version: 0.20.1 for v0.20.1/Darwin/arm64 reviewdog/reviewdog info installed /Users/kyden/go/bin/reviewdog ","date":"2024-04-19","objectID":"/linter-go/:14:2","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"如何使用 reviewdog ? 本地使用 golangci-lint run ./... 2\u003e\u00261 | reviewdog -f=golangci-lint -reporter=local 官方示例 ","date":"2024-04-19","objectID":"/linter-go/:14:3","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"Github Action 1. 创建 GitHub Actions 工作流 在项目根目录下创建一个 GitHub Actions 工作流文件,.github/workflows/reviewdog.yml 2. 配置 .golangci.yml 在项目根目录下创建一个 .golangci.yml 配置文件,配置需要启用的 linter 3. 提交代码 当你提交代码并创建拉取请求时,GitHub Actions 会自动运行 reviewdog,并根据 lint 工具的输出在拉取请求中添加评论,指出代码中的问题。 更多内容请参考官方示例 ","date":"2024-04-19","objectID":"/linter-go/:14:4","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"XIII. Summary 综上所述,Golang 生态中有众多优秀的 Linter 工具,它们能够有效地检查代码质量,提高项目的可维护性和可靠性。 开发者可以根据项目需求,选择合适的 Linter 工具,并将其集成到 CI/CD 流程中,持续改善代码质量。 未来,随着 Golang 社区的不断发展,相信会有更多优秀的 Linter 工具问世,为 Golang 开发者提供更加强大的代码分析能力。 ","date":"2024-04-19","objectID":"/linter-go/:15:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Golang","Linter","Static Check","CI/CD"],"content":"XIV. Reference Cyclomatic complexity Gocyclo bodyclose sqlrows GolangCI-Lint static analysis reviewdog ","date":"2024-04-19","objectID":"/linter-go/:16:0","tags":["Golang","Linter","Static Check","CI/CD"],"title":"深入解读 Golang 常用 Linter 工具及最佳实践","uri":"/linter-go/"},{"categories":["Typora","Theme","Markdown","Software"],"content":"分享一个好看的 Typora 主题 ","date":"2024-04-18","objectID":"/typora-theme/:0:0","tags":["Typora","Theme","Markdown","Software"],"title":"Typora Theme","uri":"/typora-theme/"},{"categories":["Typora","Theme","Markdown","Software"],"content":"效果 本 Typora 软件主题是在 Purple 之上修改而来,具体效果如下: ","date":"2024-04-18","objectID":"/typora-theme/:1:0","tags":["Typora","Theme","Markdown","Software"],"title":"Typora Theme","uri":"/typora-theme/"},{"categories":["Typora","Theme","Markdown","Software"],"content":"Source 由于实现源码太长,给出下载连接:Kyden.css ","date":"2024-04-18","objectID":"/typora-theme/:2:0","tags":["Typora","Theme","Markdown","Software"],"title":"Typora Theme","uri":"/typora-theme/"},{"categories":["Typora","Theme","Markdown","Software"],"content":"Reference Typora Purple ","date":"2024-04-18","objectID":"/typora-theme/:3:0","tags":["Typora","Theme","Markdown","Software"],"title":"Typora Theme","uri":"/typora-theme/"},{"categories":[],"content":"Kyden 起源 奥地利心理学家阿德勒曾说,幸福的人用童年治愈一生,不幸的人用一生治愈童年。 本人来自于华中地区的小农村,毋庸置疑拥有一个非常轻松加愉快的童年,而“纸鸢飞舞”属于童年中的记忆深刻的事物,故以此为作为昵称。 Kyden (纸鸢飞舞,鸢舞) 来自 “kite” 和“dance” 组合的变体 “KiteDance”。 ","date":"2024-04-18","objectID":"/about/:1:0","tags":[],"title":"About Kyden (鸢舞)","uri":"/about/"},{"categories":[],"content":"Special Thanks Thanks to the authors of following resources included in the theme: Hugo LoveIt Gitalk ","date":"2024-04-18","objectID":"/about/:2:0","tags":[],"title":"About Kyden (鸢舞)","uri":"/about/"},{"categories":["Linux","Software"],"content":"详细介绍日常使用 Arch Linux 时的各种问题与解决方案,例如 Arch 安装、常用软件推荐、系统优化等 ","date":"2024-04-17","objectID":"/archlinuxnote/:0:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"零、Install Arch Download Arch Linux ISO archlinux-x86_64.iso U 盘 ventoy 准备 略 选择 Arch Linux install medium (x86_64, UEFI) 启动安装环境 进入 root@archiso 后,需要设置互联网,推荐使用网线连接 检查网络接口是否已经启用 ip link 2: enp0s3: \u003cBROADCAST,MULTICAST,UP,LOWER_UP\u003e mtu 1500 ... 尖括号内的“UP”,表示接口已经启用,否则使用以下命令:ip link set enp0s3 up 请使用 ping 命令测试网络: ping www.baidu.com 更新系统时钟: 在互联网连接之后,systemd-timesyncd 服务将自动校准系统时间,便于安装软件包时验证签名 timedatectl 分区设置 mkfs.ext4 /dev/nvme1n1p7 #用作根分区,挂载到 / # mkfs.fat -F32 /dev/nvme1n1p3 #用作EFI分区 ,挂载到 /boot/efi # 如果安装Windows时已经有个EFI分区,就把上面的/dev/sda1换成已有的EFI分区 mkfs.ext4 /dev/nvme1n1p8 # 挂载到 /home 目录 # mount mount /dev/nvme1n1p7 /mnt #挂载根目录 mkdir -p /mnt/boot/efi #EFI分区的挂载点 mount /dev/nvme1n1p1 /mnt/boot/efi #挂载EFI分区 mount --mkdir /dev/nvme1n1p8 /mnt/home 选择软件镜像仓库 手动修改/etc/pacman.d/mirrorlist vim /etc/pacman.d/mirrorlist --- Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch --- pacman -Sy archlinuxcn-keyring pacman -Syyu 安装基础包 pacstrap /mnt bash base base-devel linux linux-headers linux-firmware neovim xsel // fstab genfstab -U -p /mnt \u003e\u003e /mnt/etc/fstab chroot arch-chroot /mnt # 时区 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime hwclock --systohc # hostname vim /etc/hostname --- 键入:`arch` --- # 设置 locale vim /etc/locale.conf --- 键入:`LANG_en_US.UTF-8` --- vim /etc/locale.gen --- 取消注释:`#en_US.UTF-8 UTF-8` 取消注释:`#zh_CN.UTF-8 UTF-8` --- locale-gen # 网络管理器,蓝牙 pacman -S networkmanager bluez bluez-utils pulseaudio-bluetooth alsa-utils pulseaudio pulseaudio-alsa sof-firmware systemctl enable NetworkManager.service systemctl enable bluetooth.service # root password passwd --- 键入密码:xxxxxx --- # ucode cat /proc/cpuinfo | grep \"model name\" pacman -S intel-ucode # amd-ucode # 安装引导加载程序 pacman -S grub efibootmgr os-prober grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB # 配置 os-prober vim /etc/default/grub --- 取消注释:`GRUB_DISABLE_OS_PROBER=false` --- grub-mkconfig -o /boot/grub/grub.cfg # Create user and usergroup useradd -m -G wheel kyden passwd kyden --- 键入密码 --- # 修改权限 pacman -S sudo man-pages man-db vim /etc/sudoers --- 取消注释:`%wheel ALL=(ALL:ALL) ALL` --- su - kyden # KDE sudo pacman -S plasma xorg nvidia dolphin konsole fish noto-fonts-cjk noto-fonts-emoji sudo systemctl enable sddm # reboot exit swapoff /mnt/swapfile umount -R /mnt reboot ","date":"2024-04-17","objectID":"/archlinuxnote/:1:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"一、内核更换 Install The Desired Kernel sudo pacman -S linux-lts linux-lts-headers Editing GRUB Config File sudo vim /etc/default/grub # --- `GRUB_DISABLE_SUBMENU=y` # disables the GRUB submenu, i.e., it enables all the available kernels to be listed on the main GRUB Menu itself instead of the “Advanced option for Arch Linux” option. `GRUB_DEFAULT=saved` # saves the last kernel used `GRUB_SAVEDEFAULT=true` # makes sure that grub uses the last selected kernel is used as default Re-Generate GRUB Configuration file sudo grub-mkconfig -o /boot/grub/grub.cfg Choose Kernel From GRUB During Boot ","date":"2024-04-17","objectID":"/archlinuxnote/:2:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"二、Software ","date":"2024-04-17","objectID":"/archlinuxnote/:3:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Check NetworkManager ping baidu.com systemctl enable NetworkManager ","date":"2024-04-17","objectID":"/archlinuxnote/:3:1","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"pacman 镜像修改 sudo vim /etc/pacman.conf # ---------------- # Misc options Color ParallelDownloads = 5 # --- [multilib] Include = /etc/pacman.d/mirrorlist #--- 键入: [archlinuxcn] Server = https://mirrors.utsc.edu.cn/archlinuxcn/$arch #--- sudo pacman -Syyu sudo pacman -S archlinuxcn-keyring ","date":"2024-04-17","objectID":"/archlinuxnote/:3:2","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"常见通用软件 sudo pacman -S yay pacman -Sc # 清除软件缓存,即/var/cache/pacman/pkg目录下的文件 pacman -Rns package_name pacman -U pacage.tar.zx # 从本地文件安装 pactree pacage_name # 显示软件的依赖树 yay -S fish # curl -L https://get.oh-my.fish | fish fish_config # 取消问候语 set -U fish_greeting \"\" sudo vim /etc/systemd/system/clash.service sudo systemctl daemon-reload sudo systemctl enable clash sudo systemctl start clash sudo systemctl status clash sudo pacman -S obs-studio 输入法 sudo pacman -S fcitx5 fcitx5-configtool fcitx5-qt fcitx5-gtk fcitx5-chinese-addons fcitx5-material-color fcitx5-pinyin-moegirl fcitx5-pinyin-zhwiki # sudo vim /etc/environment GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=\\@im=fcitx # 为了让一些使用特定版本 SDL2 库的游戏能正常使用输入法 SDL_IM_MODULE=fcitx yay -S clash-verge-rev-bin yay -Sy neofetch google-chrome obs-studio baidunetdisk nutstore-experimental xunlei-bin telegram-desktop gitkraken visual-studio-code-bin typora-free redis net-tools pot-translation translate-shell okular snipaste gwenview kcalc wemeet-bin vlc wget ark shotcut inkscape ninja gnu-netcat tcpdump cmake clang tree python-pip caj2pdf-qt ttf-hack-nerd transmission-gtk gpick speedcrunch drawio-desktop crow-translate zeal yay -S electronic-wechat-uos-bin linuxqq lx-music-desktop gpick: 可以从桌面任何地方取色,并且它还提供一些其它的高级特性 SpeedCrunch: 一个漂亮,开源,高精度的科学计算器 Snipaste: 截图工具,如不可用可选用spectacle drawio-desktop: Security-first diagramming for teams crow-translate:翻译工具 zeal:离线文档浏览器 office yay -S wps-office wps-office-mui-zh-cn ttf-wps-fonts ","date":"2024-04-17","objectID":"/archlinuxnote/:3:3","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"远程工具 - Remmina $ yay -S remmina freerdp 使用教程 安装 freerdp 插件后,可以走 RDP 协议远程 Win10(Win10 不需要其他任何设置) ","date":"2024-04-17","objectID":"/archlinuxnote/:3:4","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Git 配置git 设置user.name和user.emal git config --global user.name \"KydenLu\" git config --global user.email [email protected] # check git config --list 生成密钥 ssh-keygen -t rsa -C '[email protected]' 上述代码执行完成后,要求多次输入密码,请不要输入密码 github配置 SSH Keys 打开生成的 Key 文件 /.ssh/id_rsa.pub 复制全部内容,在 Key 中粘贴 Git 常用命令 git status git clone git pull git push git commit -m 'commits' or git commit -m 'commits' xxx.fileType git add . or git xxx.fileType git reflog Git实现从本地添加项目到远程仓库 Steps: 创建一个新的远程仓库 - Create a new repo Create repository 创建并初始化本地仓库 - git init 可添加待上传到远程仓库的项目文件 远程仓库和本地仓库关联 - git remote add origin [email protected]:lutianen/\u003crepository name\u003e 项目文件添加、提交、推送 git add file git commit -m '\u003ccommit statements\u003e' file git push -u origin master *由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来 在以后的推送或者拉取时就可以简化命令 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:5","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Golang # Download and install go sudo pacman -S go # Set environment variable in `.config/fish/config.sh` or `/etc/profile` or `~/.profile` GOROOT /usr/lib/go GOPATH /home/kyden/goProj GOBIN /home/kyden/goProj/bin PATH $GOPATH/bin $GOROOT/bin $GOBIN $PATH GOROOT,设置 Golang 的安装位置 GOBIN,执行 go install 后生成可执行文件的目录 GOPATH,工作目录,一般设置到用户目录下 # Go 工作目录结构 ├── bin # 存放 `go install` 命令生成的可执行文件,且可把 `$GOBIN` 路径加入到 `PATH` 环境变量中,这样就可以直接在终端中使用 go 开发生成的程序 ├── pkg # 存放 go 编译生成的文件 ├── readme.md └── src # 存放 go 源码,不同工程项目的代码以包名区分 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:6","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"MySQL 很多linux发行版都放弃了对mysql的支持(原因自行 Google)转而支持mariadb(mysql的另一个分支),Archlinux就是其中之一,mariadb具有和mysql一模一样的操作命令,所以完全不用考虑迁移兼容的问题 安装mariadb: sudo pacman -Sy mariadb 配置mariadb命令,创建数据库都在/var/lib/mysql/目录下面: sudo mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql 开启mariadb 服务: systemctl start mariadb 初始化密码,期间有让你设置密码的选项,设置你自己的密码就行了,然后根据自己理解y/n就可,因为很多后面可以再修改: sudo /usr/bin/mysql_secure_installation 登录mariadb 和mysql命令是一样的: mysql -u root -p 设置开机自启动服务: systemctl enable mariadb #开机自启动 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:7","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"you-get 命令行程序,提供便利的方式来下载网络上的媒体信息。 yay -S you-get 下载流行网站之音视频,例如YouTube, Youku, Niconico,以及更多 于您心仪的媒体播放器中观看在线视频,脱离浏览器与广告 下载您喜欢的网页上的图片 下载任何非HTML内容,例如二进制文件 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:8","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"挂载其他硬盘分区 # Get UUID and TYPE sudo blkid # /dev/nvme1n1p3: LABEL=\"Document\" BLOCK_SIZE=\"512\" UUID=\"111915F1111915F1\" TYPE=\"ntfs\" PARTLABEL=\"Basic data partition\" PARTUUID=\"666266ba-233b-11ed-95be-00e04c3656eb\" # Write UUID TYPE ... sudo vim /etc/fstab # \u003cdevice\u003e \u003cdir\u003e \u003ctype\u003e \u003coptions\u003e \u003cdump\u003e \u003cfsck\u003e UUID=111915F1111915F1 /home/kyden/doc ntfs3 defaults 0 0 \u003cdevice\u003e 描述要挂载的特定块设备或远程文件系统 \u003cdir\u003e 描述挂载目录 \u003ctype\u003e 文件系统类型 \u003coptions\u003e 相关的挂载选项 \u003cdump\u003e 会被 dump(8) 工具检查。该字段通常设置为 0, 以禁用检查 \u003cfsck\u003e 设置引导时文件系统检查的顺序; 对于 root 设备该字段应该设置为 1。对于其它分区该字段应该设置为 2,或设置为 0 以禁用检查 我使用 TYPE 为 ntfs 时导致启动失败,修改为 ntfs3 后成功挂载 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:9","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Present Windows ","date":"2024-04-17","objectID":"/archlinuxnote/:3:10","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"scp 文件上传、下载 上传 scp ./cifar-10-python.tar.gz kyden@\u003cip\u003e:/home/kyden/ 下载 scp kyden@\u003cip\u003e:/var/tmp/a.txt /var ","date":"2024-04-17","objectID":"/archlinuxnote/:3:11","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"picgo picgo-core 【Recommend】 Download and Install PigGo-Core Get token with GitHub Configure config.json NOTE:When using ~/.picgo/config.json, delete the comments to avoid unnecessary trouble(使用时,将注释删掉,以免产生不必要的麻烦) { \"picBed\": { \"current\": \"github\", \"github\": { \"repo\": \"\u003cuserName\u003e/PicBed\", // 设定仓库名:上文在 GitHub 创建的仓库 `\u003cuserName\u003e/PicBed` \"branch\": \"master\", // 设定分支名:`master` \"token\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\", // 设定 Token:上文生成的 toke \"path\": \"\", // 指定存储路径:为空的话会上传到根目录,也可以指定路径 \"customUrl\": \"\" // 设定自定义域名:可以为空 }, \"uploader\": \"github\", \"transformer\": \"path\" }, \"picgoPlugins\": { \"picgo-plugin-github-plus\": true } } picgo app 【Not Recommend】 略 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:12","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"VirtualBox sudo pacman -S virtualbox # dkms sudo pacman -S virtualbox-host-dkms sudo dkms autoinstall sudo modprobe vboxdrv sudo pacman -S virtualbox-guest-utils sudo systemctl enable vboxservice.service ","date":"2024-04-17","objectID":"/archlinuxnote/:3:13","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"CUDA \u0026 cuDNN yay -S cuda-11.7 cudnn8-cuda11.0 Arch Linux 会将 CUDA 相关档案安装至 /opt/cuda,有需要的话可以将 CUDA 的 PATH 加到 ~/bashrc,此路径永远指向最新版CUDA # cuda # fish set PATH /opt/cuda-11.7/bin $PATH set LD_LIBRARY_PATH /opt/cuda-11.7/lib64/ $PATH pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 torchaudio==0.13.1 --extra-index-url https://download.pytorch.org/whl/cu117 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:14","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"wireguard # Install wg sudo pacman -S wireguard-tools # Open wg sudo wg-quick up wg0 # Close wg sudo wg-quick down wg0 wg0.conf /etc/wireguard/wg0.conf [Interface] PrivateKey = xxxxxxxxxxxxxxxxxxx Address = 172.16.0.2/32 Address = 2606:4700:110:8fe8:bc78:d25a:896a:9696/128 DNS = 1.1.1.1 MTU = 1280 [Peer] PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= AllowedIPs = 0.0.0.0/0 AllowedIPs = ::/0 Endpoint = xxx.xxx.xxx.xxx 配置步骤如下 # Error: /usr/bin/wg-quick: line 32: resolvconf: command not found sudo pacman -S openresolv WARP 密钥获取:Telegram 中 Warp+ Bot 获取 2.配置文件生成:https://replit.com/@misaka-blog/wgcf-profile-generator?v=1 https://replit.com/@tianenxd/wgcf-profile-generator,需要登陆 3.优选IP ==warp-yxip== wget -N https://gitlab.com/Misaka-blog/warp-script/-/raw/main/files/warp-yxip/warp-yxip.sh \u0026\u0026 bash warp-yxip.sh ","date":"2024-04-17","objectID":"/archlinuxnote/:3:15","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Clash Verge 解决DNS泄露问题 DNS 泄露其实并没有一个明确的定义,也不存在一个官方解释。 大概就是说你访问YouTube等黑名单网站的时候,使用中国大陆的DNS服务器进行了解析,这可能导致隐私问题的。 如果在 DNS Leak Test 、ipleak这种网站的列表中看到了中国国旗,就要意识到可能发生了DNS泄露。 虽然没有人知道具体的探测机制是什么,但很可能是从网络层面获取的。在一般的家庭网络拓扑中,wireshark可以看到什么内容,运营商就能看见什么内容,所以你使用114.114.114.114、223.5.5.5这样的DNS解析去访问了什么网站是很清晰的。 Clash开启TUN模式,关闭系统代理去使用:与普通的系统代理模式区别在于,TUN模式下Clash会创建一张虚拟网卡,从网络层面接管所有的网络流量。 Step 1: 开启TUN模式 Step 2: 使用稳定的DNS DNS这部分有人会教使用运营商的DNS,运营商的DNS只适合小白用户,因为他可能连反诈,所以建议使用国内大厂的。 [Optional] 关闭浏览器的QUIC, 中国大陆的isp是限速udp的, 所以导致QUIC这个优秀的协议, 到了中国大陆的网络下成了个负面增益效果。 about://flags/#enable-quic 设置为Disabled (点下方弹出的重启浏览器生效) 关闭浏览器中的“安全DNS” chrome://settings/security 在Clash Verge的【Profiles】中,点右上角的\"NEW\" -\u003e Type选择\"Script\" -\u003e Name随意填写(例如,“修改DNS”) 右击新建的文件,然后\"Edit File\",输入以下内容后启用: function main(content) { const isObject = (value) =\u003e { return value !== null \u0026\u0026 typeof value === 'object' } const mergeConfig = (existingConfig, newConfig) =\u003e { if (!isObject(existingConfig)) { existingConfig = {} } if (!isObject(newConfig)) { return existingConfig } return { ...existingConfig, ...newConfig } } const cnDnsList = [ 'tls://223.5.5.5', 'tls://1.12.12.12', ] const trustDnsList = [ 'https://doh.apad.pro/dns-query', 'https://dns.cooluc.com/dns-query', 'https://1.0.0.1/dns-query', ] const notionDns = 'tls://dns.jerryw.cn' const notionUrls = [ 'http-inputs-notion.splunkcloud.com', '+.notion-static.com', '+.notion.com', '+.notion.new', '+.notion.site', '+.notion.so', ] const combinedUrls = notionUrls.join(','); const dnsOptions = { 'enable': true, 'default-nameserver': cnDnsList, // 用于解析DNS服务器 的域名, 必须为IP, 可为加密DNS 'nameserver-policy': { [combinedUrls]: notionDns, 'geosite:geolocation-!cn': trustDnsList, }, 'nameserver': trustDnsList, // 默认的域名解析服务器, 如不配置fallback/proxy-server-nameserver, 则所有域名都由nameserver解析 } // GitHub加速前缀 const githubPrefix = 'https://ghproxy.lainbo.com/' // GEO数据GitHub资源原始下载地址 const rawGeoxURLs = { geoip: 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat', geosite: 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat', mmdb: 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country-lite.mmdb', } // 生成带有加速前缀的GEO数据资源对象 const accelURLs = Object.fromEntries( Object.entries(rawGeoxURLs).map(([key, githubUrl]) =\u003e [key, `${githubPrefix}${githubUrl}`]), ) const otherOptions = { 'unified-delay': true, 'tcp-concurrent': true, 'profile': { 'store-selected': true, 'store-fake-ip': true, }, 'sniffer': { enable: true, sniff: { TLS: { ports: [443, 8443], }, HTTP: { 'ports': [80, '8080-8880'], 'override-destination': true, }, }, }, 'geodata-mode': true, 'geox-url': accelURLs, } content.dns = mergeConfig(content.dns, dnsOptions) return { ...content, ...otherOptions } } 设置完成后,验证DNS解析结果是否都是来自国外的Cloudflare和Google的DNS, 这时节点服务器不管拿到了你传过去的真ip还是假ip地址, 他都会再去请求一次Cloudflare/Google的DNS服务, 确保解析的正确性。 重要的是没有中国大陆的DNS服务器了,如果还是有,那你应该往当前设备的更上层寻找问题所在,比如路由器的设置等。 Clash Verge 解决 GEOIP,CN问题 目前市面上绝大多数的代理工具都依赖于 GeoIP2 数据库判断地址所属地。它们的规则结尾部分一般都会有一条类似 GEOIP, CN,用来查询目的 IP 地址是否属于中国大陆,从而判断是否直连。 这些代理工具通常使用的 GeoIP2 数据库是来自于 MaxMind 的 GeoLite2 免费数据库。这个数据库目前存在一下几个问题: 获取不便:从 2019 年 12 月 30 日起,必须注册后才能下载 数据量大:数据库庞大,包含全球的 IP 地址段,约 10 MB 准确度低:对中国大陆的 IP 地址判定不准,如:香港阿里云的 IP 被判定为新加坡、中国大陆等。 庞大的数据量对于大多数中国大陆的用户来说是没有意义的,因为只仅需要去判断 IP 的地理位置是否属于中国大陆境内,其他国家的 IP 一律代理/直连。过多的数据量会增加载入时间,降低查询效率。 我们在之前创建的Script中已经包含了下载更精简合适中国大陆的IP数据库链接, 现在只需要手动操作下载和替换即可: Update GeoData: Clash Verge Rev的设置菜单中点击Update GeoData 验证下载: 打开Clash Verge托盘中的APP Dir,找到geoip.dat文件,验证其大小是否为几百KB 重启Clash Verge:确保数据库被正确应用 ","date":"2024-04-17","objectID":"/archlinuxnote/:3:16","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"三、System optimization ","date":"2024-04-17","objectID":"/archlinuxnote/:4:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"SSD 优化 TRIM, 会帮助清理SSD中的块,从而延长SSD的使用寿命 sudo systemctl enable fstrim.timer sudo systemctl start fstrim.timer ","date":"2024-04-17","objectID":"/archlinuxnote/:4:1","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"SWAP 设置 https://wiki.archlinux.org/title/Swap#Swappiness 查看 swap 使用率,一般是 60 ,意思是 60% 的概率将内存整理到 swap: cat /proc/sys/vm/swappiness 修改 swap 使用策略为 10%,即 10% 的概率将内存整理到 swap: sudo sysctl -w vm.swappiness=10 修改配置文件:sudo vim /etc/sysctl.d/99-swappiness.conf 在文件末尾加上下面这行内容:vm.swappiness=10 重启后可查看 swappiness 的值 ","date":"2024-04-17","objectID":"/archlinuxnote/:4:2","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"Systemd journal size limit 控制日志最大可使用多少磁盘空间,修改/etc/systemd/journald.conf 中的SystemMaxUse参数 SystemMaxUse=50M ","date":"2024-04-17","objectID":"/archlinuxnote/:4:3","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"四、Games sdl-ball: yay -S sdl-ball ","date":"2024-04-17","objectID":"/archlinuxnote/:5:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"五、Problem And Solution ","date":"2024-04-17","objectID":"/archlinuxnote/:6:0","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"1. clear command - terminals database is inaccessible 解决方案:Path for Anaconda3 is set in .bashrc. It is interfering with the clear command. Removing Anaconda path from path solved the issue. ~ echo $CONDA_PREFIX (base) /opt/miniconda sudo mv $CONDA_PREFIX/bin/clear $CONDA_PREFIX/bin/clear_old ","date":"2024-04-17","objectID":"/archlinuxnote/:6:1","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"2. tput: unknown terminal \"xterm-256color\" 解决方案:setenv TERMINFO /usr/lib/terminfowps ","date":"2024-04-17","objectID":"/archlinuxnote/:6:2","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"3. 更新内核后,双屏显示时,某一个屏幕黑屏,但鼠标能够移动过去并显示,另一屏幕正常 解决方案:xrandr --output HDMI-1-0 --right-of eDP1 --auto 命令解释:配置 HDMI-1-0 输出,使其位于 eDP1 输出的右侧,并自动选择最佳的分辨率和刷新率设置 $ xrandr --listmonitors Monitors: 2 0: +*eDP1 2560/360x1440/200+0+0 eDP1 1: +HDMI-1-0 1920/479x1080/260+2560+0 HDMI-1-0 $ xrandr --output HDMI-1-0 --right-of eDP1 --auto ","date":"2024-04-17","objectID":"/archlinuxnote/:6:3","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"4. zip 压缩包解压乱码 产生这种情况的原因是,zip 格式本身的缺陷导致的:**zip 格式没有指定文件名的编码格式,因此在压缩和解压时都会采用OS本地编码,而 Window 下简体中文采用的是 GBK/GB312 编码,Linux 则采用的是 UTF-8 编码,两者不一致导致了乱码的产生。 解决方案: unzip -O GBK xxx.zip ","date":"2024-04-17","objectID":"/archlinuxnote/:6:4","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"5. wps-office大部分字体粗体出现过粗无法正常显示问题 问题: freetype2更新至2.13.0以上版本后出现的问题。导致wps-office 文档编辑文字大部分字体设置粗体出现过粗无法正常显示。 解决方案:freetype2 降级至 2.13.0 Downloadfreetype2.13.0 降级 sudo pacman -U freetype2-2.13.0-1-x86_64.pkg.tar.zst 修改 /etc/pacman.conf -\u003e IgnorePkg = freetype2,排除掉这个包(不让它更新) freetype2: ignoring package upgrade (2.13.0-1 =\u003e 2.13.2-1) env LD_LIBRARY_PATH=/usr/local/freetype2-2.13.0-1-x86_64/usr/lib `update-desktop-database ~/.local/share/applications ","date":"2024-04-17","objectID":"/archlinuxnote/:6:5","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"6. wpspdf 无法打开 PDF 文件 wpspdf 依赖于 libtiff5.so.5 以支撑其 PDF 功能。而系统更新后,Arch Linux 提供的是 libtiff.so.6 或更新版本,导致其无法正常工作。 解决方案:安装 libtiff5 ","date":"2024-04-17","objectID":"/archlinuxnote/:6:6","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"7. WPS 无法输入中文 解决方案 wpp wpspdf wpp et ","date":"2024-04-17","objectID":"/archlinuxnote/:6:7","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"8. lx-music 数据同步失败 确保PC端的同步服务已启用成功: 若连接码、同步服务地址没有内容,则证明服务启动失败,此时看启用同步功能复选框后面的错误信息自行解决 在手机浏览器地址栏输入http://x.x.x.x:5963/hello后回车,若此地址可以打开并显示 Hello~::^-^::v4,则证明移动端与PC端网络已互通, 若移动端无法打开第2步的地址,则在PC端的浏览器地址栏输入并打开该地址,若可以打开,则可能性如下: LX Music PC端被电脑防火墙拦截 PC端与移动端不在同一个网络下, 路由器开启了AP隔离(一般在公共网络下会出现这种情况) 要验证双方是否在同一个网络或是否开启AP隔离,可以在电脑打开cmd使用ping命令ping移动端显示的ip地址,若可以通则说明网络正常 ","date":"2024-04-17","objectID":"/archlinuxnote/:6:8","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"9. fatal: unable to access 'https://github.com/xxxxxxx.git/': Failed to connect to github.com port 443: Timed out 问题:代理出问题 解决方案: git config --global --unset http.proxy git config --global --unset https.proxy ","date":"2024-04-17","objectID":"/archlinuxnote/:6:9","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Linux","Software"],"content":"10. fatal: unable to access 'https://github.com/xxxx.git/': gnutls_handshake() failed: The TLS connection was non-properly terminated. 解决方案: git config --global http.sslVerify false ","date":"2024-04-17","objectID":"/archlinuxnote/:6:10","tags":["Arch","Linux","Software"],"title":"Arch Linux 使用指南","uri":"/archlinuxnote/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"借助于 Github Pages 提供的静态网站托管服务,并采用了 Hugo 这一开源项目,加快了建站流程,而且有多种开源网站主题可供选择.","date":"2024-04-17","objectID":"/build-blog/","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"借助于 Github Pages 提供的静态网站托管服务,并采用了 Hugo 这一开源项目,加快了建站流程,而且有多种开源网站主题可供选择. ","date":"2024-04-17","objectID":"/build-blog/:0:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"I. 前言 在博客网站搭建完成之后,有充分的理由相信,自己在未来很长一段时间内将不会再次重复建站。 常言道天有不测风云,为了防止各种意外情况发生,导致本博客网站无法正常使用,同时防止自己忘记搭建流程,记录于此。 ","date":"2024-04-17","objectID":"/build-blog/:1:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"II. 效果 ","date":"2024-04-17","objectID":"/build-blog/:2:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"III. 相关知识简介 ","date":"2024-04-17","objectID":"/build-blog/:3:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"Github Pages GitHub Pages 是一个免费的静态网站托管服务,它允许用户通过 GitHub 存储库来托管和发布网页,可以使用它来展示项目文档、博客或个人简历。 现阶段,Github Pages 支持公共存储库的免费的托管;对于私有仓库,需要进行缴费。 ","date":"2024-04-17","objectID":"/build-blog/:3:1","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"Hugo 官方号称,Hugo 是世界上最快的网站建设框架(The world’s fastest framework for building websites)。 ","date":"2024-04-17","objectID":"/build-blog/:3:2","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"IV. Steps ","date":"2024-04-17","objectID":"/build-blog/:4:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"Github 仓库创建 需要创建两个仓库,一个用于网站源码管理(sA),一个用于网站部署(sB): sA 可以是 public,也可以是 private; sB 仓库的名称必须是 username.github.io(username 是 Github Accout 中username,不是 profile 中的 Name),同时还需要添加 README.md; ","date":"2024-04-17","objectID":"/build-blog/:4:1","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"使用 Hugo 创建网站 首先,使用 Git 将 sA 拉取下来: ~/ $ git clone https://github.com/lutianen/Kyden-blog.git 然后,进入本地的 sA 目录(即,Kyden-blog)下,使用 hugo 建站: # Linux: Install ~/Kyden-blog $ sudo pacman -S hugo ~/Kyden-blog $ hugo version # 建站,然后将生成的内容复制到 `sA` 仓库中 ~/Kyden-blog $ hugo new Kyden-blog ~/Kyden-blog $ mv Kyden-blog/ . ~/Kyden-blog $ rm Kyden-blog -rf ","date":"2024-04-17","objectID":"/build-blog/:4:2","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"Hugo 设置网站主题 可以从 Hugo Themes 挑选合适的主题进行应用: ~/Kyden-blog $ cd themes ~/Kyden-blog/themes $ git clone https://github.com/kakawait/hugo-tranquilpeak-theme.git tranquilpeak 安装 Hugo 主题后,根据个人情况修改相应的配置文件即可; ","date":"2024-04-17","objectID":"/build-blog/:4:3","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"文章管理 启动 Hugo server 启动本地 server: ~/Kyden-blog $ hugo server -D 浏览器打开 http://localhost:1313/ 进行预览; 新建文章 ~/Kyden-blog $ hugo new content `post/Golang/Go.md` # `post/Golang/Go.md` 表明 markdown 的路径 部署文章 构建 Hugo 网站相关静态文件 Hugo 将构建完成的静态内容保存到 sA 仓库中的 public 文件夹中; ~/Kyden-blog $ hugo 部署 进入 public 目录,利用 Git 进行管理该文件夹,并推送到远程 sB 仓库中: ~/Kyden-blog/public $ git init ~/Kyden-blog/public $ git commit -m \"first commit\" ~/Kyden-blog/public $ git branch -M master ~/Kyden-blog/public $ git remote add origin https://github.com/lutianen/lutianen.github.io.git ~/Kyden-blog/public $ git push -u origin master 自动化部署:deploy.sh #!/bin/bash echo -e \"\\033[0;32mDeploying updates to GitHub...\\033[0m\" # Build the project. hugo # if using a theme, replace with hugo -t # Go To Public folder cd public # Add changes to git. git add . # Commit changes. msg=\"rebuilding site `date` \" echo -e \"\\033[0;32m$msg\\033[0m\" if [ $# -eq 1 ] then msg=\"$1\" fi git commit -m \"$msg\" # Push source and build repos. git push origin master # Come Back up to the Project Root cd .. 删除文章 进入 Kyden-blog/post/ 目录中,删除,目标文件夹(包含相关文章资源)即可; NOTE:Kyden-blog/public 中相关文件可以删除,也可以不删除,推荐删除; ","date":"2024-04-17","objectID":"/build-blog/:4:4","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"Google Analytics(分析) 首先,在 GA 网站中注册、设置完成相应选项,即可获取 ID:G-XXXXXXXXXX; 然后在 layout/_default/baseof.html 文件中添加以下代码即可: \u003c!-- Google tag (gtag.js) --\u003e \u003cscript async src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX\"\u003e\u003c/script\u003e \u003cscript\u003e window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXXXXX'); \u003c/script\u003e 该段代码获取方法如下:Google Analytics(分析) -\u003e 管理 -\u003e “媒体资源设置\"列中的数据流 -\u003e 网站 -\u003e 对应的数据流 -\u003e “Google 代码” 下的查看代码说明 -\u003e “安装说明” 选择手动添加 建议添加完成后,在GA分析中进行测试,确保生效 ","date":"2024-04-17","objectID":"/build-blog/:4:5","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"评论系统 - Gitalk 需要注意的是,Gitalk 的评论采用的是 PR/Issue 的方式存储评论,因此,一般需要新建一个专门的 Repo,例如username/gitalk. 申请注册新应用,并填写以下相关内容: Application name: 随意 Homepage URL: 包含 http/https 前缀,例如https://lutianen.github.io Authorization callback URL: 和上面 Homepage URL 保持一致就行 注册完成后,手动生成 Client secrets(只会出现一次),同时获得 Client ID. 最后,在主题设置中填写相应信息即可,例如 LoveIt 中的 config.toml: [params.page.comment] enable = true [params.page.comment.gitalk] enable = true owner = \"lutianen\" repo = \"gitalk\" clientId = \"xxxxxxxxxxxxxxxxxxxx\" clientSecret = \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\" ","date":"2024-04-17","objectID":"/build-blog/:4:6","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"网站运行时间统计 在 footer.html 加入以下内容即可: {{- /* runtime */ -}} \u003c/br\u003e\u003cscript\u003e function siteTime() { var seconds = 1000; var minutes = seconds * 60; var hours = minutes * 60; var days = hours * 24; var years = days * 365; var today = new Date(); var startYear = 2024; var startMonth = 4; var startDate = 18; var startHour = 12; var startMinute = 57; var startSecond = 2; var todayYear = today.getFullYear(); var todayMonth = today.getMonth() + 1; var todayDate = today.getDate(); var todayHour = today.getHours(); var todayMinute = today.getMinutes(); var todaySecond = today.getSeconds(); var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond); var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond); var diff = t2 - t1; var diffYears = Math.floor(diff / years); var diffDays = Math.floor((diff / days) - diffYears * 365); var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours); var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) / minutes); var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours - diffMinutes * minutes) / seconds); if (startYear == todayYear) { document.getElementById(\"sitetime\").innerHTML = \"已安全运行 \" + diffDays + \" 天 \" + diffHours + \" 小时 \" + diffMinutes + \" 分钟 \" + diffSeconds + \" 秒\"; } else { document.getElementById(\"sitetime\").innerHTML = \"已安全运行 \" + diffYears + \" 年 \" + diffDays + \" 天 \" + diffHours + \" 小时 \" + diffMinutes + \" 分钟 \" + diffSeconds + \" 秒\"; } } setInterval(siteTime, 1000); \u003c/script\u003e \u003cspan id=\"sitetime\"\u003e载入运行时间...\u003c/span\u003e ","date":"2024-04-17","objectID":"/build-blog/:4:7","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"V. Problem And Solution ","date":"2024-04-17","objectID":"/build-blog/:5:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"添加图片不显示 Hugo 的配置文件和文章中的引用图片都是以 static 作为根目录,因此图片无法显示的解决方案如下: 将图片放入 static/img 目录下 在文章中的图片引用方式为:/img/xxx.png 无法采用 Typora 等软件进行预览,需要在网页中进行预览: http://localhost:1313/ ","date":"2024-04-17","objectID":"/build-blog/:5:1","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"文章缩略 如果想要文章在某个地方缩略,只需要在该位置加入 \u003c!--more--\u003e 即可。 ","date":"2024-04-17","objectID":"/build-blog/:5:2","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"VI. 视频嵌入 ","date":"2024-04-17","objectID":"/build-blog/:6:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"},{"categories":["Github Pages","Hugo","博客","Linux"],"content":"VI. References Abot Github Pages Hugo 参考文章 Gitalk 评论系统安装 ","date":"2024-04-17","objectID":"/build-blog/:7:0","tags":["Github Pages","Hugo","博客","Linux"],"title":"GitHub Pages + Hugo 建博客站","uri":"/build-blog/"}]