ソースコード、レベル1、シェル
最良のドキュメントはソースコードであるため、ここで説明します。 BashはYaccを使用して入力コマンドを解析し、文字「|」を検出すると「command_connect()」を返します。
parse.y :
1242 pipeline: pipeline '|' newline_list pipeline 1243 { $$ = command_connect ($1, $4, '|'); } 1244 | pipeline BAR_AND newline_list pipeline 1245 { 1246 1247 COMMAND *tc; 1248 REDIRECTEE rd, sd; 1249 REDIRECT *r; 1250 1251 tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1; 1252 sd.dest = 2; 1253 rd.dest = 1; 1254 r = make_redirection (sd, r_duplicating_output, rd, 0); 1255 if (tc->redirects) 1256 { 1257 register REDIRECT *t; 1258 for (t = tc->redirects; t->next; t = t->next) 1259 ; 1260 t->next = r; 1261 } 1262 else 1263 tc->redirects = r; 1264 1265 $$ = command_connect ($1, $4, '|'); 1266 } 1267 | command 1268 { $$ = $1; } 1269 ;
また、ここでは文字ペア「|&」の処理を確認します。これは、stdoutとstderrの両方をパイプラインにリダイレクトすることと同等です。 次に、command_connect()を参照してください:
make_cmd.c :
194 COMMAND * 195 command_connect (com1, com2, connector) 196 COMMAND *com1, *com2; 197 int connector; 198 { 199 CONNECTION *temp; 200 201 temp = (CONNECTION *)xmalloc (sizeof (CONNECTION)); 202 temp->connector = connector; 203 temp->first = com1; 204 temp->second = com2; 205 return (make_command (cm_connection, (SIMPLE_COM *)temp)); 206 }
ここで、コネクタは文字「|」です intのような。 コマンドシーケンス(「&」、「|」、「;」などを介してリンクされている)を実行すると、execute_connection()が呼び出されます:
execute_cmd.c :
2325 case '|': ... 2331 exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
PIPE_INおよびPIPE_OUTは、入力および出力ストリームに関する情報を含むファイル記述子です。 NO_PIPEの値を取ることができます。つまり、I / Oはstdin / stdoutです。
execute_pipeline()はかなり膨大な関数であり、その実装は
execute_cmd.cに含まれています。 私たちにとって最も興味深い部分を検討します。
execute_cmd.c :
2112 prev = pipe_in; 2113 cmd = command; 2114 2115 while (cmd && cmd->type == cm_connection && 2116 cmd->value.Connection && cmd->value.Connection->connector == '|') 2117 { 2118 2119 if (pipe (fildes) < 0) 2120 { } ....... 2178 execute_command_internal (cmd->value.Connection->first, asynchronous, 2179 prev, fildes[1], fd_bitmap); 2180 2181 if (prev >= 0) 2182 close (prev); 2183 2184 prev = fildes[0]; 2185 close (fildes[1]); ....... 2190 cmd = cmd->value.Connection->second; 2191 }
したがって、bashは、検出した各「|」文字に対してpipe()システムコールを呼び出すことにより、パイプラインシンボルを処理します。 適切なファイル記述子を入力および出力ストリームとして使用して、各コマンドを個別のプロセスで実行します。
ソースコード、レベル2、カーネル
カーネルコードに戻り、pipe()関数の実装を確認します。 この記事では、カーネルバージョン3.10.10安定版について説明します。
fs / pipe.c (この記事のコードスニペットがありません):
35 unsigned int pipe_max_size = 1048576; 40 unsigned int pipe_min_size = PAGE_SIZE; 869 int create_pipe_files(struct file **res, int flags) 870 { 871 int err; 872 struct inode *inode = get_pipe_inode(); 873 struct file *f; 874 struct path path; 875 static struct qstr name = {. name = “” }; 881 path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name); 889 f = alloc_file(&path, FMODE_WRITE, &pipefifo_fops); 893 f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT)); 896 res[0] = alloc_file(&path, FMODE_READ, &pipefifo_fops); 902 res[0]->f_flags = O_RDONLY | (flags & O_NONBLOCK); 903 res[1] = f; 904 return 0; 917 } 918 919 static int __do_pipe_flags(int *fd, struct file **files, int flags) 920 { 921 int error; 922 int fdw, fdr; 927 error = create_pipe_files(files, flags); 931 fdr = get_unused_fd_flags(flags); 936 fdw = get_unused_fd_flags(flags); 941 audit_fd_pair(fdr, fdw); 942 fd[0] = fdr; 943 fd[1] = fdw; 944 return 0; 952 } 969 SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags) 970 { 971 struct file *files[2]; 972 int fd[2]; 975 __do_pipe_flags(fd, files, flags); 977 copy_to_user(fildes, fd, sizeof(fd)); 984 fd_install(fd[0], files[0]); 985 fd_install(fd[1], files[1]); 989 } 991 SYSCALL_DEFINE1(pipe, int __user *, fildes) 992 { 993 return sys_pipe2(fildes, 0); 994 }
気付いた場合、コードはO_NONBLOCKフラグをチェックしています。 fcntlのF_SETFL操作を使用して設定できます。 彼は、パイプラインのI / Oフローをブロックせずにモードに切り替える責任があります。 このモードでは、ロックする代わりに、ストリームの読み取り/書き込みプロセスはerrnoコードEAGAINで終了します。
パイプラインに書き込まれるデータブロックの最大サイズは、armのアーキテクチャのメモリの1ページ(4K)です。
arch / arm / include / asm / limits.h :
8 #define PIPE_BUF PAGE_SIZE
kernels> = 2.6.35の場合、パイプラインバッファーのサイズを変更できます。
fcntl(fd, F_SETPIPE_SZ, <size>)
上で見たように、最大許容バッファサイズは、ファイル/ proc / sys / fs / pipe-max-sizeで指定されます。