In this roadmap we study the following issues associated with the file system:
As before, examine and understand the file system code first. Nachos file operations are similar (but not identical) to those in UNIX. It is a good idea to read the UNIX man pages for creat, open, close, read, write, lseek, and unlink (e.g., type "man creat").
Command line switches support the following file system operations:
-cp copies a file from UNIX to nachos -p prints a nachos file to stdout -r removes a nachos file from the file system -l lists the contents of the nachos directory -D prints the contents of the entire file system -t tests the performance of the nachos file system
If you want to use the real Nachos file system (based on the
simulated disk), rather than the stub, remove
-DFILESYS_STUB from DEFINE in build.sun/Makefile
(or build.linux/Makefile).
Examples
./nachos -f -cp ../test/halt halt
Format the disk and copy the program halt into the simulated disk.
./nachos -x halt
Run the program halt from the simulated disk.
Before tracing the program, we first give an overview of
nachos file system. It has the following objects:
FileSystem
{ OpenFile *freeMapFile; OpenFile *directoryFile; };
Directory
{ int tableSize; DirectoryEntry *table; };
DirectoryEntry
{ bool inUse; int sector; char name[FileNameMaxLen + 1]; };
OpenFile
{ FileHeader *hdr; int seekPosition; };
FileHeader
{ Int numBytes; int numSectors; int dataSectors[NumDirect]; };
---------- FileSystem ---------- | | freeMapFile -------------------- directoryFile | | | BitMap OpenFile Directory | | FileHeader DirectoryEntry
Question
What file system objects exist in memory and/or on disk?
How are these objects moved between memory and disk?
Now, we examine nachos file system by tracing the program. Start with threads/kernel.cc/Initialize() called by threads/main.cc/main(int argc, char **argv).
void Kernel::Initialize() { ... synchDisk = new SynchDisk(); #ifdef FILESYS_STUB fileSystem = new FileSystem(); #else fileSystem = new FileSystem(formatFlag); #endif // FILESYS_STUB ... }
If FILESYS_STUB is not defined, not using UNIX file system stub, then a FileSystem is constructed. The parameter formatFlag is set when the command line switch -f is present. First, a new synchDisk is constructed before the fileSystem is created. The file system consists of a root directory and a bitmap, which we have studied in user program. Both the directory and bitmap are treated as files, objects of class OpenFile, which provides operations on files. Then, you can test this simple file system using command line switch.
machine/disk.*, filesys/synchdisk.*
class SynchDisk : public CallBackObj { public: SynchDisk(); // Initialize a synchronous disk, // by initializing the raw Disk. ~SynchDisk(); // De-allocate the synch disk data void ReadSector(int sectorNumber, char* data); // Read/write a disk sector, returning // only once the data is actually read // or written. These call // Disk::ReadRequest/WriteRequest and // then wait until the request is done. void WriteSector(int sectorNumber, char* data); void CallBack(); // Called by the disk device interrupt // handler, to signal that the // current disk operation is complete. private: Disk *disk; // Raw disk device Semaphore *semaphore; // To synchronize requesting thread // with the interrupt handler Lock *lock; // Only one read/write request // can be sent to the disk at a time }; SynchDisk::SynchDisk(char* name) { semaphore = new Semaphore("synch disk", 0); lock = new Lock("synch disk lock"); disk = new Disk(this); }
Questions:
class Disk { public: Disk(CallBackObj *toCall); // Create a simulated disk. // Invoke toCall->CallBack() // when each request completes. ~Disk(); // Deallocate the disk. void ReadRequest(int sectorNumber, char* data); // Read/write an single disk sector. // These routines send a request to // the disk and return immediately. // Only one request allowed at a time! void WriteRequest(int sectorNumber, char* data); void CallBack(); // Invoked when disk request // finishes. In turn calls, callWhenDone . int ComputeLatency(int newSector, bool writing); // Return how long a request to // newSector will take: // (seek + rotational delay + transfer) private: int fileno; // UNIX file number for simulated disk char diskname[32]; // name of simulated disk's file CallBackObj *callWhenDone; // Invoke when any disk request finishes bool active; // Is a disk operation in progress? int lastSector; // The previous disk request int bufferInit; // When the track buffer started // being loaded int TimeToSeek(int newSector, int *rotate); // time to get to the new track int ModuloDiff(int to, int from); // # sectors between to and from void UpdateLast(int newSector); };
Questions:
class FileSystem { public: ... private: OpenFile* freeMapFile; // Bit map of free disk blocks, // represented as a file OpenFile* directoryFile; // "Root" directory -- list of // file names, represented as a file };
The file system consists of freeMapFile (OpenFile *),
a bit map treated as a file, and directoryFile (OpenFile *),
the root directory is also treated as a file.
Question: Where are their file headers?
The baseline file system has the following
restrictions: only one (root) directory, entries in directory
are of fixed size, maximum number of entries in the
directory is 10.
FileSystem::FileSystem(bool format) { DEBUG(dbgFile, "Initializing the file system."); if (format) { PersistentBitmap *freeMap = new PersistentBitmap(NumSectors); Directory *directory = new Directory(NumDirEntries); FileHeader *mapHdr = new FileHeader; FileHeader *dirHdr = new FileHeader; DEBUG(dbgFile, "Formatting the file system."); // First, allocate space for FileHeaders for the directory // and bitmap (make sure no one else grabs these!) freeMap->Mark(FreeMapSector); freeMap->Mark(DirectorySector); // Second, allocate space for the data blocks containing // the contents of the directory and bitmap files. There // better be enough space! ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize)); ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize)); // Flush the bitmap and directory FileHeaders back to disk // We need to do this before we can "Open" the file, since // open reads the file header off of disk (and currently // the disk has garbage on it!). DEBUG(dbgFile, "Writing headers back to disk."); mapHdr->WriteBack(FreeMapSector); dirHdr->WriteBack(DirectorySector); // OK to open the bitmap and directory files now // The file system operations assume these two files // are left open while Nachos is running. freeMapFile = new OpenFile(FreeMapSector); directoryFile = new OpenFile(DirectorySector); // Once we have the files "open", we can write the initial // version of each file back to disk. The directory at this // point is completely empty; but the bitmap has been changed // to reflect the fact that sectors on the disk have been // allocated for the file headers and to hold the file data // for the directory and bitmap. DEBUG(dbgFile, "Writing bitmap and directory back to disk."); freeMap->WriteBack(freeMapFile); // flush changes to disk directory->WriteBack(directoryFile); if (debug->IsEnabled('f')) { freeMap->Print(); directory->Print(); } delete freeMap; delete directory; delete mapHdr; delete dirHdr; } else { // if we are not formatting the disk, just open the files // representing the bitmap and directory; these are left // open while Nachos is running freeMapFile = new OpenFile(FreeMapSector); directoryFile = new OpenFile(DirectorySector); } }
When the file system is constructed, if the command line switch is "-f", the disk is formatted (wipe out all files on the disk). It first initializes a bitmap and the root directory in memory (headers and files); flush the headers back to disk; open bitmap and root so that their headers are kept in memory and we can operate on them; flush bitmap and root directory files back to the disk; clean up memory. If the disk has been formatted, just open bitmap and root directory (bring their file headers into memory).
Question: How many disk reads/writes when the file system is contructed with format=TRUE?
The file system provides the following operations. These operations are associated with the file system (directory, bitmap). The operations associated with individual files are in OpenFile object.
class FileSystem { public: ... bool Create(char *name, int initialSize); OpenFile* Open(char *name); bool Remove(char *name); ... };
bool FileSystem::Create(char *name, int initialSize) { Directory *directory; PersistentBitmap *freeMap; FileHeader *hdr; int sector; bool success; DEBUG(dbgFile, "Creating file " << name << " size " << initialSize); directory = new Directory(NumDirEntries); directory->FetchFrom(directoryFile); if (directory->Find(name) != -1) success = FALSE; // file is already in directory else { freeMap = new PersistentBitmap(freeMapFile,NumSectors); sector = freeMap->FindAndSet(); // find a sector to hold the file header if (sector == -1) success = FALSE; // no free block for file header else if (!directory->Add(name, sector)) success = FALSE; // no space in directory else { hdr = new FileHeader; if (!hdr->Allocate(freeMap, initialSize)) success = FALSE; // no space on disk for data else { success = TRUE; // everthing worked, flush all changes back to disk hdr->WriteBack(sector); directory->WriteBack(directoryFile); freeMap->WriteBack(freeMapFile); } delete hdr; } delete freeMap; } delete directory; return success; }
This function creates a file with no data by adding an entry in the directory. Study carefully the data movement between memory and disk.
The file header is flushed back to the disk before the directory. Because, if system crashes in between, the directory does not have the entry for the file although the file header is on the disk. The file system still functions. If, however, the order is reversed, the directory were flushed back before the file header. If the system crashes in between, we would have the entry in the directory referencing to a sector number (i-number) which is not the file header. The file system would be corrupted.
Question: What if the system crashes after the directory is flushed back but before the free map is flushed back?
OpenFile * FileSystem::Open(char *name) { Directory *directory = new Directory(NumDirEntries); OpenFile *openFile = NULL; int sector; DEBUG(dbgFile, "Opening file" << name); directory->FetchFrom(directoryFile); sector = directory->Find(name); if (sector >= 0) openFile = new OpenFile(sector);// name was found in directory delete directory; return openFile; // return NULL if not found }
Steps of opening a file:
bool FileSystem::Remove(char *name) { Directory *directory; PersistentBitmap *freeMap; FileHeader *fileHdr; int sector; directory = new Directory(NumDirEntries); directory->FetchFrom(directoryFile); sector = directory->Find(name); if (sector == -1) { delete directory; return FALSE; // file not found } fileHdr = new FileHeader; fileHdr->FetchFrom(sector); freeMap = new PersistentBitmap(freeMapFile,NumSectors); fileHdr->Deallocate(freeMap); // remove data blocks freeMap->Clear(sector); // remove header block directory->Remove(name); freeMap->WriteBack(freeMapFile); // flush to disk directory->WriteBack(directoryFile);// flush to disk delete fileHdr; delete directory; delete freeMap; return TRUE; }
This function removes a file from disk. In order to find the data sectors and free them, we must bring in the file header, which requires the directory file and the bitmap file.
Question: What if the system crashes after the free map is flushed back but before the directory is flushed back?
class OpenFile { public: ... private: FileHeader *hdr; // Header for this file int seekPosition; // Current position within the file };
Data structures: a pointer to FileHeader and a position.
------------------ | hdr |---> FileHeader ------------------ | seekPosition | |----------------|
OpenFile::OpenFile(int sector) { hdr = new FileHeader; hdr->FetchFrom(sector); seekPosition = 0; } int OpenFile::Read(char *into, int numBytes) { int result = ReadAt(into, numBytes, seekPosition); seekPosition += result; return result; } int OpenFile::Write(char *into, int numBytes) { int result = WriteAt(into, numBytes, seekPosition); seekPosition += result; return result; } int OpenFile::ReadAt(char *into, int numBytes, int position) { int fileLength = hdr->FileLength(); int i, firstSector, lastSector, numSectors; char *buf; if ((numBytes <= 0) || (position >= fileLength)) return 0; // check request if ((position + numBytes) > fileLength) numBytes = fileLength - position; DEBUG(dbgFile, "Reading " << numBytes << " bytes at " << position << " from file of length " << fileLength); firstSector = divRoundDown(position, SectorSize); lastSector = divRoundDown(position + numBytes - 1, SectorSize); numSectors = 1 + lastSector - firstSector; // read in all the full and partial sectors that we need buf = new char[numSectors * SectorSize]; for (i = firstSector; i <= lastSector; i++) kernel->synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize), &buf[(i - firstSector) * SectorSize]); // copy the part we want bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes); delete [] buf; return numBytes; } int OpenFile::WriteAt(char *from, int numBytes, int position) { int fileLength = hdr->FileLength(); int i, firstSector, lastSector, numSectors; bool firstAligned, lastAligned; char *buf; if ((numBytes <= 0) || (position >= fileLength)) return 0; // check request if ((position + numBytes) > fileLength) numBytes = fileLength - position; DEBUG(dbgFile, "Writing " << numBytes << " bytes at " << position << " from file of length " << fileLength); firstSector = divRoundDown(position, SectorSize); lastSector = divRoundDown(position + numBytes - 1, SectorSize); numSectors = 1 + lastSector - firstSector; buf = new char[numSectors * SectorSize]; firstAligned = (position == (firstSector * SectorSize)); lastAligned = ((position + numBytes) == ((lastSector + 1) * SectorSize)); // read in first and last sector, if they are to be partially modified if (!firstAligned) ReadAt(buf, SectorSize, firstSector * SectorSize); if (!lastAligned && ((firstSector != lastSector) || firstAligned)) ReadAt(&buf[(lastSector - firstSector) * SectorSize], SectorSize, lastSector * SectorSize); // copy in the bytes we want to change bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes); // write modified sectors back for (i = firstSector; i <= lastSector; i++) kernel->synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize), &buf[(i - firstSector) * SectorSize]); delete [] buf; return numBytes; }
Question: What happens when a new object OpenFile is constructed?
OpenFile provides operations, Read and Write, on files, which are implemented by ReadAt and WriteAt using the internal seekPosition. Note that we can only transfer one sector at a time from the disk, thus when read, we have to fetch all associated sectors from the disk into memory (kernel space) then copy what we want (read may not start from the beginning of the first sector and may not end at the end of the last sector). When write, we first fetch the first and the last sectors from the disk into the kernel space and then modify them and write all sectors back to the disk (the first and the last sectors may be partially modified).
class FileHeader { public: ... private: int numBytes; // Number of bytes in the file int numSectors; // Number of data sectors in the file int dataSectors[NumDirect]; // Disk sector numbers for each data // block in the file };
Data structures of FileHeader:
------------------- | numBytes | ------------------- | numSectors | ------------------- | dataSectors[0] | ------------------- | dataSectors[1] | ------------------- | | | ... | | | -------------------
Question: What is the value of NumDirect?
bool FileHeader::Allocate(PersistentBitmap *freeMap, int fileSize) { numBytes = fileSize; numSectors = divRoundUp(fileSize, SectorSize); if (freeMap->NumClear() < numSectors) return FALSE; // not enough space for (int i = 0; i < numSectors; i++) { dataSectors[i] = freeMap->FindAndSet(); // since we checked that there was enough free space, // we expect this to succeed ASSERT(dataSectors[i] >= 0); } return TRUE; } void FileHeader::Deallocate(PersistentBitmap *freeMap) { for (int i = 0; i < numSectors; i++) { ASSERT(freeMap->Test((int) dataSectors[i])); // ought to be marked! freeMap->Clear((int) dataSectors[i]); } } void FileHeader::FetchFrom(int sector) { kernel->synchDisk->ReadSector(sector, (char *)this); }
There are two ways of initializing a file header:
Question: How is the consistency checked when a file header is deallocated?
Compare the functions FetchFrom() and WriteBack() here with those in directory. The bitmap also has FetchFrom() and WriteBack() functions.
class DirectoryEntry { public: bool inUse; // Is this directory entry in use? int sector; // Location on disk to find the // FileHeader for this file char name[FileNameMaxLen + 1]; // Text name for file, with +1 for // the trailing '\0' };
A directory is a table (array) of DirectoryEntry. The data structures of a DirectoryEntry:
------------ | inUse | ------------ | sector | sector number of file header ------------ | | | name | | | ------------ class Directory { public: ... void FetchFrom(OpenFile *file); void WriteBack(OpenFile *file); ... };
Compare FetchFrom() and WriteBack() functions with those in FileHeader.
Here is a sample run of the file system. Can you identify the disk reads/writes?
birkhoff-build.sun>nachos -f Machine halting! Ticks: total 41560, idle 41500, system 60, user 0 Disk I/O: reads 3, writes 19 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -f -cp ../test/halt halt Machine halting! Ticks: total 395560, idle 392570, system 2990, user 0 Disk I/O: reads 42, writes 46 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -x halt Machine halting! Ticks: total 301069, idle 297600, system 3450, user 19 Disk I/O: reads 59, writes 18 Console I/O: reads 0, writes 0 Paging: faults 3 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -cp ../filesys/filehdr.h filehdr.h Machine halting! Ticks: total 4013060, idle 3993790, system 19270, user 0 Disk I/O: reads 204, writes 240 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -l halt swap58545 filehdr.h Machine halting! Ticks: total 10180, idle 9430, system 750, user 0 Disk I/O: reads 18, writes 0 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -f -cp ../test/halt.c halt.c Machine halting! Ticks: total 428560, idle 425190, system 3370, user 0 Disk I/O: reads 44, writes 52 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -l halt.c Machine halting! Ticks: total 10180, idle 9430, system 750, user 0 Disk I/O: reads 18, writes 0 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0 birkhoff-build.sun>nachos -D Bit map file header: FileHeader contents. File size: 128. File blocks: 2 File contents: \1\ff\ff\ff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0 Directory file header: FileHeader contents. File size: 2000. File blocks: 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 File contents: \1\0\0\0\0\0\0\13halt.c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 Bitmap set: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 , 23, 24, Directory contents: Name: halt.c, Sector: 19 FileHeader contents. File size: 554. File blocks: 20 21 22 23 24 File contents: /* halt.c\a * Simple program to test whether running a user program works.\ a *\9\a * Just do a "syscall" that shuts down t he OS.\a *\a * NOTE: for some reason, user programs with global data struct ures \a * sometimes haven't worked in the Nach os environment. So be careful\a * out there! One option is to allocate da ta structures as \a * automatics within a pro cedure, but if you do this, you have to\a * be careful to allocate a big en ough stack to hold the automatics!\a */\a\a#include "syscall.h"\a\aint\amain()\a{\a Halt();\a}\a\a\a Machine halting! Ticks: total 24460, idle 22500, system 1960, user 0 Disk I/O: reads 45, writes 0 Console I/O: reads 0, writes 0 Paging: faults 0 Network I/O: packets received 0, sent 0