联系方式

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

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

日期:2018-11-17 09:50

README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 1/7

Bring in README.md from the development repo.

Gene Stark authored 2 weeks ago

693a78dc

The goal of this assignment is to become familiar with low-level Unix/POSIX system calls related to processes, signal handling, files, and I/O

redirection. You will implement a printer spooler program, called imprimer , that accepts user requests to queue files for printing, cancel printing

requests, pause and resume print jobs, show the status of printers and print jobs, and set up pipelines to convert queued files of one type to the

type of file accepted by an available printer.

After completing this assignment, you should:

Understand process execution: forking, executing, and reaping.

Understand signal handling.

Understand the use of "dup" to perform I/O redirection.

Have a more advanced understanding of Unix commands and the command line.

Have gained experience with C libraries and system calls.

Have enhanced your C programming abilities.

We strongly recommend that you check the return codes of all system calls and library functions. This will help you catch errors.

BEAT UP YOUR OWN CODE! Use a "monkey at a typewriter" approach to testing it and make sure that no sequence of operations, no

matter how ridiculous it may seem, can crash the program.

Your code should NEVER crash, and we will deduct points every time your program crashes during grading. Especially make sure that you

have avoided race conditions involving process termination and reaping that might result in "flaky" behavior. If you notice odd behavior you

don't understand: INVESTIGATE.

You should use the debug macro provided to you in the base code. That way, when your program is compiled without -DDEBUG , all of your

debugging output will vanish, preventing you from losing points due to superfluous output.

When writing your program, try to comment as much as possible and stay consistent with code formatting. Keep your code

organized, and don't be afraid to introduce new source files if/when appropriate.

This assignment will involve the use of many system calls and library functions that you probably haven't used before. As such, it is imperative

that you become comfortable looking up function specifications using the man command.

The man command stands for "manual" and takes the name of a function or command (programs) as an argument. For example, if I didn't know

how the fork(2) system call worked, I would type man fork into my terminal. This would bring up the manual for the fork(2) system call.

Navigating through a man page once it is open can be weird if you're not familiar with these types of applications. To scroll up and

down, you simply use the up arrow key and down arrow key or j and k, respectively. To exit the page, simply type q. That having been

said, long man pages may look like a wall of text. So it's useful to be able to search through a page. This can be done by typing the / key,

followed by your search phrase, and then hitting enter. Note that man pages are displayed with a program known as less . For more

information about navigating the man pages with less , run man less in your terminal.

Now, you may have noticed the 2 in fork(2) . This indicates the section in which the man page for fork(2) resides. Here is a list of the man

page sections and what they are for.

Section Contents

1 User Commands (Programs)

README.md 31.2 KB

Homework 4 Printer Spooler - CSE 320 - Fall 2018

Professor Eugene Stark

Due Date: Friday 11/16/2018 @ 11:59pm

Introduction

Takeaways

Hints and Tips

Reading Man Pages

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 2/7

Section Contents

2 System Calls

3 C Library Functions

4 Devices and Special Files

5 File Formats and Conventions

6 Games et. al

7 Miscellanea

8 System Administration Tools and Daemons

From the table above, we can see that fork(2) belongs to the system call section of the man pages. This is important because there are

functions like printf which have multiple entries in different sections of the man pages. If you type man printf into your terminal, the man

program will start looking for that name starting from section 1. If it can't find it, it'll go to section 2, then section 3 and so on. However, there is

actually a Bash user command called printf , so instead of getting the man page for the printf(3) function which is located in stdio.h , we

get the man page for the Bash user command printf(1) . If you specifically wanted the function from section 3 of the man pages, you would

enter man 3 printf into your terminal.

Remember this: man pages are your bread and butter. Without them, you will have a very difficult time with this assignment.

Fetch and merge the base code for hw4 as described in hw0 . You can find it at this link: https://gitlab02.cs.stonybrook.edu/cse320/hw4

NOTE: For this assignment, you need to run the following command in order for your Makefile to work:

