shell中pipeline的运行探秘

在上一篇shell的前世今生shell是什么,为什么有shell,它如何工作,怎样认识shellhttps://blog.csdn.net/cleanfield/article/details/127204188?spm=1001.2014.3001.5502

中 写到了pipeline的运行机制,整个pipe队列中每一个命令都是一个子进程,前后之间通过管道连接,今天来具体演示一下。

提示:(cmd&)这样操作的话会直接启后台进程cmd,并且父进程是init

查看pipeline中各子命令的进程编号

 可以看到,这些进程因为(&)的原因,都是以init进程为父进程的

然后lsof查看各自pipe对接情况

1.tail -f和grep timer的对接

 可以看到前者有一个开放write权限的管道,inode是543459506,而后者有一个开放read的pipe,inode也是543459506,正好配对

2.grep timer和grep cmd的对接

 可以看出,前者的write pipe和后者的read的pipe是同样的inode。

这样不管中间连接多少子命令,最后只有一个输入和一个输出是对外的,

bash中的源码是这样的 execute_cmd.c 

  prev = pipe_in;    //最开始的输入端
  cmd = command;

  while (cmd && cmd->type == cm_connection &&
	 cmd->value.Connection && cmd->value.Connection->connector == '|')
    {
      /* Make a pipeline between the two commands. */
      if (pipe (fildes) < 0)       //这里创建pipe
	{
	  sys_error (_("pipe error"));
#if defined (JOB_CONTROL)
	  terminate_current_pipeline ();
	  kill_current_pipeline ();
	  UNBLOCK_CHILD (oset);
#endif /* JOB_CONTROL */
	  last_command_exit_value = EXECUTION_FAILURE;
	  /* The unwind-protects installed below will take care
	     of closing all of the open file descriptors. */
	  throw_to_top_level ();
	  return (EXECUTION_FAILURE);	/* XXX */
	}

      /* Here is a problem: with the new file close-on-exec
	 code, the read end of the pipe (fildes[0]) stays open
	 in the first process, so that process will never get a
	 SIGPIPE.  There is no way to signal the first process
	 that it should close fildes[0] after forking, so it
	 remains open.  No SIGPIPE is ever sent because there
	 is still a file descriptor open for reading connected
	 to the pipe.  We take care of that here.  This passes
	 around a bitmap of file descriptors that must be
	 closed after making a child process in execute_simple_command. */

      /* We need fd_bitmap to be at least as big as fildes[0].
	 If fildes[0] is less than fds_to_close->size, then
	 use fds_to_close->size. */
      new_bitmap_size = (fildes[0] < fds_to_close->size)
				? fds_to_close->size
				: fildes[0] + 8;

      fd_bitmap = new_fd_bitmap (new_bitmap_size);

      /* Now copy the old information into the new bitmap. */
      xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);

      /* And mark the pipe file descriptors to be closed. */
      fd_bitmap->bitmap[fildes[0]] = 1;

      /* In case there are pipe or out-of-processes errors, we
	 want all these file descriptors to be closed when
	 unwind-protects are run, and the storage used for the
	 bitmaps freed up. */
      begin_unwind_frame ("pipe-file-descriptors");
      add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
      add_unwind_protect (close_fd_bitmap, fd_bitmap);
      if (prev >= 0)
	add_unwind_protect (close, prev);
      dummyfd = fildes[1];
      add_unwind_protect (close, dummyfd);

#if defined (JOB_CONTROL)
      add_unwind_protect (restore_signal_mask, &oset);
#endif /* JOB_CONTROL */

      if (ignore_return && cmd->value.Connection->first)
	cmd->value.Connection->first->flags |= CMD_IGNORE_RETURN;
      execute_command_internal (cmd->value.Connection->first, asynchronous,
				prev, fildes[1], fd_bitmap);    //这里创建子进程,输入端是prev,也就是父进程            
                                                //中pipe的读端,会变成下一个子进程的输入端
                                                //filds[1]是pipe的写端,传给子进程做输出

      if (prev >= 0)   
	close (prev);   //当前父进程中的读端,已经由子进程接管,父进程这里关闭

      prev = fildes[0];    //赋值下一个子进程的读端,
      close (fildes[1]);   //关闭当前父进程中的写入端

      dispose_fd_bitmap (fd_bitmap);
      discard_unwind_frame ("pipe-file-descriptors");

      cmd = cmd->value.Connection->second;   //游标指向下一个子命令
    }

上面源码中的中文注释是我对逻辑的理解。

本文只做shell中pipeline的解析,这是我觉得shell中比较绕的地方,特此记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值