|
|
To illustrate the use of SCO OpenServer system programming tools in the development of an application, we are going to pretend we are engaged in the development of a computer system for a library. The system is known as liber. The early stages of system development, we assume, have already been completed; feasibility studies have been done, the preliminary design is described in the coming paragraphs. We are going to stop short of producing a complete detailed design and module specifications for our system. You will have to accept that these exist. In using portions of the system for examples of the topics covered in this section, we will work from these virtual specifications.
We make no claim as to the efficacy of this design. It is the way it is only in order to provide some passably realistic examples of SCO OpenServer system programming tools in use. It is not an application, but rather is code fragments only.
liber is a system for keeping track of the books in a library. The hardware consists of a single computer with terminals throughout the library. One terminal is used for adding new books to the data base. Others are used for checking out books and as electronic card catalogs.
The design of the system calls for it to be brought up at the beginning of the day and remain running while the library is in operation. Associated with each terminal is a program specific to the function of that terminal, each running as a separate SCO OpenServer process. The system has one master index that contains the unique identifier of each title in the library. When the system is running the index is mapped into the address space of each process. Semaphores are used to synchronize access to the index. In the pages that follow fragments of some of the system's programs are shown to illustrate the way they work together. The startup program performs the system initialization; opening the semaphores and the index file; mapping the index file into memory; and kicking off the other programs. The id numbers for the semaphores (wrtsem, and rdsem) are written to a file during initialization, this file is then read by all the subsidiary programs so that all use the same semaphores.
All the programs share access to the index file. They gain access to it with the following code:
/* * Gain access to the index file, map it in. * After mapping, free the file descriptor so * that it will be available for other uses -- * the mapping will remain until the program * exits, or until the mapping is removed either * by munmap() or by mapping over top of this one * with another call to mmap(). Note the use of * the read/write open mode -- all programs but * "add-books" should open just for read-only. */ if ((index_fd = open("index.file", O_RDWR)) == -1) { (void) fprintf(stderr, "index open failed: %d\n", errno); exit(1); } /* * Establish the mapping. As with the call to * open(), all programs but "add-books" should * map with PROT_READ for read-only access. */ if ((int)(index = (INDEX *)mmap(0, sizeof (INDEX), PROT_READ|PROT_WRITE, MAP_SHARED, index_fd, 0) == -1) { (void) fprintf(stderr, "mmap failed: %d\n", errno); exit(1); } (void) close(index_fd);
The preceding code fragment establishes a mapping to the index file in the address space of the program. Access to the addresses at which the file is mapped affect the file directly, no further file operations are required. For instance, if the access deposits data at the accessed address, then the file will be modified by operation. If the access examines data, then the file will be accessed. In either case, the portion of the file containing the information will be obtained or restored to secondary storage automatically by the system and transparently to the application.
Of the programs shown, add-books is the only one that alters the index. The semaphores are used to ensure that no other programs will try to read the index while add-books is altering it. The checkout program locks the file record for the book, so that each copy being checked out is recorded separately and the book cannot be checked out at two different checkout stations at the same time.
The program fragments do not provide any details on the structure of the index or the book records in the data base.
/* liber.h - header file for the * library system. */ typedef ... INDEX; /* data structure for book file index */ typedef struct { /* type of records in book file */ char title[30]; char author[30]; . . . } BOOK; int index_fd; int wrtsem; int rdsem; INDEX *index;int book_file; BOOK book_buf;
/* startup program */
/* * 1. Open index file and map it in. * 2. Open two semaphores for providing exclusive write access to index. * 3. Stash id's for shared memory segment and semaphores in a file * where they can be accessed by the programs. * 4. Start programs: add-books, card-catalog, and checkout running * on the various terminals throughout the library. */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include "liber.h"
void exit(); extern int errno;
key_t key; int shmid; int wrtsem; int rdsem; FILE *ipc_file;
main() { . . . /* * Open index file and map it. */
/* See previous example */
/* * Get the read/write semaphores. */ if ((wrtsem = semget(key, 1, IPC_CREAT | 0666)) == -1) { (void) fprintf(stderr, "startup: semget failed: errno=%d\n", errno); exit(1); }
if ((rdsem = semget(key, 1, IPC_CREAT | 0666)) == -1) { (void) fprintf(stderr, "startup: semget failed: errno=%d\n", errno); exit(1); } (void) fprintf(ipc_file, "%d\n%d\n", wrtsem, rdsem);
/* * Start the add-books program running on the terminal in the * basement. Start the checkout and card-catalog programs * running on the various other terminals throughout the library. */ . . . }
/* card-catalog program */
/* * 1. Read screen for author and title. * 2. Use semaphores to prevent reading index while it is being written. * 3. Use index to get position of book record in book file. * 4. Print book record on screen or indicate book was not found. * 5. Go to 1. */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <fcntl.h> #include "liber.h"
void exit(); extern int errno; struct sembuf sop[1];
main() { . . .
while (1) { /* * Read author/title/subject information from screen. */
/* * Wait for write semaphore to reach 0 (index not being written). */ sop[0].sem_op = 1; if (semop(wrtsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); } /* * Increment read semaphore so potential writer will wait * for us to finish reading the index. */ sop[0].sem_op = 0; if (semop(rdsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); }
/* Use index to find file pointer(s) for book(s) */
/* Decrement read semaphore */ sop[0].sem_op = -1; if (semop(rdsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); }
/* * Now we use the file pointers found in the index to * read the book file. Then we print the information * on the book(s) to the screen. */
/* * Note design alternatives for this portion of the * the code: the book file could be accessed by * lseek()s to the portion of the file containing * the record, and then read() could be used to * obtain the file information. Alternatively, the * entire book file could be mapped into memory, and the * the record accessed directly without further * file operations, or the area of the file containing * the book record could just be mapped and then unmapped * when the access is complete. */ . . .
} /* while */ } /* checkout program */
/* * 1. Read screen for Dewey Decimal number of book to be checked out. * 2. Use semaphores to prevent reading index while it is being written. * 3. Use index to get position of book record in book file. * 4. If book not found print message on screen, otherwise lock * book record and read. * 5. If book already checked out print message on screen, otherwise * mark record "checked out" and write back to book file. * 6. Unlock book record. * 7. Go to 1. */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <fcntl.h> #include "liber.h"
void exit(); long lseek(); extern int errno; struct flock flk; struct sembuf sop[1]; long bookpos;
main() { . . . while (1) { /* * Read Dewey Decimal number from screen. */
/* * Wait for write semaphore to reach 0 (index not being written). */ sop[0].sem_flg = 0; sop[0].sem_op = 0; if (semop(wrtsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); } /* * Increment read semaphore so potential writer will wait * for us to finish reading the index. */ sop[0].sem_op = 1; if (semop(rdsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); }
/* * Now we can use the index to find the book's record position. * Assign this value to "bookpos". */
/* Decrement read semaphore */ sop[0].sem_op = -1; if (semop(rdsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); }
/* * Lock the book's record in book file, read the record. * Here again we have the design option of deciding to * access and update the database through the use of * seeks, read()s and write()s; or file mapping can * be used to access the file. File mapping has the * disadvantage that it does not interact well with * enforcement-mode locking, although semaphores * could be used as an alternative synchronization * mechanism to file locking. File mapping would have * potential efficiency advantages, eliminating the need * for repetitive file access operations and attendant * data copying. For this example, however, we choose * not to use mapping to demonstrate the use of other * system facilities. */ flk.l_type = F_WRLCK; flk.l_whence = 0; flk.l_start = bookpos; flk.l_len = sizeof(BOOK); if (fcntl(book_file, F_SETLKW, &flk) == -1)
{ (void) fprintf(stderr, "trouble locking: %d\n", errno); exit(1); } if (lseek(book_file, bookpos, 0) == -1) { (Error processing for lseek); } if (read(book_file, &book_buf, sizeof(BOOK)) == -1) { (Error processing for read); }
/* * If the book is checked out inform the client, otherwise * mark the book's record as checked out and write it * back into the book file. */
/* Unlock the book's record in book file. */ flk.l_type = F_UNLCK; if (fcntl(book_file, F_SETLK, &flk) == -1) { (void) fprintf(stderr, "trouble unlocking: %d\n", errno); exit(1); } } /* while */ }
/* add-books program */
/* * 1. Read a new book entry from screen. * 2. Insert book in book file. * 3. Use semaphore "wrtsem" to block new readers. * 4. Wait for semaphore "rdsem" to reach 0. * 5. Insert book into index. * 6. Decrement wrtsem. * 7. Go to 1. */
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include "liber.h"
void exit(); extern int errno; struct sembuf sop[1]; BOOK bookbuf;
main() { . . . for (;;) {
/* * Read information on new book from screen. */
addscr(&bookbuf);
/* write new record at the end of the bookfile. * Code not shown, but * addscr() returns a 1 if title information has * been entered, 0 if not. */
/* * Increment write semaphore, blocking new readers from * accessing the index. */ sop[0].sem_flg = 0; sop[0].sem_op = 1; if (semop(wrtsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); }
/* * Wait for read semaphore to reach 0 (all readers to finish * using the index). */ sop[0].sem_op = 0; if (semop(rdsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); } /* * Now that we have exclusive access to the index we * insert our new book with its file pointer. */
/* Decrement write semaphore, permitting readers to read index. */ sop[0].sem_op = -1; if (semop(wrtsem, sop, 1) == -1) { (void) fprintf(stderr, "semop failed: %d\n", errno); exit(1); } } /* for */ . . . }
The example following, addscr, illustrates two significant points about curses screens:
/* addscr is called from add-books. * The user is prompted for title * information. */ #include <curses.h>WINDOW *cmdwin;
addscr(bb) struct BOOK *bb; { int c;
initscr(); nonl(); noecho(); cbreak();
cmdwin = newwin(6, 40, 3, 20); mvprintw(0, 0, "This screen is for adding titles to the data base"); mvprintw(1, 0, "Enter a to add; q to quit: "); refresh(); for (;;) { refresh(); c = getch(); switch (c) { case 'a': werase(cmdwin); box(cmdwin, '|', '-'); mvwprintw(cmdwin, 1, 1, "Enter title: "); wmove(cmdwin, 2, 1); echo(); wrefresh(cmdwin); wgetstr(cmdwin, bb->title); noecho(); werase(cmdwin); box(cmdwin, '|', '-'); mvwprintw(cmdwin, 1, 1, "Enter author: "); wmove(cmdwin, 2, 1); echo(); wrefresh(cmdwin); wgetstr(cmdwin, bb->author); noecho(); werase(cmdwin); wrefresh(cmdwin); endwin(); return(1); case 'q': erase(); endwin(); return(0); } } }
# # Makefile for liber library system #
CC = cc CFLAGS = -O all: startup add-books checkout card-catalog
startup: liber.h startup.c $(CC) $(CFLAGS) -o startup startup.c
add-books: add-books.o addscr.o $(CC) $(CFLAGS) -o add-books add-books.o addscr.o
add-books.o: liber.h
checkout: liber.h checkout.c $(CC) $(CFLAGS) -o checkout checkout.c
card-catalog: liber.h card-catalog.c $(CC) $(CFLAGS) -o card-catalog card-catalog.c