$ sudo apt-get install libreadline-dev

The sudo password for your VM is cse320 unless you changed it.

The above command installs the GNU readline library, which is a software library that provides line-editing and history capabilites for

interactive programs with a command-line interface. It allows users to move the cursor, search the command history, control a kill ring (which is

just a more flexible version of a copy/paste clipboard) and use tab completion on a text terminal. We highly recommend that you use it for this

assignment.

Here is the structure of the base code:

.

├── .gitlab-ci.yml

└── hw4

├── include

│ ├── debug.h

│ └── imprimer.h

├── lib

│ └── imp_util.o

├── Makefile

├── rsrc

│ └── imprimer.cmd

├── src

│ └── main.c

├── tests

└── util

├── printer

├── show_printers.sh

└── stop_printers.sh

If you run make , the code should compile correctly, resulting in an executable bin/imprimer . If you run this program, it doesn't do very much,

because there is very little code -- you have to write it!

When started, imprimer should present the user with a command-line interface with the following prompt

imp>

Getting Started

Imprimer : Functional Specification

Command-Line Interface

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 3/7

Typing a blank line should should simply cause the prompt to be repeated, without any other printout or action by the program. Non-blank lines

are interpreted as commands to be executed. Imprimer commands have a simple syntax, in which each command consists of a sequence of

"words", which contain no whitespace characters, separated by sequences of one or more whitespace characters. The first word of each command

is a keyword that names the command. Any remaining words are the arguments to the command. Imprimer should understand the following

commands, with arguments as indicated. Square brackets are not part of the arguments; they merely identify arguments that are optional.

Miscellaneous commands

help

quit

Configuration commands

type file_type

printer printer_name file_type

conversion file_type1 file_type2 conversion_program [arg1 arg2 ...]

Informational commands

printers

jobs

Spooling commands

print file_name [printer1 printer2 ...]

cancel job_number

pause job_number

resume job_number

disable printer_name

enable printer_name

The help command takes no arguments, and it responds by printing a message that lists all of the types of commands understood by the

program.

The quit command takes no arguments and causes execution to terminate.

The type command declares file_type to be a file type to be supported by the program. Possible examples (but not an exhaustive list) of file

types are: pdf (Adobe PDF), ps (Adobe Postscript), txt (text), png (PNG image files), etc. A file will be presumed to be of a particular type

when it has an extension that matches that type. For example, foo.txt will be presumed to be a text file, if txt has previously been declared

using the type command. Files whose names do not having an extension that matches a declared type are considered of unknown type and are

to be rejected if an attempt is made to spool them for printing. Essentially any identifier can be used as a file type -- they may (but aren't

required to) correspond to "known" file types that have standards, are supported by other programs, etc.

The printer command declares the existence of a printer named printer_name, which is capable of printing files of type file_type. The

printer_name is just an identifier, such as Alice . Each printer is only capable of printing files of the (one) type that has been declared for it, and

your program should take care not to send a printer the wrong type of file.

The conversion command declares that files of type file_type1 can be converted into file_type2 by running program conversion_program with

any arguments that have been indicated. It is assumed that conversion_program reads input of type file_type1 from the standard input and

writes output of type file_type2 to the standard output, so that it is suitable for use in a pipeline consisting of possibly several such programs. For

example, on your Linux Mint VM:

The command pdf2ps - - can be used to convert PDF read from the standard input to Postscript on the standard output.

The command pbmtext can be used to convert text read from the standard input to a Portable Bitmap (pbm) file on the standard output.

The command pbmtoascii can be used to convert a Portable Bitmap (pbm) file read from the standard input to an ASCII graphics (i.e. text)

file on the standard output.

The command pbmtog3 can be used to convert a Portable Bitmap (pbm) file read from the standard input to a Group 3 FAX file (g3).

There are many others: some of them work well together and others do not. For many of these commands there are also corresponding

commands that convert formats in the reverse direction.

