西维蜀黍

【TrueNAS】磁盘健康检查(health check)

# list disk
$ geom disk list

Prints all SMART information

$ smartctl -a /dev/nvme0

Executes extended disk self-test

$ smartctl -t long /dev/ad0 
  ...


【Golang】实现 HTTP Server

Example

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
    // Functions serving as handlers take a http.ResponseWriter and a http.Request as arguments. The response writer is used to fill in the HTTP response. Here our simple response is just “hello\n”.
    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {
    // This handler does something a little more sophisticated by reading all the HTTP request headers and echoing them into the response body.
    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {
    // We register our handlers on server routes using the http.HandleFunc convenience function. It sets up the default router in the net/http package and takes a function as an argument.
    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)
    
    // Finally, we call the ListenAndServe with the port and a handler. nil tells it to use the default router we’ve just set up.
    http.ListenAndServe(":8090", nil)
}
$ go run http-servers.go &

$ curl localhost:8090/hello
hello
  ...


【Golang】实现 - 连接池实现

Conn Interface

// Conn is a generic stream-oriented network connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn interface {
	// Read reads data from the connection.
	// Read can be made to time out and return an Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetReadDeadline.
	Read(b []byte) (n int, err error)

	// Write writes data to the connection.
	// Write can be made to time out and return an Error with Timeout() == true
	// after a fixed time limit; see SetDeadline and SetWriteDeadline.
	Write(b []byte) (n int, err error)

	// Close closes the connection.
	// Any blocked Read or Write operations will be unblocked and return errors.
	Close() error

	// LocalAddr returns the local network address.
	LocalAddr() Addr

	// RemoteAddr returns the remote network address.
	RemoteAddr() Addr

	// SetDeadline sets the read and write deadlines associated
	// with the connection. It is equivalent to calling both
	// SetReadDeadline and SetWriteDeadline.
	//
	// A deadline is an absolute time after which I/O operations
	// fail with a timeout (see type Error) instead of
	// blocking. The deadline applies to all future and pending
	// I/O, not just the immediately following call to Read or
	// Write. After a deadline has been exceeded, the connection
	// can be refreshed by setting a deadline in the future.
	//
	// An idle timeout can be implemented by repeatedly extending
	// the deadline after successful Read or Write calls.
	//
	// A zero value for t means I/O operations will not time out.
	//
	// Note that if a TCP connection has keep-alive turned on,
	// which is the default unless overridden by Dialer.KeepAlive
	// or ListenConfig.KeepAlive, then a keep-alive failure may
	// also return a timeout error. On Unix systems a keep-alive
	// failure on I/O can be detected using
	// errors.Is(err, syscall.ETIMEDOUT).
	SetDeadline(t time.Time) error

	// SetReadDeadline sets the deadline for future Read calls
	// and any currently-blocked Read call.
	// A zero value for t means Read will not time out.
	SetReadDeadline(t time.Time) error

	// SetWriteDeadline sets the deadline for future Write calls
	// and any currently-blocked Write call.
	// Even if write times out, it may return n > 0, indicating that
	// some of the data was successfully written.
	// A zero value for t means Write will not time out.
	SetWriteDeadline(t time.Time) error
}
  ...


【Golang】使用 - 写入文件

ioutil.WriteFile()

WriteFile writes data to a file named by filename. If the file does not exist, WriteFile creates it with permissions perm (before umask); otherwise WriteFile truncates it before writing, without changing permissions.

package main

import (
	"io/ioutil"
	"log"
)

func main() {
	message := []byte("Hello, Gophers!")
	err := ioutil.WriteFile("hello", message, 0644)
	if err != nil {
		log.Fatal(err)
	}
}
  ...


【Golang】使用 - 读取文件

读取整个文件 - ioutil.ReadFile

ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.

package main

import (
	"fmt"
	"io/ioutil"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
  // the most basic file reading task is slurping a file’s entire contents into memory
	dat, err := ioutil.ReadFile("test.txt")
	check(err)
	fmt.Print(string(dat))
}

Ouput:

1
2
3
4
5
6
  ...


【Golang】Concurrent Control

Concurrency

Share by communicating

Concurrent programming is a large topic and there is space only for some Go-specific highlights here.

Concurrent programming in many environments is made difficult by the subtleties required to implement correct access to shared variables. Go encourages a different approach in which shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design. To encourage this way of thinking we have reduced it to a slogan:

Do not communicate by sharing memory; instead, share memory by communicating.

This approach can be taken too far. Reference counts may be best done by putting a mutex around an integer variable, for instance. But as a high-level approach, using channels to control access makes it easier to write clear, correct programs.

One way to think about this model is to consider a typical single-threaded program running on one CPU. It has no need for synchronization primitives. Now run another such instance; it too needs no synchronization. Now let those two communicate; if the communication is the synchronizer, there’s still no need for other synchronization. Unix pipelines, for example, fit this model perfectly. Although Go’s approach to concurrency originates in Hoare’s Communicating Sequential Processes (CSP), it can also be seen as a type-safe generalization of Unix pipes.

  ...


【Golang】Embedding

Embedding Types Within A Struct

A field declared with a type but no explicit field name is called an embedded field.

// Golang program to illustrate the
// concept of inheritance
package main

import (
	"fmt"
)

type Base struct {
	b int
}

func (base *Base) Describe() string {
	return fmt.Sprintf("base %d belongs to us", base.b)
}

type Container struct { // Container is the embedding struct
	Base // Base is the embedded field
	c    string
}

func main() {
	co := Container{}
	co.b = 1
	co.c = "string"

	// Read the fields within embedded fields
	fmt.Printf("co.b: %v - %v\n", co.b, &co.b)
	fmt.Printf("co.Base.b: %v - %v \n", co.Base.b, &co.Base.b)

	// Call the methods of embedded fields
	fmt.Println(co.Describe())
	fmt.Println(co.Base.Describe())
}
  ...


【Golang】源码 - sync - RWMutex

Source Code

A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.

A RWMutex must not be copied after first use.

If a goroutine holds a RWMutex for reading and another goroutine might call Lock, no goroutine should expect to be able to acquire a read lock until the initial read lock is released. In particular, this prohibits recursive read locking. This is to ensure that the lock eventually becomes available; a blocked Lock call excludes new readers from acquiring the lock.

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // semaphore for writers to wait for completing readers
  // 这个 semaphore 在作用是:当ongoing reader 完成了,waiting writer可以被通知到,并且解除blocked,以进行writing
  // 调用 runtime_Semrelease(&rw.writerSem) 可以unblock blocked writer (by wakeup it via semaphore),这时候调用了 runtime_SemacquireMutex(&rw.writerSem) 的地方就不会继续被blocked了
  // 调用 runtime_SemacquireMutex(&rw.writerSem) 的地方会被 blocked,直到 runtime_Semrelease(&rw.writerSem) 被调用
  
	readerSem   uint32 // semaphore for readers to wait for completing writers
  // 这个 semaphore 在作用是:当ongoing writing 完成了,waiting reader可以被通知到,并且解除blocked,以进行reading
  // 调用 runtime_Semrelease(&rw.readerSem) 可以 Unblock blocked readers (by wakeup them via semaphore),这时候调用了 runtime_SemacquireMutex(&rw.readerSem) 的地方就不会继续被blocked了
  // 调用 runtime_SemacquireMutex(&rw.readerSem) 的地方会被 block,直到 runtime_Semrelease(&rw.readerSem) 被调用
	readerCount int32  // number of pending readers
  // if readerCount < 0, means a writing is ongoing (thus all readers have to wait, until the writing is done)
  // if readerCount == 0, means no ongoing writing and no onging reading
  // if readerCount > 0, mean has onging reading, but no ongoing writing
  
	readerWait  int32  // number of departing readers
}

这意味着:

  • 当有writer进来时,
    • 如果这时候没有ongoing reader,writer就直接write
    • 如果这时候有ongoing reader,writer就一直等待(被阻塞在runtime_SemacquireMutex(&rw.writerSem),直到
  • 当有reader进来时,
    • 如果这时候没有ongoing writer,reader就直接read
    • 如果这时候有ongoing writer,reader一直等待(被阻塞在untime_SemacquireMutex(&rw.readerSem)),直到writer完成(writer完成后,会调用runtime_Semrelease(&rw.readerSem)来通知所有reader)
  ...


【Golang】源码 - Channel 实现

Channel 在运行时的内部表示是 runtime.hchan,该结构体中包含了用于保护成员变量的互斥锁,从某种程度上说,Channel 是一个用于同步和通信的有锁队列,使用互斥锁解决程序中可能存在的线程竞争问题是很常见的,我们能很容易地实现有锁队列。

  ...


【Golang】使用 - sync.Mutex

This concept is called mutual exclusion, and the conventional name for the data structure that provides it is mutex.

type Mutex

A Mutex is a mutual exclusion lock(排它锁). The zero value for a Mutex is an unlocked mutex.

A Mutex must not be copied after first use.

type Mutex struct {
    // contains filtered or unexported fields
}
  ...