출처: https://xiaoxubeii.github.io/articles/linux-io-models-and-go-network-model-2/
Go netpoller의 구현은 간단하므로 Go 프로그램에서는 기동 시에 M(여기에서는 The Go scheduler가 관여하고 있다)을 만들고 감시 기능을 실행한다.
runtime/proc.go:110
func main() { |
netpoll는 sysmon 에서 폴링되고, 기초가 되는 fd를 listen 하고, fd 준비가 완료되면, 폴러는 블럭된 G를 웨이크업 한다(플랫폼에 따라서 인터페이스 구현이 서로 다르다. 여기에서는 Linux epoll 에 대해서만 이야기한다).
runtime/proc.go:4315
func sysmon() { |
Linux 플랫폼에서는 netpoll는 준비가 된 fd를 가지고 있는 epollwait를 호출한다.
runtime/netpoll_epoll.go:61
func netpoll(block bool) *g { |
Go epoll 작성과 등록
Go epoll는 어떻게 fd를 만들고, listen 하고 있을까?
Go epoll 중의 실제 콜 스텝은 같다. 단순한 TCP 접속을 예로 하면 클라이언트 코드는 아래와 같다.
func main() { |
net.Dial은 최종적으로 net/dial.go의 DialContext를 호출한다.
net/dial.go:341 |
이상의 일련의 호출 후, fd.dial 까지 코드를 실행하고, 마지막으로 pollDesc.init를 호출하여 epoll 초기화와 등록을 한다.
internal/poll/fd_poll_runtime.go:35
func (pd *pollDesc) init(fd *FD) error { |
go:linkname을 사용하여 링크된 runtime_pollServerInit 와 runtime_pollOpen 함수가 실제로 호출되는 것에 주의하자.
runtime/netpoll.go:85
//go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit |
처음의 poll_runtime_pollServerInit 를 보자. Linux 플랫폼에서는 epollcreate 또는 epollcreate1 을 호출하는 netpollinit 에 의해 epoll을 초기화 한다.
runtime/netpoll_epoll.go:25
func netpollinit() { |
poll_runtime_pollOpen이 netpollopen을 경유하여 epollctl 등록 리스닝 fd를 호출하고 있는 사이에 poll_runtime_pollOpen은 epollctl 등록 리스닝 fd를 호출한다.
runtime/netpoll_epoll.go:43
func netpollopen(fd uintptr, pd *pollDesc) int32 { |
그리고 처음에 이야기한 fd 준비를 위해 epollwait에 호출해서 전체 프로세스는 종료한다. 이 프로세스 전체는 실제로는 Linux epoll의 콜 스텝에 정확하게 대응한다.