The printers command prints a report on the current status of the declared printers, one printer per line. For example:

imp> printers

PRINTER, 0, alice, ps, disabled, idle

PRINTER, 1, bob, pcl, disabled, idle

The jobs command prints a similar status report for the print jobs that have been queued. For example:

imp> jobs

JOB 0 22 Oct 2018 16:08:11 pdf queued 22 Oct 2018 16:08:11 0 foo pdf ffffffff

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 4/7

JOB, 0, 22 Oct 2018 16:08:11, pdf, queued, 22 Oct 2018 16:08:11, 0, , foo.pdf, ffffffff

JOB, 1, 22 Oct 2018 16:08:16, ps, queued, 22 Oct 2018 16:08:16, 0, , bar.ps, ffffffff

JOB, 2, 22 Oct 2018 16:08:34, txt, queued, 22 Oct 2018 16:08:34, 0, , mumble.txt, ffffffff

The formats of these status reports will be generated by functions that have been provided for you, as discussed in more detail below. You must

use the provided functions, which will make it easier for us to automate some of the testing of your program.

The print command sets up a job for printing file_name. The specified file name must have an extension that identifies it as one of the file types

that have previously been declared with the type command. If optional printer names are specified, then these printers must previously have

been declared using the printer command, and they define the set of eligible printers for this job. Only a printer in the set of eligible printers for

a job should be used for printing that jobs. Moreover, an eligible printer can only be used to print a job if there is a way to convert the file in the

job to the type that can be printed by that printer. If no printer name is specified in the print command, then any declared printer is an eligible

printer.

The cancel command cancels an existing job. If the job is currently being processed, then any processes in the conversion pipeline for that job

are terminated (by sending a SIGTERM signal to their process group).

The pause command pauses a job that is currently being processed. Processes in the conversion pipeline for that job are stopped (by sending a

SIGSTOP signal to their process group).

The resume command resumes a job that was previously paused. Processes in the conversion pipeline for that job are continued (by sending a

SIGCONT signal to their process group).

The disable command sets the state of a specified printer to "disabled". This does not affect the status of any job currently being processed by

that printer, but a disabled printer is not eligible to accept any further jobs until it has been re-enabled using the enable commnd.

The enable command sets the state of a specified printer to "enabled". When a printer becomes enabled, if there is a pending job that can now

be processed by the newly enabled printer, then processing is immediately started for one such job.

Your program must produce output in the following situations, which do not necessarily occur in direct response to a user command:

Whenever the status of a printer has changed. In this case, the output must consist of a status line showing the new status of that printer,

formatted using the function ( imp_format_printer_status() ) we provide for this purpose, as described for the printers command above.

Whenever the status of a job has changed. In this case, the output must consist of a status line showing the new status of that job, formatted

using the function ( imp_format_job_status() ) we provide for this purpose, as described for the jobs command above.

Whenever an error occurs while executing a user command. In this case, the output must consist of a single line containing an error message

that has been formatted using the function ( imp_format_error_message() ) that we provide for this purpose.

Your program is permitted to emit output in addition to that specified above, but any such output must occur on a line that does not start with

" PRINTER ", " JOB ", ERROR , or " imp> , so that we can filter it out if we need to.

The normal mode of operation of imprimer is as an interactive application. However, it can also be run in batch mode, in which it reads

commands from a command file. If imprimer is started as follows:

$ imprimer -i command_file

then it begins by reading and executing commands from command_file until EOF, at which point it presents the normal prompt and executes

commands interactively. Normally this feature would be used to cause configuration commands (declarations of types, printers, and conversions)

to be read from a command file, rather than typed each time. If a quit command appears in the command file, then the program terminates

without entering interactive mode. This can be used to run a series of commands completely automatically without user intervention.

If imprimer is started with the " -o output_file" option, then any output it produces that would normally appear on the terminal is to be

redirected instead to the specified output file.

If the program is run in interactive mode (the default), then it should use the readline() function to read commands from the user. If the

