中 写到了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中比较绕的地方,特此记录。