联系方式

  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-21:00
  • 微信:codinghelp

您当前位置:首页 >> C/C++编程C/C++编程

日期:2020-05-27 11:01

CSCI 3120 Assignment 2

Due May 30 by 9:00 AM ADT

NOTES

You may wish to create your program using multiple files. And you may wish

to use a Makefile. If you have more than one file for this assignment, please

create a tar file containing all files needed to compile and execute your program

(including your Makefile, if you use one), and upload that tar file to

Brightspace. If you don’t use a Makefile and your program requires more than

a simple “gcc a2.c -o a2” to compile, you must include that information in

the comment at the top of your main program file. BUT in such a case you really

should use a Makefile!

As always for this course, this assignment must compile and run on the FCS

Unix server csci3120.cs.dal.ca. Do not use any of the other FCS servers

for this course.

As in A1, this program should follow the C-coding-style-notes (found in

the “Assignments and related material” folder on Brightspace.

[1] As you may know, there are lots of different command shells available for Unix-type systems;

ash, bash, csh, dash, fish, ksh, sh, tcsh and zsh immediately come to mind, but I’m sure

you can find more. For this question, you will create your own (rudimentary) shell using C. You

can create your program using any system you like, but your program must compile and run on

csci3120.cs.dal.ca; if it doesn’t, the marker is authorized to give you 0, regardless of how

much work you have put into your program.

All of the above shells are complex programs which provide many, many features. This assignment

question requires you to only implement a tiny subset of the functionality of a normal shell. Below

I list each type of command-line you are to implement, and how many points each of those pieces

is worth. Your shell should prompt the user for input with the prompt “$ ”, as seen below in the

sample commands.

In the examples below, the input typed by a user is in red, and output by the shell is in black.

Your shell should implement the following capabilities. These capabilities are explained further

below, along with simplifying assumptions that your program may make. The first few show sample

output from the executed commands, but for brevity some of the rest do not show the output. Try

the commands yourself in your shell to see the sort of output to expect.

(i) (1 point) If the command is exit, your shell terminates. This is what a terminal session which

starts your program and then immediately runs the exit command should look like (where

“<prompt>” is whatever prompt you have at your regular shell):

<prompt> ./a2

$ exit

<prompt>

1

(ii) (5 points) Run a program which takes no arguments and waitpid() for the executed program

to finish, then print another prompt and wait for another command line; examples:

$ who

<output of the who command>

$ date

<output of the date command>

(iii) (5 points) Run a program with arguments; examples:

$ who am i

<output of the who am i command>

$ ls -l /etc/passwd /etc/hosts

-rw-r--r--. 1 root root 158 Sep 10 2018 /etc/hosts

-rw-r--r--. 1 root root 1279 May 6 09:58 /etc/passwd

(iv) (5 points) Run a sequence of commands on one line; examples

$ date ; sleep 10 ; date

Sat May 16 09:05:36 ADT 2020

Sat May 16 09:05:46 ADT 2020

$ echo The users on the system right now are: ; who

The users on the system right now are:

<a list of users starts here>

(v) (9 points) Run two programs (each with or without arguments) where the output of the first

program is piped into the input of the second program; examples

$ date | sed s/.*://

$ ls -l | grep rwx

(vi) (3 points) Implement “background jobs”; that is, if a command ends with “&”, rather than

your shell waiting for it to complete, the shell continues on with other commands on the line

(if any), or outputs a prompt and waits for another command.

$ xz some-big-file-to-be-compressed &

$ compute-1000000-digits-of-pi & sleep 3 ; ps uaxw

(vii) (2 points) (If you don’t implement this part, you will create Zombies!) Just before printing the

prompt, use the waitpid() system call (with the WNOHANG option!) to “reap” any background

jobs which have terminated. (Use -1 as the first argument to waitpid().) If a background

job has terminated, output its process id (pid). In the following example, imagine <delay>

means the user has gone away for a few minutes and then returned.

$ sleep 10 & date

<output of the date command>

$ <delay> who

<output of the who command>

Process 4321 terminated

$

(viii) (3 bonus points) If there are two commands on a line separated by “&&”, execute the second

command if and only if it the first command exits with a success indication. (The waitpid()

system call returns this information.) Your program only needs to implement this for lines

with exactly two commands; example

$ grep root /etc/passwd && echo There is root!

2

root:x:0:0:root:/root:/bin/bash

operator:x:11:0:operator:/root:/sbin/nologin

There is root!

$ grep tree /etc/passwd && echo There is tree!

$

Hints, simplifications, and more details:

(i) Normal shells don’t require space around command separators such as “;”, “&” and “&&”. To

make it easier for you to parse the command lines, you may assume that these separators are

always surrounded by white space (the first two may legally be at the very end of a line as

well).

(ii) Speaking of parsing the input line, you may use the readline library if you want (and if you

know what that is). But if you don’t mind having the minimal line-editing capability provided

by the Linux terminal driver (which is considerably less than what shells provide for you),

don’t worry about it. I’m not worrying, and the markers will not expect you to do that.

(iii) Normal shells implement “wildcard” characters, such as “*”. You definitely do not have

to do those! Or variables. Or I/O redirection. Or programming constructs. Or commandline

editing features (e.g., going back and forth with arrow keys). Or any other feature not

mentioned above! Not being able to go back and forth (with arrow keys or control-this or

sec-that) will mean you will have to type carefully when testing your shell, or you can copy

and paste entire command lines from (say) an editor window into the terminal window running

your shell.

(iv) Although “real” shells allow arbitrarily complex pipelines (as they are called) using the “|”,

“;”, “&” and “&&” features (and others), (e.g., “p1 | p2 | p3 && p4 ; p5 & p6”), for

this assignment you don’t have to implement the ability to handle any combination of “&”

with either “&&” or “|”. Nor do you have to allow more than one “|” per line. You do have

to implement the ability to parse and execute lines with more than one “&” and/or “;”, such

as “p1 ; p2 args ; p3”, “p1 arg1 arg2 & p2 args ; p3 arg1 arg2” and even

“p1 arg & p2 args ; p3 & p4” where, as you might guess, the “p<i>s” are arbitrary

programs.

(v) Ordinary shells do some trickery to avoid having background jobs output text to your screen.

You really don’t want to try to implement that! So if you use a put a program into the

background and it outputs text, that output will get mixed in with whatever other output is

coming out (including your $ prompt. Don’t let this issue worry you. If you want to see

what messes can occur, save the following three lines of code into a file called (for example)

delay5, execute the command chmod 755 delay5 , and then inside your shell try a

command line like $ ./delay5 & and wait 5 seconds to see what happens.

#! /bin/sh

sleep 5

echo $0 is now outputting stuff!

(vi) Your program should not crash and burn if the user types in an invalid command line. However,

as soon as your program detects some syntax it don’t handle, it can output a short message,

terminate processing of the command line, and go back to the waitpid()/prompt section.

(vii) Normal shells allow long command lines. You may make the restriction that the command

line will never be longer than 80 chars. Which, if you think about it, also limits the number

3

of tokens on a command line. Feel free to use these limitations to simplify the design of your

program. (But please try not to do something horrible when a much more elegant solution is

more or less obvious.)

(viii) String parsing in C is a bit of a nuisance, unless you decide to get out heavy-duty tools. For

this assignment, you might find the following code sketch (which is not industrial-strength,

and needs some modification for this assignment, but should give you some ideas) useful:

char * tok;

char cmd_name[MAX_CHARS + 1];

...

// Read a new line into "str", making sure it is < MAX_CHARS chars long.

...

// Now parse out the tokens ("words") on that line.

tok = strtok(str, " \t");

if (tok == NULL)

{

// Empty line, nothing to process here, go read another line

}

strcpy(cmd_name, tok);

while ((tok = strtok(NULL, " \t")) != NULL)

{

// look at the token pointed to by tok and

// see if it is a ’;’, a ’&’, or an argument

// if a ’;’ or ’&’ fork()/execvp() the command

// and then keep processing the line.

// And if you do the bonus part also check for ’&&’

// Keep in mind strcmp() returns 0 if two strings match!

// Also note that you have to strcpy() (or equivalent)

// the string tok points to into your own character array

// before the next call to strtok(), otherwise that token will

// be gone forever!

}

(ix) The shell looks through the directories in your PATH variable to find the first one containing

an executable file by the same name as the program name on the command line. Don’t

bother dealing with that yourself. Rather, instead of plain exec() use execvp(), which will

perform the hunt itself. The first argument is a pointer to the command name (“cmd_name”

above). The second argument to execvp() is an array of pointers; each pointer of that array

points to one of the arguments, and the next element of that array must be set to NULL.

(x) You must use execvp() (or one of the other exec() system calls); you are absolutely not

allowed to use the system() function.

This is a fairly significant piece of work. Don’t worry about catching every possible input error a

user might make. If your program correctly processes correct input (as above), and it doesn’t crash

on invalid input, you can be happy.

4

This is not a programming course, and therefore I am not your programming instructor. But, having

said that, here are some suggestions as to how to proceed which I think will help you successfully

complete this assignment. You don’t have to follow them, but I would suggest you consider doing

so, unless you are a whiz-bang programmer who doesn’t need any help at all.

I would strongly suggest you start by writing a program that can output a prompt, read a line of

text, and parse the line of text into individual tokens (using strtok(), or, if you prefer, some

other technique). When you can do that and output the tokens to your screen, give yourself a pat

on the back, copy that code to another file in case of accidents, and carry on.

Now might be a good time to think about how you will structure your program so that it can handle

more than one command on a line.

After that, extend the program so that it prompts and reads in a loop, until the input line is “exit”,

at which point your program exits. When you can do that, give yourself a pat on the back, copy

that code to another file in case of accidents, and carry on.

After that, figure out how to correctly call execvp() for a command with no arguments, and to

wait() or waitpid() for it to finish. Try that out with commands like who or date and see if

you get sensible output on your screen. When you can do that, give yourself a pat on the back,

copy that code to another file in case of accidents, and carry on.

After that, add the ability for the command to have arguments. This is (essentially) just additional

work with strtok(), strcpy() and copying pointers into an array.

After that, add the ability to have multiple commands on one line, separated by “;”.

The next one (piping from one program to another) is tricky; you might consider whether you

want to do it now, or after everything else is done. You know how to parse a line into commands

with arguments, but now you will have to fork() and execvp() two commands, but only after

you use pipe() to create an unnamed pipe. In the child processes (after fork() but before

execvp()) you must “detach” standard output (of the first command) from the terminal and send

it to the pipe. Similarly you must detach the standard input of the second command from the

keyboard and instead read from the pipe. You will want to read the man page for dup2(). Keep

in mind that standard input (“stdin”) is file descriptor 0 and standard output (“stdout”) is file

descriptor 1. Also don’t forget to close() unused ends of pipes in all the right places, so that

your program does not end up with more and more pipes. (But I suggest that you first get piping

working, then add code to close() unused pipe ends and check you haven’t broken anything.)

Next up: allow so-called “background jobs”, where a command is followed by “&”. This should

be relatively easy, because all you have to do is not waitpid() for the child to finish. Having

done that, you should next implement the waitpid() functionality for these “background jobs”.

After all the above, the only thing to do is to implement the bonus “&&” feature. In this case

waitpid() is your friend, because it returns information you can use to determine whether the

first command succeeded.

EVALUATION:

If your program does not compile on csci3120.cs.dal.ca it is worth 0 points, no matter

5

how good it is otherwise. Similarly, if your program compiles but crashes (when running on

csci3120.cs.dal.ca) on most or all input, it is still worth 0.

If your program compiles and runs without crashing on correct input, you will receive points for

each feature you correctly implement, as per the point values above.

While there are no points specifically allocated to code style, quality and readability, the markers

have the prerogative to deduct marks if they feel your code fails to meet reasonable standards in

any of these three areas.

6


版权所有:编程辅导网 2021 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。 站长地图

python代写
微信客服:codinghelp