Tiny-shell (III): to achieve pipeline processing
Outline
This lesson we explore how to implement pipeline processing tsh, such as cmd1 | cmd2 | cmd3
the processing of this command. I recommend that you read the realization of the pipeline and processing tsh in redirect .
Analysis and realization
For cmd1 | cmd2 | cmd3
such an order, we order |
(pipe) as the separator, calling make_argv
function command entire command is split into an array, i.e. split into cmd1
, cmd2
and cmd3
. As can be seen, for the middle of the cmd2
command that accepts cmd1
input, and output to pass cmd3
, which is the focus of our treatment. So for each |
, we create a pipeline and sub-process, and using pipes to communicate between processes, so that between the input and output of the command will be strung up. For each command (except the last one outside), execute_cmd
we have created a pipeline and a child process, the standard output of each command will be redirected to the standard input of the next command. Its parent process standard output redirected to the pipe, and call the execute_redirect
function executes the command. The child process to redirect standard input from a pipeline, and back to the for loop to create a child process to handle the next command in the pipeline, this is analogous to a process chain. For the last command list, it execute_cmd
does not create a child process or pipeline (because there is no command behind), but direct call execute_cmd
function.
Father and son on the use of the pipeline to input and output communication, I can refer to this blog , which has detailed graphic explanation.
Compile and run
$ gcc -o pipeline tsh1.c execute_redirect.c parse_redirect.c execute_cmd_pipe.c make_argv.c
$ ./pipeline
tsh1>> ls -l | sort -n | grep execute
-rw-rw-r-- 1 chris chris 1365 3月 10 21:22 execute_cmd_pipe.c
-rw-rw-r-- 1 chris chris 1513 3月 10 21:18 execute_redirect.c
-rw-rw-r-- 1 chris chris 448 2月 27 20:27 execute_cmd_simple.c
-rw-rw-r-- 1 chris chris 587 3月 1 22:11 execute_cmd_redirect.c
Code
execute_cmd_pipe.c
/**
* @Filename: execute_cmd_pipe.c
* @Description: 处理流水线的execute_cmd函数,为了直观,省略了错误检查。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void execute_redirect(char *s, int in, int out);
int make_argv(const char *, const char *delimiters, char ***argvp);
void execute_cmd(char *cmds) {
int child;
int count;
int fds[2];
int i;
char **pipelist;
count = make_argv(cmds, "|", &pipelist);
if (count <= 0) {
fprintf(stderr, "Failed to find any commands\n");
exit(1);
}
/* 处理流水线, 比如"cmd1 | cmd 2 | cmd3" */
for (i = 0; i < count - 1; i++) {
pipe(fds);
child = fork();
if (child) {
dup2(fds[1], STDOUT_FILENO);
close(fds[0]);
close(fds[1]);
/* 只有第一条命令才可能有输入重定向 */
execute_redirect(pipelist[i], i == 0, 0);
exit(1);
}
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
close(fds[1]);
}
/**
* 处理最后一条命令, 只有最后一条命令才可能有输出的重定向
* 当只有一条命令时且有输入输出重定向时, 上面的for循环不执行, 且in,out都为1
*/
execute_redirect(pipelist[i], i == 0, 1);
exit(1);
}
execute_redirect.c
/**
* @Filename: execute_redirect.c
* @Description: 对可能带有重定向的单个代码进行处理
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int make_argv(const char *s, const char *delimiters, char ***argvp);
int parse_redirect_in(char *s);
int parse_redirect_out(char *s);
/**
* in为1, 表示需要处理输入重定向
* out为1, 表示需要处理输出重定向
*/
void execute_redirect(char *s, int in, int out) {
char **chargv;
char *pin;
char *pout;
/**
* 实现了对于"sort > f2 < f1"这种小于符号在后的命令处理
* 之前只能处理小于符号在大于符号之前.
*/
if (in && ((pin = strchr(s, '<')) != NULL) &&
out && ((pout = strchr(s, '>')) != NULL) && (pin > pout)) {
/* 先处理输入重定向, 处理完之后就把in置0 */
if (parse_redirect_in(s) == -1) {
perror("Failed to redirect input");
return ;
}
in = 0;
}
/* 一般情况按先处理输出重定向再处理输入重定向 */
if (out && (parse_redirect_out(s) == -1))
perror("Failed to redirect output");
else if (in && (parse_redirect_in(s) == -1))
perror("Failed to redirect input");
else if (make_argv(s, " \t", &chargv) <= 0)
fprintf(stderr, "Failed to parse command line\n");
else {
execvp(chargv[0], chargv);
perror("Failed to execute command");
}
exit(1);
}