program is run in batch mode, then readline cannot be used, so in this case the program will have to read commands using either the standard

I/O library or low-level Unix I/O.

The purpose of imprimer is to process print jobs that are queued by the user. Each time there is a change in status of a job or printer as a result

of a user command or the completion of a job being processed, imprimer must scan the set of queued jobs to see if there are any that can now

be processed, and if so, start them. In order for a job to be processed, there must exist a printer that is enabled and not busy, the printer must be

in the eligible_printers set for that job, and there must be a way to convert the type of file in the job to the type of file the printer is capable

of printing. If these conditions hold, then the job status is set to RUNNING , the chosen printer is set to "busy" and the chosen_printer field of the

JOB structure is set to point to the PRINTER that has been selected. A group of processes called conversion pipeline is set up to run a series of

programs that will convert the type of file in the job to the type of file that the printer can print. This is described further below.

Program Output

Batch Mode

Reading Input

Processing Print Jobs

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 5/7

A job will exist at any given time in one of various states, the possibilities for which are defined by the JOB_STATUS enum in imprimer.h . These

states and their meanings are:

QUEUED -- The job has been created and is ready for processing. A job will persist in this state only as long as there are no printers in the set

of eligible printers for that job that can be used to print the job. As soon as an eligible printer (of an appropriate type) becomes available, the

job will transition to the RUNNING state.

RUNNING -- An eligible printer of an appropriate type has been chosen for the job and a conversion pipeline has been created to convert the

file in the job to the type of file that the printer is capable of printing. The chosen printer must be among the printers in the

eligible_printers set for that job. For a job to be started on a printer, the printer must be "enabled" and "not busy". The printer status is

changed to "busy", and it stays that way as long as the job is RUNNING .

PAUSED -- A job that was previously RUNNING has temporarily been stopped by sending a SIGSTOP signal to the process group of the

processes in the conversion pipeline. A job in the PAUSED state will remain in that state until a resume command has been issued by the

user. This will cause a SIGCONT signal to be sent to the process group of the conversion pipeline.

??The state of a job should not be changed immediately when the user issues a pause command. Instead, the SIGSTOP signal

should first be sent and the state of the job changed from RUNNING to PAUSED only when a SIGCHLD signal has subsequently been

received and a call to the waitpid() function returns showing WIFSTOPPED true of the process status. Similarly, the state of a job

should not be changed immediately when the user issues a resume command, but only once a SIGCHLD signal has been received

and a subsequent call to waitpid() returns showing WIFCONTINUED true of the process status.

COMPLETED -- A job enters this state from the RUNNING state once processing has completed and the processes in the conversion pipeline

have terminated normally. Once in the COMPLETED state, a job will remain in the queue so that its status can be inspected, but it will be

ineligible for further processing. A job will remain in the queue until just after the execution of the first user command that is issued after the

job has been completed for one minute. At that point the job will be deleted from the queue and freed.

ABORTED -- A job enters this state from the RUNNING state if one or more processes in the job pipeline terminate abnormally. Once having

entered the ABORTED state, a job is treated similarly to a COMPLETED job as described above.

The imprimer program must install a SIGCHLD handler so that it can be notified immediately upon completion of a job being processed. The

handler must appropriately update the job and printer status information and start any further jobs in the queue that can now be processed by

virtue of the printer having become available.

Note that you will need to use sigprocmask() to block signals at appropriate times, to avoid races between the handler and the

main program, the occurrence of which which will result in indeterminate behavior.

Each time the status of a job or printer changes, your program must immediately print a corresponding status line (created using the

imp_format_job_status() or imp_format_printer_status() functions). This is so that so that it is evident (to those grading your program) what

and when state changes have occurred.

In order to determine whether a particular printer can be used to service a particular job, it will be necessary to determine whether there is a

sequence of conversions that can be used to transform the type of the file in the job to the type of file the printer can print. To determine this,

