gpt4 book ai didi

c - 用C制作 shell 时正确的管道连接方法是什么

转载 作者:太空狗 更新时间:2023-10-29 15:36:12 24 4
gpt4 key购买 nike

我正在尝试创建自己的shell,我相信我已经正确地完成了分叉,但我不知道如何正确地管道。任何帮助或提示将不胜感激。
基本上我的管道不工作,我花了很多时间试图找出如何让它们在进程之间正确地传输数据。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h" // from Steven's book Advanced programing in the UNIX Enviroment


extern int makeargv(char *, char * , char ***);
int main()
{ char **argp;
int i,j,vpret;
char buf[80];
pid_t pid;
int pnum;
//pipe number
int ploc[16];
//pipe location
//(this has a max of 12 possible pipes)
int targ;
int fdleft[2], fdright[2];




printf(" <(^_^)> \nHello \n I am your console and I am here to help you \n");
printf(" If you dont need me anymore just say \"bye\" ");
fflush(stdout);
write(1,"\n(>^_^)> ",8);
while(strcmp(fgets(buf, 80, stdin), "bye\n")!=0)
{
j=makeargv(buf," \n",&argp); //this breaks the line up and returns the number of commands
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++;
}

}

for (i = 0; i < (pnum+1); i++)
{
pipe(fdright);
if (i != 0)
{
dup2(fdright[1], fdleft[0]);
}
pid = fork();


switch (pid)
{
case -1:
err_sys("fork failed");
break;
case 0: // child
if (i != pnum)
{
dup2(fdright[1],1);
}
if ( i != 0);
{
dup2(fdright[0],0);
}
//printf("(^o^) running pipe[%i]\n" , i);
targ =(ploc[i]) ;
execvp(argp[targ],&argp[targ]);
write(1,"(-_-) I'm sorry the exec failed \n",33);
exit(1);
default:

dup2(fdleft[1], fdright[1]);
waitpid(pid,NULL, 0);
close(fdright[0]);
close(fdright[1]);
//waitpid(pid,NULL, 0);
}
}
//waitpid(pid, NULL, 0);
write(1,"\n(>^_^)> ",8);
}
printf(" v(^o^)^ BYE BYE!\n");

}

谢谢

最佳答案

各种评论:
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()),即使您在调用 dup2()时使用它。
然后main for循环必须运行两次,每个命令一次。对于管道中的三个或更多命令的一般情况(例如, who | grep me | sort),第一个命令( who)需要其标准输入不受影响,但其标准输出将转到连接 whogrep的管道。“middle”命令(第二个命令是倒数第二个命令,在示例中是 grep me)每个命令都需要其标准输入来自上一个管道,并且需要为其标准输出创建一个新管道。最后一个命令(在本例中,第三个命令 sort)需要其标准输入来自最后一个管道,并且其标准输出保持不变。
你的代码不会这么做,或者接近。
当使用 pipe()然后 dup()dup2()将任何描述符映射到标准I/O描述符时,需要关闭管道的两端。你也没有足够的电话。
您的父进程必须依次启动每个孩子,并且只等待它们在启动它们之后退出。有不同的方法来组织流程。父级可以分叉一次;子级可以负责启动管道中的前导命令,最后执行最后一个命令本身。父对象只有一个直接子对象(其他子对象是孙子),因此只需等待一个命令完成。另一种选择是,父进程知道管道中的每个进程,并等待它们全部完成。
如果父进程在启动其余进程之前等待管道中的每个命令完成,则可能会以死锁的形式结束。一个孩子将许多数据写入管道,直到内核从程序中读取数据,直到进程从管道读取,但是从管道读取的进程尚未启动,父进程正在等待孩子退出。您还失去了多处理和并发的好处。
在“案例0”中,您有一个无关的分号。我的编译器警告过我。如果你没有警告你,你需要使用更多的编译警告或得到一个更好的编译器。
case 0: // child
if (i != pnum)
{
dup2(fdright[1], 1);
}
if (i != 0); // Unwanted semi-colon!
{
dup2(fdright[0], 0);
}

关于小型壳体中的管道,有许多问题,包括:
SO 13636252
SO 13693446
SO 13905948
SO 14312939
SO 13213864
13636252的答案几乎是通用的。唯一的障碍是它使用了容易混淆的 close(),而且它写得非常紧密,具有相互递归的函数,重复性很小。otoh,它工作正常, char ***函数也使用 makeargv()参数。
重写代码
这是您重新编写的代码,以便能够正常工作。它包括 char ***err_sys()的实现。my makeargv()只是假设命令行中的单词少于32个。不管我怎么想,它都不是一个健壮的命令行解析器。它允许您键入 makeargv()并给出正确答案;它还允许 ls | wc并给出正确答案;它还允许 who | grep me | sort并给出正确答案。不过,管道符号周围的空格非常重要(在普通shell中,它们是可选的,因此 ls也应该可以工作,但这段代码不会这样做。
#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!
$

关于c - 用C制作 shell 时正确的管道连接方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15673333/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com