golang-操作系统-同步&管道&匿名管道&内存管道

十、同步

1. 计数器案例,同一个程序开俩个 进程A和进程B。并发执行 把数据库中符合条件的记录迁移到磁盘。用计数器记录 当前被处理的最大行号。
   	1. 第一步,读取计数器的值C
   	2. 第二步,从数据库中读取 C+10000 行数据。
   	3. 第三步, 遍历符合条件的数据,并形成新的数据集合
   	4. 第四步,将新的数据集合 存储到哦指定目录文件中。
   	5. 第五步,把计数器值+10000
   	6. 检查数据是否全部处理完成。是则退出,否则从第一步开始。
2. 原子操作,把执行过程中不能被中断的操作 为原子操作。
3. 临界区,把只能串行化访问或执行的资源或代码为 临界区。所有的系统调用都属于原子操作。
4. 原子操作和 临界区 是不同的概念,临界区 要求 一个访问者在临界区时,其他访问者不饿能被放进来。原子操作是不能被中断。原子操作需要芯片级别的支持,当今的cpu都提供了对原子的操作。

十一、管道

  1. 管道是一种半双工的通讯方式,它只能被用于父进程与子进程,以及同祖先的子进程之间的通讯。

  2. shell命令 用到管道,ps aux | grep go

  3. shell 为每个命令创建一个进程,然后把左边的命令 标准输出管道 与右边的命令的 标准输入 连接起来。

  4. golang通过exec.command 执行命令+exec.command().StdoutPipe()以管道形式输出 到缓冲区bytes.Buffer

    	//1.输入定义系统命令
    	cmd0 := exec.Command("echo", "-n", "my first command from golang.")
    	//2.定义命令执行标准输出管道
    	stdout0, _ := cmd0.StdoutPipe()
    	//3.启动执行系统命令
    	if err := cmd0.Start(); err != nil {
    		log.Fatalf("command err:%s", err.Error())
    	}
    	
    	var outputBuf bytes.Buffer
    	for {
    		output0 := make([]byte, 5)
    		//4.通过标准输出管道 读出数据  打印
    		n, err := stdout0.Read(output0)
    		if err != nil {
    			if err == io.EOF {
    				break
    			} else {
    				log.Fatalf("can not read data from pipe:%s", err.Error())
    			}
    		}
    		//5.当n< 5时表示数据读取完毕,当n>=5表示数据没有完全读完。需要for循环读取,直到err == io.EOF
    		if n > 0 {
    			outputBuf.Write(output0[:n])
    		}
    	}	
    	log.Printf("data from pipe:%s\n", output0[:n])
    
  5. golang通过exec.command 执行命令+exec.command().StdoutPipe()以管道形式输出 +通过带缓冲区的读取器输出

    //1.输入定义系统命令
       	cmd0 := exec.Command("echo", "-n", "my first command from golang.")
       	//2.定义命令执行标准输出管道
       	stdout0, _ := cmd0.StdoutPipe()
       	//3.启动执行系统命令
       	if err := cmd0.Start(); err != nil {
       		log.Fatalf("command err:%s", err.Error())
       	}
       	//4.定义一个缓冲读取器,
       	outputBuf := bufio.NewReader(stdout0)
       	//5.第二个参数表示该行 是否被读完
       	output0, _, err := outputBuf.ReadLine()
       	if err != nil {
       		log.Fatalf("can not read data from pipe:%s", err.Error())
       	}
    	log.Printf("data from pipe:%s\n", string(output0))
    
  6. golang通过stdout1 := exec.Command().StdoutPipe()以管道形式输出 + 通过 bufio.NewReader(stdout1).WriteTo(stdout2) 读取,并写入第二个管道stdout2:= exec.Command().StdoutPipe()

    //1.执行俩个命令
    	cmd1 := exec.Command("ps", "aux")
    	cmd2 := exec.Command("grep", "nginx")
    	//2.第一个命令以输出管道
    	stdout1, err := cmd1.StdoutPipe()
    	if err != nil {
    		log.Fatalf("stdoutpipe1 err:%v", err)
    	}
    	//3.启动第一个命令的执行
    	if err := cmd1.Start(); err != nil {
    		log.Fatalf("cmd1 start err:%v", err)
    	}
    	//3.第一个管道内容传递给缓冲区1
    	outputBuf1 := bufio.NewReader(stdout1)
    	//4.定义第二个命令的输入管道
    	stdin2, err := cmd2.StdinPipe()
    	if err != nil {
    		log.Fatalf("stdin2 err:%v", err)
    	}
    	//5.把第一个缓冲区1 写入第二个输入管道2
    	_, err = outputBuf1.WriteTo(stdin2)
    	if err != nil {
    		log.Fatalf("outputBuf1 write to stdin2 err:%v", err)
    	}
    	//6.定义第二个命令标准输出缓冲区2
    	var outputBuf2 bytes.Buffer
    	cmd2.Stdout = &outputBuf2
    	if err := cmd2.Start(); err != nil {
    		log.Fatalf("cmd2 start err:%v", err)
    	}
    	//7.关闭第二个输入管道
    	if err := stdin2.Close(); err != nil {
    		log.Fatalf("cmd2 close err:%v", err)
    	}
    	//8.等待所有运行结束
    	if err := cmd2.Wait(); err != nil {
    		log.Fatalf("cmd2 wait err:%v", err)
    	}
    	log.Printf("outputBuf2 :%s", outputBuf2.String())
    	/**
    	结果:
    	2022/09/17 20:27:11 outputBuf2 :
    	root       1084  0.0  0.0  46504  1208 ?        Ss   19:50   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
    	nginx      1086  0.0  0.0  49044  2052 ?        S    19:50   0:00 nginx: worker process
    	nginx      1091  0.0  0.0  49044  2052 ?        S    19:50   0:00 nginx: worker process
    	nginx      1094  0.0  0.0  49044  2052 ?        S    19:50   0:00 nginx: worker process
    	nginx      1096  0.0  0.0  49044  2052 ?        S    19:50   0:00 nginx: worker process
    
    	*/
    
  7. 以上是匿名管道,相对应的是命名管道,任何程序都可以通过命名管道交换数据。命名管道以文件形式存在于文件系统中

  8. //命名管道默认是阻塞的,单向的。
    //1.创建命名管道myfifo1
    mkfifo -m 644 myfifo1
    //2.阻塞等待 myfifo1 命名管道数据到来。tee从标准输入设备读取数据,将内容输出到标准输出设备,并写入文件。
    tee dst.log < myfifo1 &
    //3. 把dst.log内容写入管道myfifo1
    cat dst.log > myfifo1
    
    1. 注意,命名管道 默认会 将其中一端 还未就绪的时候阻塞 另一端的进程
    //注意,命名管道 默认会 将其中一端 还未就绪的时候阻塞 另一端的进程。
    	//1. os.pipe() 输出俩个结构,输入端;输出端 *os.File
    	reader, writer, _ := os.Pipe()
    	var wg sync.WaitGroup
    	wg.Add(1)
    	go func(file *os.File) {
    		defer file.Close()
    		defer wg.Done()
    		if _, err := file.Write([]byte("i love you!")); err != nil {
    			log.Fatalf("write pipe err:%v", err)
    		}
    		log.Printf("writer pipe succ.\n")
    	}(writer)
    	wg.Add(1)
    	go func(file *os.File) {
    		defer file.Close()
    		defer wg.Done()
    		output := make([]byte, 512)
    		n, err := file.Read(output)
    		if err != nil {
    			log.Fatalf("reader pipe err:%v", err)
    		}
    		log.Printf("reader pipe:%s", string(output[:n]))
    	}(reader)
    	wg.Wait()
    	/**
    		结果:
    	2022/09/17 20:54:17 writer pipe succ.
    	2022/09/17 20:54:17 reader pipe:i love you!
    
    	*/
    
    1. 匿名管道会在管道缓冲区被写满后 使写数据的 进程被阻塞。命名管道会在一段就绪前 阻塞 另一端的进程。

    2. 当有多个输入端同时写入数据的时候得考虑原子性,操作系统提供的管道使不提供原子操作支持的。

    3. 内存管道 go 提供的io.Pipe()

    4. go channel 和 pipeline区别

      不同 channel pipe
      结构 stream of a go type stream of bytes
      平台 specific type in golang unix like
      缓冲区 depend on circumstances always buffered
      特点 connect between goroutine between two process(std.in/out)

你可能感兴趣的:(操作系统,golang,linux,开发语言)