|
Программирование >> Структура ядра и системные вызовы
Команда ввода-вывода в канал состоит из набора простых и (или) сложных команд, объединенных командными каналами (Т)- Синтаксис такой команды следующий: <pipe> ::= [<basic> I <complex>) [Г <basic> <complex>]+ Ниже приведен пример команды с использованием канала: Is -1 /etc/passwd sort -г cat -п > foo Сложная команда - это одна или несколько команд shell (простых, с вводом-выводом в канал и сложных, заключенных в скобки и дополнительно сопровождаемых переназначением ввода и вывода). Синтаксис сложной команды: <complex> ::= С <shell> t; <shell>)* ) [< <fHe>) [> <file>] Ниже дан пример сложной кжанды: ( cat < /etc/passwd sort -г wc; pwd ) > foo Если объединить все перечисленные синтаксические правила, исходный файл уасс для синтаксического анализатора minishell будет выглядеть так: %{ /* shell.у: синтаксический анализатор программы minishell */ finclude <iostream.h> finclude <stdio.h> finclude <string.h> finclude <assert.h> finclude <sys/types.h> finclude <sys/wait.h> finclude <unistd.h> finclude <fcntl.h> finclude shell.h static CMD INFO * pCmd = 0; %union ( char* s; CMD INFO* c; %token <s> NAME %% , . \ input streain : cmd line. \n . ( exec cmd ($<c>l); ) t- , I input stream cmd line \n* ( exec cmd ($<c>2) ; ) ,у%,ч I error \n ( yyerrok; yyclearin; cmd line : shell backgrnd { $<c>$ = $<c>l; ) cmd line ; shell backgrnd { $<c>l->add next($<c>3); $<c>$ = $<c>l; shell : basic { $<c>$ = $<c>l; ) I complex ( $<c>$ = $<c>l; ) I shell I basic ( $<c>l->add pipe($<c>3); $<c>$ = $<c>l; I shell I complex ( $<c>l->add pipe($<c>3); $<c>$ = $<c>l; basic : cmd spec io spec ( $<c>$ $<c>l; } complex : ( cmd line ) ( pCmd = new CMD INFO; pCmd->pSubcmd = $<c>2; io spec ( $<c>$ = pCmd; ) cmd spec : NAME , j ( $<c>$ = pCmci,5 add vect (0,$<s>l) ;, I cmd spec NAME { $<c>$ = add vect($<c>l.$<s>2) ; } io spec : /* empty */ I io spec redir -> V: edir,t < NAME ( pCmd->add iofile (pCmd->infile, $<s>2) ; 1 I > NAME { pCmd->add iofile(pCmd->outfile,$<s>2) ; backgrnd : ,/* empty */ { pCmd->backgrnd = l/:. /* программа выдачи сообщений об ошибках */ void yyerror (char* s) cerr s endi; } /* добавить команду или аргумент в список */ CMD INFO *add vect (CMDi INFO* pCmd, char* str) int len = 1; -i if (!pCmd) assert(pCftid = new CMD INFO); pCmd->add arg(str); return pCmd; . .J В файле shell.y синтаксическое правило cmdjspec распознает имя команды, необязательные ключи и аргументы. Если это правило срабатывает , то создается объект CMD INFO для хранения команды и ее аргументов (с помощью функции add vect), а глобальный указатель pCmd устанавливается так, чтобы указывать на этот вновь созданный объект. С помощью правила io spec собираются имена файлов, связанные с переназначением ввода и (или) вывода, и с применением функции CMD INFO::add Jile добавляются к текущему объекту CMD INFO (на который указывает pCmd). Правило basic состоит из правил cmdjspec и iojspec и служит для распознавания одной элементарной команды, указанной пользователем. Оно передает объект CMD INFO, созданный правилом cmd spec, в правило shell. Правило shell может соответствовать правилам basic, complex или pipe, последнее из которых состоит из набора правил basic и (или) complex, разделенных лексемой . Для правила pipe синтаксический анализатор связывает объекты CMD INFO (с использованием их указателя CMDJNFO:: Pipe), которые были созданы правилами basic и complex с помощью функции CMDINFO::add jipe. Правило shell возвращает правилу cmdjine либо объект CMD INFO, созданный правилом basic или complex, либо связный список этих объектов, обозначенный указателем Pipe. Правило cmdjine соответствует одному или нескольким правилам shell, каждое из которых может завершаться необязательным символом & и отделяться символом Синтаксический анализатор связывает объекты CMD INFO, возвращенные из правил shell (с использованием указателя CMDJNFO::Next), в связный список Next с помощью функции CMDJNFO:: addjxext. Правило cmdjine представляет программе minishell одну строку команды и возвращает первый объект CMD INFO, имеющийся в связном списке Next (который строится по правилу input stream или complex). Правило complex представляет собой правило cmdjine, заключенное в парные круглые скобки. После символа ) может быть указано переназначение ввода и (или) вывода. Правило complex служит для представления набора команд shell, предназначенных для выполнения в отдельном процессе. Синтаксический анализатор создает специальный объект CMD INFO для обработки данного правила. В этом объекте аргумент CMDJNFO::argv равен нулю, указатель CMDJNFO::pSubcmd указывает на объект CMDJNFO (который может быть связным списком Next), возвращенный из правила cmdjine, а любое переназначение ввода и (или) вывода записывается в переменные CMDINFO::infde и CMDJNFO::outfde объекта. Правило complex возвращает объект CMD INFO, созданный им по правилу shell. Наконец, правило input stream состоит из одного или более правил cmdjine, каждое из которых завершается символом \п . Для применения каждого активного правила cmdjine синтаксический анализатор вызывает функцию execjcmd, чтобы выполнить команды shell, связанные с объектами CMDJNFO, возвращенными правилом cmdjine. Функция execcmd выглядит следующим образом: /* exec cmd.C: функции, предназначенные для выполнения одной входной строки команды shell */ ♦include <iostream.h> ♦include <stdio.h> ♦include <stdlib.h> ♦include <unistd.h> ♦include <fcntl.h> ♦include <sys/wait.h> ♦include shell.h /* изменить порт стандартного ввода-вывода процесса */ void chg io( char* fileName, mode t mode, int fdesc ) int fd= open(fileName,mode,0777); if (fd==-l) perror( open ); else ( if {dup2{fd,fdesc)==-1) perror( dup2 ); close(fd), /* создать один или несколько командных каналов */ void execjJipes( CMD INFO *pCmd ) CMD INFO *ptr; int fifo(2][23; int bg=0, first=l, cur pipe = 0; pid t pid; while (ptr=pCmd) pCmd = ptr->Pipe; if (pipe(fifofcur pipe])==-!) perror ( pipe ) ; return,-/, . switch(fork 0 ) case -1: perror( fork ) ; не первая команда return; case О : if (Ifirst) ( dup2(fifo[l-cur pipe][0],0); close(fifo[l-cur pipe][0]); ) else if (ptr->infile) chg io(ptr->infile,0 RDONLY,0); if (pCmd) не последняя команда dup2(fifo[cur pipe][1], 1); else if (ptr->outfile) chg io(ptr->outfile, 0 WRONLY I 0 CREAT 10 TRUNC ,1УГ, close (fifo[cur pipe] [0)) ; close(fifo[cur pipe][1]); execvp(ptr->argv[0],ptr->argv); cerr Execute ~ ptr->argv[0) fails\n ; exit (4); if (Ifirst) close(fifo[l-cur pipe][0]); close(fifo[cur pipe][1])? cur pipe = 1 - cur pipe, bg = ptr->baclcgrnd; delete ptr; first = 0; close(fifo[l-cur pipe][0]); while (!bg && (pid=waitpid(-l,0, 0))!=-l) /* выполнить одну командную строку shell */ void exec cmd( CMD INFO *pCmd ) { pid t prim pid, pid; CMD INFO *ptr = pCmd; создать вторичный shell для обработки одной командной строки switch( prim pid = fork()) case -1: perror( fork ); return; case 0: break; default: if (waitpid(prim pid,0,0)!=prim pid) perror( waitpid ); return; while (ptr=pCmd) pCmd = ptr->Next; if (ptr->Pipe) exec pipes(ptr); else выполнить для каждой команды выполнить команду, связанную с каналом noBnpot fipc для выполнежд!; команды switch (pidfforkO) case, -il!? perror ( fork }ireturn; case 0:, break; default: if (!ptr->backrrtd && waitpid(pid, 0, 0)!=pid) perror( waitpid ); delete ptr; continue; ) , , . if (ptr->infile) chg io(ptr->infile,0 RDONLy,0); if (ptr->outfile) chg io(ptr->6utfile,0 WRONLYI0 CREAT0 TRUNC,1); if (ptr->argv) ( execvp(ptr->argvto], ptr->argv); cerr Execute ptr->argv[0] fails\n ; exit(999); ) else { exec cmd(ptr->pSubcmd); exit(O); exit(O); Функция exec cmd эмулирует shell, в том смысле, что создает порожденный процесс для выполнения каждой вводимой командной строки. В частности, эта функция вызывается с указателем на объект CMD INFO, который служит для представления одной или нескольких выполняемых команд. Функция вначале выполняет системный вызов fork для создания порожденного процесса, а родительский процесс вызывает waitpid для ожидания завершения порожденного процесса. Порожденный процесс, являясь процессом shell, анализирует заданный связный список объектов CMD INFO и выполняет каждую команду так, как описано ниже. Если в команде используется канал, он вызывает команду exec pipe для выполнения команд, заданных связным списком Pipe этого объекта. Если команда является сложной или простой, процесс вторичного shell вызывает fork, чтобы создать новый порожденный процесс для выполнения команды следующим образом: Если в объекте CMDINFO задано переназначение стандартного ввода и (или) вывода, новый порожденный процесс с помошью функции chgjo перенаправляет свой стандартный ввод и (или) вывод на указанные файлы. Если команда является простой, новый порожденный процесс вызывает exec для выполнения команды, указанной в CMD INFO::argv объекта. Однако в том случае, когда команда является сложной (CMDINFO.-.pSubcmd объекта ~ не NULL), новый порожденный процесс рекурсивно вызывает функцию
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |