bwwu/posix-shell
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Brandon Wu **************************************************************************** * CS 111 Lab 1C * **************************************************************************** DESIGN PROJECT: The interactive shell is reachable by commandline option ./timetrash -i 0. CONTENTS 1. Implementation Details ................. 11 2. Limitations ............................ 71 1. IMPLEMENTATION DETAILS This POSIX shell command reader employs a token-centric implementation of the parser. The command_stream object is implemented as a linked list of command_nodes which is a structure defined with the following members: command_node* nxt; command_t cmd; I.e. it contains a pointer to a command_node containing the subsequent command if it exists (or NULL if it is the last command in the stream). The "cmd" member is a pointer to the command object. The command structure follows the definition provided in "command-internals.h". The "read_command_stream" function processes each command in the stream individually until the stream is empty (returns NULL). The "make_command_stream" function calls the "make_command" function until it returns NULL (no commands remain in the filestream). The "make_command" function is an interface that calls its auxilary function "make_cmd_aux". The "make_cmd_aux" function parses the filestream for a valid command. In the basic case, it reads in a single line from the filestream using the "get_line" function and stores the line in a line buffer array. The line buffer is parsed for individual tokens by calling the "get_token" function. A token is a structure defined with the following members: token_type type; char* word; The type "token_type" is an enum of all special tokens and words (not including white space). The word member is only utilized for token_type = WORD, in which it contains an array of characters that compose the word. Make cmd_aux asks get_token to categorize and return the next token in the line buffer and processes each type differently: For token_type = WORD, each token sequentially. The word member is copied into the cmd->u.word field. This operation is terminated when the end of the line buffer is reached or if another special character is encountered. For token_type = {IN,OUT}, corresponding to the I/O redirection characters, if the current command does not already have such field defined,the following token returned by get_token is placed in the cmd->{input,output} field. For token_type = {AND, OR, PIPE, SEMI} corresponding to all sequence-type tokens of the form A <token> B, all tokens processed thusfar are copied intoa newly allocated command at cmd->u.command[0]. The contents of cmd->u.command[1] are found by calling make_cmd_aux recursively. For token_type = OPAREN (open parenthesis), a SUBSHELL_COMMAND is present. The contents of this command are determined recursively, until a ')' is discovered or EOF. PART B: Execute command works trivially for all commands once execute command for a SIMPLE_COMMAND is estabilished. For a SIMPLE_COMMAND, the execution calls "exec_simple" which performs a fork() system call. For the serial case (time_travel == 0), the parent waits on the child pid to finish, storing the exit status of the child it its appropriate location. The child calls an exec() system call to change the program of execution to that of the command that is called. This approach works well for the parallelization case. The AND and OR command recursively calls the execute_command function, either one or two recursive calls is made depending on the type of command and the exit status of the first executed command (if exit is 0 for AND or exit is 1 for OR). Sequence command also works similarly. The PIPE_COMMAND is handled by a special case. Consider the following pipe command: A | B. This is interpreted as A > temp; B < temp. Therefore, for a large stream of pipe commands, e.g. A | B | C | D, we utilize two temporary files and interleave them so there is no R/W thrashing. The SUBSHELL_COMMAND simply calls the execute command function recursively on its subshell command. PART C: PART C introduces support for "time_travel" mode. The basic idea here is that when the fork() system call is made, the parent need not wait for the child to terminate, it simply moves onto executing the next command. The only time a wait occurs is when a child should wait on another child. In this case, the entire thread of execution enters a critical section, so the new process is not forked until the one it is blocked on finishes. This is implemented using a dependency table held by the main function but manipulated by sub-routines in execute_command.c. The Dependency table is implemented as a linked list with the following structure: struct depend_node { depend_node_t nxt; char* handle; pid_t pid; }; The handle member contains a string of the file handle name. The pid field holds the pid that the current command is blocked on (i.e. it should wait for pid to exit before forking its own child). 2. LIMITATIONS The following limitations are present within the design: 1. Under some very special cases that have not been completely identified, syntax errors are reported at lines > than the actual line of the error. Most common seen in multi-line subshell commands 2. When a nested subshell is used, the inner subshell command must be contained within a single line. For example, ( echo hello ) is valid, and so is ( ( echo hello ) ) but ( ( echo hello ) ) is a syntax error 3. When in time_travel mode, sometimes the process will exit before all commands are done. This happens because the parent does not wait for the children to exit unless the next command to be scheduled is blocked by one of the previous commands.
About
No description, website, or topics provided.
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published