西维蜀黍

【Golang】设计模式 - Generator

Generator Pattern

Channels are first-class values, just like strings or integers.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func boring(msg string) <-chan string { // Returns receive-only channel of strings.
	c := make(chan string)
	go func() { // We launch the goroutine from inside the function.
		for i := 0; ; i++ {
			c <- fmt.Sprintf("%s %d", msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c // Return the channel to the caller.
}

func main() {
	c := boring("boring!") // Function returning a channel.
	for i := 0; i < 5; i++ {
		fmt.Printf("You say: %q\n", <-c)
	}
	fmt.Println("You're boring; I'm leaving.")
}
  ...


【Performance】Concurrency vs Parallelism

Concurrency is not parallelism, although it enables parallelism.

If you have only one processor, your program can still be concurrent but it cannot be parallel.

On the other hand, a well-written concurrent program might run efficiently in parallel on a multiprocessor. That property could be important…

  ...


【Golang】使用 epoll

  ...


【Golang】源码 - netpoll

Go netpoll

Go netpoll 通过在底层对 epoll/kqueue/iocp 的封装,从而实现了使用同步编程模式达到异步执行的效果。总结来说,所有的网络操作都以网络描述符 netFD 为中心实现。netFD 与底层 PollDesc 结构绑定,当在一个 netFD 上读写遇到 EAGAIN 错误时,就将当前 goroutine 存储到这个 netFD 对应的 PollDesc 中,同时调用 gopark 把当前 goroutine 给 park 住,直到这个 netFD 上再次发生读写事件,才将此 goroutine 给 ready 激活重新运行。显然,在底层通知 goroutine 再次发生读写等事件的方式就是 epoll/kqueue/iocp 等事件驱动机制。

net.Listen("tcp", ":8888") 方法返回了一个 TCPListener,它是一个实现了 net.Listener 接口的 struct。

通过 listener.Accept() 接收的新连接 TCPConn 则是一个实现了 net.Conn 接口的 struct,它内嵌了 net.conn struct。

  • 不管是 Listener 的 Accept 还是 Conn 的 Read/Write 方法,都是基于一个 netFD 的数据结构的操作
  • netFD 是一个网络描述符,类似于 Linux 的文件描述符的概念,netFD 中包含一个 poll.FD 数据结构
  • 而 poll.FD 中包含两个重要的数据结构 Sysfd 和 pollDesc,前者是真正的系统文件描述符,后者对是底层事件驱动的封装,所有的读写超时等操作都是通过调用后者的对应方法实现的
  ...


【Operating System】memory

User Space And Kernel Space

现在操作系统都是采用虚拟存储器,那么对 32 位操作系统而言,它的寻址空间(虚拟存储空间)为 4G(2 的 32 次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对 Linux 操作系统而言,将最高的 1G 字节(从虚拟地址 0xC0000000 到 0xFFFFFFFF),供内核使用,称为内核空间,而将较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF),供各个进程使用,称为用户空间。

  ...


【Linux】I/O 轮询技术 - poll

poll

pollselect的基础之上进行改进,并避免不需要的检查。但是,当 fd 较多的时候,它的性能还是十分低下的。通过poll 实现的轮询与select相似,但性能限制有所改善。

  ...


【Linux】I/O 轮询技术 - select

select

select是基于I/O多路复用模型。select 可以让内核在"多个 fd 对应的I/O操作中任何一个“就绪"(指数据已经被拷贝到kernel space)或"经过指定时间后",唤醒(wake)并通知用户线程(在唤醒之前,用户线程因为被阻塞而处于sleep状态)。比如:

  • 当1、4或5中任何一个 fd 的状态为可读时
  • 或当4、7中任何一个 fd 的状态为可写时
  • 或当6、8中任何一个 fd 的处理过程中抛出异常时
  • 或经过10.2秒后
  ...


【Linux】I/O 轮询技术 - read

read 方法 - 同步非阻塞I/O模型

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

On files that support seeking, the read operation commences at the file offset, and the file offset is incremented by the number of bytes read. If the file offset is at or past the end of file, no bytes are read, and read() returns zero.

If count is zero, read() may detect the errors described below. In the absence of any errors, or if read() does not check for errors, a read() with a count of 0 returns zero and has no other effects.

  ...


【Linux】I/O 轮询技术 - epoll

Background

由于在selectpoll中,每一次检查监听的 fd 状态变化时,都需要遍历一次用户传入的 fd 集合,因而如果当 fd 的数量很多时,一次集合遍历将会非常耗时。

为了解决这个问题,在selectpoll的基础上进行优化,在Linux kernel 2.6中,引入了event poll(epoll)方法。

epoll将 fd 的监测声明和真正的监测进行了分离。

从实现的角度来说,epoll会创建一个epoll instance ,这个epoll instance 对应一个 fd 。同时,这个epoll instance 中包含一个 fd 集合(称为epoll setinterest list),集合中存储了所有希望被监听的 fd 。当某个 fd 对应的I/O操作完成时,这个 fd 会被放入ready listready listepoll set的子集。

该方案是Linux下效率最高的I/O事件通知机制。本质在于epoll基于内核层面的事件通知。具体来说,epoll不是在用户线程发起轮询调用后,不断地去检查所有监听的 fd 是否状态发生变化(pollselect是这样做的),而是在当这些 fd 中任何一个发生状态变化时,内核就将这些对应的 fd 移动到epoll实例中的ready list中(这里发生了一次内核事件通知)。因此,当用户线程调用轮询方法epoll_wait时,内核不再需要进行一次 fd 的状态变化检查的遍历,而是直接返回结果。

下图为通过epoll方式实现轮询的示意图:

epoll

epoll stands for event poll and is a Linux kernel system call for a scalable I/O event notification mechanism, first introduced in version 2.5.44 of the Linux kernel.

Its function is to monitor multiple file descriptors to see whether I/O is possible on any of them. It is meant to replace the older POSIX select(2) and poll(2) system calls, to achieve better performance in more demanding applications, where the number of watched file descriptors is large (unlike the older system calls, which operate in O(n) time, epoll operates in O(1) time.

  ...


【hugo】Add Last Edited Date to Post

The Manual Way

The simplest way is to record a lastmod date and time within your post’s header - like the example below:

---
title: My Example Post
date: 1990-01-01T00:00:00+00:00
lastmod: 1995-04-04T00:00:00+00:00
url: /example-post/
---

We can then use this information in our theme and layouts to display the information. In our code, we don’t show the last updated date if it’s the same as the created date (there’s no point?)

  ...