Linux-X-Go联名专题:管道|(pipe)详解以及由此诞生的CSP思想与Go语言

885 阅读4分钟

Linux-X-Go联名专题:管道|(pipe)详解以及由此诞生的CSP思想与Go语言

最近scitiacoder在使用Linux(*nix)查看一些日志的时候用到了awk命令, 比如awk '{print $1}'打印第一列以及awk 'NR==2{print}'来打印第二行等

这个命令通常会结合管道 | (pipe)来使用, 比如

ps -elf | awk 'NR==2{print}'

那么这个管道 | 的远离究竟是什么呢? 顺着这个思路我们继续讲下去

管道 | (pipe)

管道我们学过操作系统都知道, 是一个单向的, 进程间通讯IPC的机制, 通常以FIFO的缓冲区来实现, 有匿名管道和命名管道等两种实现 --<<现代操作系统 原理与实现>> 陈海波等(强烈推荐这本书)

匿名管道就是我们的这个竖线 | , 比如说process 1 | process 2, 操作系统会为管道分配(一组两个)独立的文件描述符, 使用内存作为缓冲区,左边用来写,右边用来读。如果没有数据写入,输出端(读)的进程会尝试读数据,如果没有进程拥有写端口,就会返回EOF, 否则阻塞在这个系统调用上, 直到数据到来

小知识: 我们知道操作系统会为每个打开的进程维护一个文件描述符表fd, 有三个文件描述符会在进程创建时被默认打开, 那就是stdin, stdout, stderr

wiki

管道的idea是Douglas McIlroy提出的, 之后Ken Thompson大佬一个晚上将其实现了pipe()并移植到了Unix中, 之后快速排序的发明人Tony Hoare根据管道发明了有名的CSP(communicating sequential processes)思想

不要使用共享内存来通信,而是使用通信来共享内存 --CSP

Russ Cox大佬也总结了Tony Hoare CSP Threads这一思想Bell Labs and CSP Threads 如果你是Go语言开发者, 你应该知道Russ Cox是谁..., 于是就有了后来的Go语言...

CSP思想打印素数与Go语言实现

在Russ这篇文章的网上有这么一个有趣的事: csp 大意是用csp思想来打印素数, 并给出了伪代码:

  1. 从左边channel得到一个数为p
  2. 打印p
  3. 循环loop:
  4. 从左边channel再得到一个数为n
  5. 如果n不能整除p( n%p != 0 ): 将n发向右边channel

scientiacoder根据CSP思想用Go语言实现了一版,可以直接运行:

package main

import (
	"fmt"
	"time"
)

// 使用csp的思想来打印素数
func printPrime(leftChan <-chan int) {
	c := make(chan int)
	var p int
	select {
	case p = <-leftChan:
		fmt.Printf("%d ", p)
	}
	go printPrime(c)
	for {
		select {
		case n := <-leftChan:
			if n%p != 0 {
				c <- n
			}
		}
	}
}

func main() {
	c := make(chan int)
	go printPrime(c)
	for i := 2; i < 1000; i++ {
		c <- i
	}

	time.Sleep(1 * time.Second)
}

打印结果:

go run csp/printPrime.go 
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 
137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 
269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 
419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 
571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 
727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 
883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997

本文章首发于scientiacoder io, 更多后端(操作系统,数据库,中间件等)深度知识, golang原理等请访问https scientiacoder io 转载请注明scientiacoder io