you will need to maintain a suitable data structure (e.g. a matrix) to record the information supplied by the user in the form of conversion

commands, and you will need to use a suitable algorithm (e.g. breadth-first search) to search for a path of conversions between the two file types.

If it exists, then the path of conversions (and the associated conversion commands) forms the basis for setting up the conversion pipeline to

process the job.

Creation of a conversion pipeline should be begun by the main program forking a single process to serve as the "master" process for the

pipeline. This process should use setpgid() to set its process group ID to its own process ID. The master process will then fork one child process

for each link in the conversion path between the type of the file in the job and the type of file that the chosen printer can print. Redirection

should be used so that the standard input of the first process in the pipeline is the file to be printed and the standard output of the last process

in the pipeline is the chosen printer (for which a file descriptor has been obtained using imp_connect_to_printer() ). In addition, the pipe()

and dup() (or dup2() ) system calls should be used to arrange to connect the standard output of each intermediate process in the pipeline to

the standard input of the next process. Each process in the pipeline will execute (using execve() ) one of the conversion commands (previously

declared by the user using the conversion command) to convert the file read on its standard input to the type required by the next process in

the pipeline.

It is possible that the type of the queued file is the same as the type of file the printer can print. In this case, no conversion is

required, and the conversion program will consist of the master process and a single child process, which should execute the program

/bin/cat with no arguments.

The master process of a conversion pipeline is used to simplify the interaction of the conversion pipeline with the main process. Since the master

process creates its own process group before forking the child processes, all the child processes will exist in that process group. The processes in

the pipeline can therefore be paused and resumed by using killpg() to send a SIGSTOP or SIGCONT to that process group. Only the master

process is a child of the main process, so the main process only has to keep track of the process ID for the master process of each conversion

pipeline that it starts. The master process of a conversion pipeline will need to keep track of its child processes, and to use waitpid() to reap

them and collect their exit status. If any child process terminates by a signal or with a nonzero exit status, then the conversion pipeline will be

deemed to have failed and the master process should exit with a nonzero exit status. The main process should interpret the nonzero exit status as

Conversion Pipelines

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 6/7

an indication that the job has failed, and it should set the job to the ABORTED state. If all of the child processes in a conversion pipeline terminate

normally with zero exit status, then the master process should also terminate normally with zero exit status. The main process should interpret

this situation as an indication that the job has succeeded, and it should set the job the the COMPLETED state.

Important: You must create the processes in a conversion pipeline using calls to fork() and execve() . You must not use the

system() function, nor use any form of shell in order to create the pipeline, as the purpose of the assignment is to giving you

experience with using the system calls involved in doing this.

The imprimer.h header file that we have provided defines function prototypes for the functions you are to use to format output for your

program and to make connections to printers. It also contains definitions of some constants and data types related to these functions.

Do not make any changes to imprimer.h . It will be replaced during grading, and if you change it, you will get a zero!

We have provided you with an object file imp_util.o (in the lib directory), which will be automatically linked with your program. This contains

several functions, whose prototypes are given in the imprimer.h header file, which you should use as follows:

char *imp_format_printer_status(PRINTER *printer, char *buf, size_t size);

char *imp_format_job_status(JOB *job, char *buf, size_t size);

char *imp_format_error_message(char *msg);

You must use these functions to format required output by your program. The reason for this is so that everyone's program produces output

in a uniform format that we might have a fighting chance to process automatically. The output looks a bit odd, but you will note that it is in

comma-separated-value (CSV) format that can be readily parsed. These functions take a buffer that you supply, along with its size, and they

return a pointer to that same buffer.

int imp_connect_to_printer(PRINTER *printer);

This is the function you must use to connect to a printer. If successful, it returns a file descriptor to be used to send data to the printer; if

unsuccessful, -1 is returned. If the printer is not currently "up", then it will be started (see about the printer program below).

In order to interface with the above functions, the header file imprimer.h defines structure types PRINTER and JOB . You must pass in instances

