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