各种评论:
while (strcmp(fgets(buf, 80, stdin), "bye\n")!=0)
如果 shell 被赋予 EOF 而不是bye
. 不要像这样组合两个函数调用。如果您想在一个循环条件下完成所有操作,请使用:
while (fgets(buf, sizeof(buf), stdin) != 0 && strcmp(buf, "bye\n") != 0)
我们将在另一次讨论命令行长度的限制。
由于您没有提供makeargv()
查看,我们必须假设它可以正常工作。
您将事物拆分为命令和管道的循环是:
pnum = 0;
ploc[0] = 0;
if (j > 16) j = 16;
for (i = 0; i < j; i++)
{
if (strcmp(argp[i], "|") == 0)
{
argp[i] = NULL;
ploc[pnum+1] = (i+1);
pnum++;
}
}
假设我们有一个命令行输入:ls -l | grep lemon
. 看来您makeargv()
将返回 5 并设置argp
如下:
argp[0] = "ls";
argp[1] = "-l";
argp[2] = "|";
argp[3] = "grep";
argp[4] = "lemon";
argp[5] = 0; // Inferred - things will crash sooner or later if wrong
你的这个循环会给你:
ploc[0] = 0;
ploc[1] = 3;
pnum = 1;
您的代码在数组中有一对文件描述符fdleft
,但您从不初始化数组(pipe()
例如 call ),即使您在调用dup2()
.
然后主for
循环必须运行两次,每个命令运行一次。对于管道中三个或更多命令的一般情况(who | grep me | sort
例如 ),您的第一个命令(who
)需要其标准输入保持不变,但其标准输出将进入连接who
和的管道grep
。“中间”命令(倒数第二个命令,或grep me
在示例中)每个都需要其标准输入来自前一个管道,并且需要为其标准输出创建一个新管道。最后一个命令(在本例中为第三个命令sort
)需要其标准输入来自最后一个管道,并且其标准输出保持不变。
您的代码不会那样做,也不会靠近。
当您使用pipe()
and then dup()
ordup2()
将任何描述符映射到标准 I/O 描述符时,您需要关闭管道的两端。你也没有足够的close()
电话。
您的父进程必须依次启动每个子进程,并且仅在启动所有子进程后等待它们退出。有不同的方法来组织过程。父母可以分叉一次;孩子可能负责启动管道中的主要命令,最后执行最后一个命令本身。父母只有一个直系子女(其他是孙子女),所以它只需要等待一个命令完成。另一种方法是父进程知道管道中的每个进程,并等待它们全部完成。
如果您的父进程在启动其余命令之前等待管道中的每个命令完成,则最终可能会出现死锁。一个孩子向它的管道写入了太多数据,以至于它被内核阻塞,直到某个进程从管道读取,但是从管道读取的进程尚未启动,父进程正在等待子进程退出。您也失去了多处理和并发的好处。
在您的“案例 0”中,您有一个无关的分号。我的编译器警告过我。如果你的没有警告你,你需要使用更多的编译警告或获得更好的编译器。
case 0: // child
if (i != pnum)
{
dup2(fdright[1], 1);
}
if (i != 0); // Unwanted semi-colon!
{
dup2(fdright[0], 0);
}
关于 SO 上的 mini-shell 中的管道有很多问题,包括:
13636252 的答案几乎是通用的。唯一的障碍是它使用char ***
容易混淆,而且它写得如此紧凑,它具有相互递归的功能,重复最少。OTOH,它工作正常,你的makeargv()
函数也使用了一个char ***
参数。
重新编写的代码
这是您的代码重做,以便它工作。它包括err_sys()
和的实现makeargv()
。我makeargv()
只是假设命令行中的单词少于 32 个。无论如何,它都不是一个强大的命令行解析器。它确实允许您输入ls | wc
并给出正确答案;它还允许who | grep me | sort
并给出正确的答案;它还允许ls
并给出正确的答案。不过,管道符号周围的空格是至关重要的(在普通的 shell 中,它们是可选的,因此who|grep me|sort
也应该可以工作,但它不适用于此代码。
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
extern int makeargv(char *line, char *seps, char ***args);
extern void err_sys(const char *msg);
static void dump_argv(char **argv);
static void dump_fds(void);
int main(void)
{
char buf[80];
printf(" <(^_^)> \nHello \n I am your console and I am here to help you\n");
printf(" If you don't need me anymore just say \"bye\"\n");
fflush(stdout);
dump_fds();
printf("(>^_^)> ");
while (fgets(buf, sizeof(buf), stdin) != 0 && strcmp(buf, "bye\n") != 0)
{
pid_t pid;
char **argp;
int fdleft[2] = { -1, -1 };
int fdright[2] = { -1, -1 };
int pnum = 0;
int ploc[16];
int j = makeargv(buf, " \n", &argp);
ploc[0] = 0;
if (j > 16)
j = 16;
for (int i = 0; i < j; i++)
{
if (strcmp(argp[i], "|") == 0)
{
argp[i] = NULL;
ploc[++pnum] = i+1;
}
}
printf("pnum = %d\n", pnum);
for (int k = 0; k < pnum+1; k++)
printf("ploc[%d] = %d\n", k, ploc[k]);
for (int i = 0; i < pnum+1; i++)
{
if (i != pnum)
{
if (pnum > 0)
{
if (pipe(fdright) != 0)
err_sys("pipe");
//printf("%d: fdright = { %d, %d }\n", i, fdright[0], fdright[1]);
//dump_fds();
}
}
if ((pid = fork()) < 0)
err_sys("fork failed");
else if (pid == 0)
{
/* Child */
int targ;
//dump_fds();
if (i != pnum)
{
dup2(fdright[1], 1);
close(fdright[0]);
close(fdright[1]);
}
if (i != 0)
{
dup2(fdleft[0], 0);
close(fdleft[0]);
close(fdleft[1]);
}
targ = ploc[i];
dump_argv(&argp[targ]);
dump_fds();
execvp(argp[targ], &argp[targ]);
fprintf(stderr, "(-_-) I'm sorry the exec failed\n");
exit(1);
}
if (i != 0)
{
//dump_fds();
//printf("%d: fdleft = { %d, %d }\n", i, fdleft[0], fdleft[1]);
assert(fdleft[0] != -1 && fdleft[1] != -1);
close(fdleft[0]);
close(fdleft[1]);
//dump_fds();
}
printf("PID %d launched\n", pid);
fdleft[0] = fdright[0];
fdleft[1] = fdright[1];
}
//dump_fds();
//printf("%d: fdleft = { %d, %d }\n", -1, fdleft[0], fdleft[1]);
close(fdleft[0]);
close(fdleft[1]);
free(argp);
//dump_fds();
int corpse;
int status;
while ((corpse = waitpid(0, &status, 0)) > 0)
printf(":-( PID %d status 0x%.4X\n", corpse, status);
printf("\n(>^_^)> ");
}
printf(" v(^o^)^ BYE BYE!\n");
}
static void dump_argv(char **argv)
{
int n = 0;
char **args;
args = argv;
while (*args++ != 0)
n++;
fprintf(stderr, "%d: %d args\n", getpid(), n);
args = argv;
while (*args != 0)
fprintf(stderr, "[%s]\n", *args++);
fprintf(stderr, "EOA\n");
}
/* Report on open file descriptors (0..19) in process */
static void dump_fds(void)
{
struct stat b;
char buffer[32];
sprintf(buffer, "%d: ", getpid());
char *str = buffer + strlen(buffer);
for (int i = 0; i < 20; i++)
*str++ = (fstat(i, &b) == 0) ? 'o' : '-';
*str++ = '\n';
*str = '\0';
fputs(buffer, stderr);
}
int makeargv(char *line, char *seps, char ***args)
{
enum { MAX_ARGS = 32 };
char **argv = malloc(32 * sizeof(char *)); // Lazy!
if (argv == 0)
err_sys("out of memory in makeargv()");
int n;
char **argp = argv;
char *str = line;
for (n = 0; n < MAX_ARGS - 1; n++)
{
str += strspn(str, seps);
if (*str == '\0')
break;
*argp++ = str;
int len = strcspn(str, seps);
if (len == 0)
break;
str[len] = '\0';
str += len + 1;
}
*argp = 0;
dump_argv(argv);
*args = argv;
return(n);
}
void err_sys(const char *msg)
{
int errnum = errno;
char *errmsg = strerror(errnum);
fprintf(stderr, "%s (%d: %s)\n", msg, errnum, errmsg);
exit(1);
}
样本输出:
$ ./pipes-15673333
<(^_^)>
Hello
I am your console and I am here to help you
If you don't need me anymore just say "bye"
29191: ooo-----------------
(>^_^)> who | grep jl | sort
29191: 6 args
[who]
[|]
[grep]
[jl]
[|]
[sort]
EOA
pnum = 2
ploc[0] = 0
ploc[1] = 2
ploc[2] = 5
PID 29194 launched
PID 29195 launched
29194: 1 args
[who]
EOA
PID 29196 launched
29194: ooo-----------------
29195: 2 args
[grep]
[jl]
EOA
29195: ooo-----------------
29196: 1 args
[sort]
EOA
29196: ooo-----------------
:-( PID 29194 status 0x0000
jleffler console Mar 27 15:11
jleffler ttys000 Mar 27 16:26
jleffler ttys001 Mar 27 16:26
jleffler ttys002 Mar 27 16:26
jleffler ttys003 Mar 27 16:26
jleffler ttys004 Mar 27 16:26
jleffler ttys005 Mar 27 16:26
:-( PID 29195 status 0x0000
:-( PID 29196 status 0x0000
(>^_^)> ls
29191: 1 args
[ls]
EOA
pnum = 0
ploc[0] = 0
PID 29197 launched
29197: 1 args
[ls]
EOA
29197: ooo-----------------
bash.getopts.update makefile pipeline.c pthread-1.c shuntzeroes.c timezeromoves.c
cmpfltint.c mda.c pipeline.dSYM pthread-2.c so.14304827 uint128.c
const-stuff.c mq-saurabh pipes-13905948.c pthread-3.c so.367309 uname.c
dupdata.sql mqp-saurabh pipes-14312939.c quine.c so.6964747 unwrap.c
fifocircle.c multi-pipe-sort.c pipes-15673333 ranges.sql so.6965001 xxx.sql
idsdb00246324.ec multiopts.sh pipes-15673333.c recv.c so.8854855.sql yyy.sql
incunabulum.c nextpipe.c pipes-15673333.dSYM regress.c strandsort.c
madump.c pipeline powa.c send.c streplace.c
:-( PID 29197 status 0x0000
(>^_^)> ls -C | wc
29191: 4 args
[ls]
[-C]
[|]
[wc]
EOA
pnum = 1
ploc[0] = 0
ploc[1] = 3
PID 29200 launched
PID 29201 launched
29200: 2 args
29201: 1 args
[ls]
[wc]
[-C]
EOA
EOA
29201: ooo-----------------
29200: ooo-----------------
:-( PID 29200 status 0x0000
16 46 581
:-( PID 29201 status 0x0000
(>^_^)> bye
v(^o^)^ BYE BYE!
$