of these structures that have all fields properly initialized. The meaning of each of the fields is documented in the comments in the imprimer.h

file. Each of these structures also has an additional other field, which can be used to point to arbitrary information of your own choosing should

you have a need to do so. The functions above ignore the value of this field, so there is no harm if you don't initialize it.

The printer program we have provided (in the util directory) simulates a printer that you can connect to and send data to. It doesn't actually

"print" anything, but it does log any files you send to it in spool directory. It also maintains a debug log in that directory, in case it is necessary

to get some idea of what the printer has been doing.

A printer is automatically started when you try to connect to it using the imp_connect_to_printer() function, if it is not already up. You can also

start a printer "manually" by a command of the following form:

$ util/printer [-d] [-f] PRINTER_NAME FILE_TYPE

This starts a printer with name PRINTER_NAME , which is capable of printing files of type FILE_TYPE . Each printer that is started must have a

unique name; if you try to start a second printer with the same name as an existing printer, the second command will fail. Once started, printers

stay "up" until they are explicitly stopped, You can stop all printers using the command make stop_printers . The command make

show_printers can be used to show you the printers that are currently up.

The optional -d and -f arguments to the printer command are used to cause the printer to exhibit some random behavior. If -d is

specified, then random delays might occur during "printing". If -f is specified, then the printer will be "flaky", which means that it might

disconnect at random times, causing the conversion pipeline to fail. The imp_connect_to_printer() function has a flags argument that can

also be used to specify these flags. The flags only take effect when the printer is first started; once a printer is "up", the flags passed when

connecting to it have no further effect. The flags should be the bitwise "or" of one or more of PRINTER_NORMAL , PRINTER_DELAY , and

PRINTER_FLAKY .

The util directory contains shell scripts show_printers.sh and stop_printers.sh . These are most easily invoked using make show_printers

or make stop_printers , though they can also be run directly.

The rsrc directory contains a file imprimer.cmd . This is a sample file that contains some imprimer configuration commands that can be read

using the -i option. For example:

Provided Components

The imprimer.h Header File

The imp_util.o Library

The printer Program

The show_printers.sh and stop_printers.sh Shell Scripts

The imprimer.cmd File

11/13/2018 README.md · master · CSE 320 / hw4-doc · GitLab

https://gitlab02.cs.stonybrook.edu/cse320/hw4-doc/blob/master/README.md 7/7

The spool directory is created by make in order to store various files created by the "printers". For example, if a printer is started with name

alice , then spool/alice.log will contain debug log information, spool/alice.pid will contain the process ID of the printer process (for use

by stop_printers.sh ), spool/alice.sock will be a "socket" that is used by imp_connect_to_printer() to connect the printer. Also, each time

a file is "printed" the data that was received is stored in a separately named file in this directory. The spool directory is not removed by a normal

make clean . To remove the spool directory and all its contents, you can use make clean_spool .

$ imprimer -i rsrc/imprimer.cmd

At some point after this assignment has initially been handed out, I will probably make available either a list of "recommended" commands

to use in a conversion pipeline, or I will make available some "dummy" commands for testing purposes. Not having these commands

available should not stop you from getting started; you can always test your program using cat as a "conversion" command.

I am considering making things more interesting and realistic by adding an additional display command to imprimer (similar to the

printer command) together with a corresponding display program (similar to the printer program). The purpose of this would be to

provide a way for files to be actually "printed" by running a command that displays them graphically in a window. If I decide to do this, I will

make an announcement about it and update this document.

As usual, make sure your homework compiles before submitting. Test it carefully to be sure that doesn't crash or exhibit "flaky" behavior due to

race conditions. Use valgrind to check for memory errors and leaks. Besides --leak-check=full , also use the option --track-fds=yes to

check whether your program is leaking file descriptors because they haven't been properly closed. You might also want to look into the

valgrind --trace-children and related options.

Submit your work using git submit as usual. This homework's tag is: hw4 .


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

python代写
微信客服:codinghelp